- Main Page
- Classes
- Files

- File List

# src/http_reader.cpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.com)
    00005 //
    00006 // Distributed under the Boost Software License, Version 1.0.
    00007 // See http://www.boost.org/LICENSE_1_0.txt
    00008 //
    00009
    00010 #include <boost/asio.hpp>
    00011 #include <boost/logic/tribool.hpp>
    00012 #include <pion/http/reader.hpp>
    00013 #include <pion/http/request.hpp>
    00014
    00015
    00016 namespace pion {    // begin namespace pion
    00017 namespace http {    // begin namespace http
    00018
    00019
    00020 // reader static members
    00021
    00022 const boost::uint32_t       reader::DEFAULT_READ_TIMEOUT = 10;
    00023
    00024
    00025 // reader member functions
    00026
    00027 void reader::receive(void)
    00028 {
    00029     if (m_tcp_conn->get_pipelined()) {
    00030         // there are pipelined messages available in the connection's read buffer
    00031         m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);   // default to close the connection
    00032         m_tcp_conn->load_read_pos(m_read_ptr, m_read_end_ptr);
    00033         consume_bytes();
    00034     } else {
    00035         // no pipelined messages available in the read buffer -> read bytes from the socket
    00036         m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);   // default to close the connection
    00037         read_bytes_with_timeout();
    00038     }
    00039 }
    00040
    00041 void reader::consume_bytes(const boost::system::error_code& read_error,
    00042                               std::size_t bytes_read)
    00043 {
    00044     // cancel read timer if operation didn't time-out
    00045     if (m_timer_ptr) {
    00046         m_timer_ptr->cancel();
    00047         m_timer_ptr.reset();
    00048     }
    00049
    00050     if (read_error) {
    00051         // a read error occured
    00052         handle_read_error(read_error);
    00053         return;
    00054     }
    00055
    00056     PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " bytes from HTTP "
    00057                    << (is_parsing_request() ? "request" : "response"));
    00058
    00059     // set pointers for new HTTP header data to be consumed
    00060     set_read_buffer(m_tcp_conn->get_read_buffer().data(), bytes_read);
    00061
    00062     consume_bytes();
    00063 }
    00064
    00065
    00066 void reader::consume_bytes(void)
    00067 {
    00068     // parse the bytes read from the last operation
    00069     //
    00070     // note that boost::tribool may have one of THREE states:
    00071     //
    00072     // false: encountered an error while parsing message
    00073     // true: finished successfully parsing the message
    00074     // indeterminate: parsed bytes, but the message is not yet finished
    00075     //
    00076     boost::system::error_code ec;
    00077     boost::tribool result = parse(get_message(), ec);
    00078
    00079     if (gcount() > 0) {
    00080         // parsed > 0 bytes in HTTP headers
    00081         PION_LOG_DEBUG(m_logger, "Parsed " << gcount() << " HTTP bytes");
    00082     }
    00083
    00084     if (result == true) {
    00085         // finished reading HTTP message and it is valid
    00086
    00087         // set the connection's lifecycle type
    00088         if (get_message().check_keep_alive()) {
    00089             if ( eof() ) {
    00090                 // the connection should be kept alive, but does not have pipelined messages
    00091                 m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_KEEPALIVE);
    00092             } else {
    00093                 // the connection has pipelined messages
    00094                 m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_PIPELINED);
    00095
    00096                 // save the read position as a bookmark so that it can be retrieved
    00097                 // by a new HTTP parser, which will be created after the current
    00098                 // message has been handled
    00099                 m_tcp_conn->save_read_pos(m_read_ptr, m_read_end_ptr);
    00100
    00101                 PION_LOG_DEBUG(m_logger, "HTTP pipelined "
    00102                                << (is_parsing_request() ? "request (" : "response (")
    00103                                << bytes_available() << " bytes available)");
    00104             }
    00105         } else {
    00106             m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
    00107         }
    00108
    00109         // we have finished parsing the HTTP message
    00110         finished_reading(ec);
    00111
    00112     } else if (result == false) {
    00113         // the message is invalid or an error occured
    00114         m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);   // make sure it will get closed
    00115         get_message().set_is_valid(false);
    00116         finished_reading(ec);
    00117     } else {
    00118         // not yet finished parsing the message -> read more data
    00119         read_bytes_with_timeout();
    00120     }
    00121 }
    00122
    00123 void reader::read_bytes_with_timeout(void)
    00124 {
    00125     if (m_read_timeout > 0) {
    00126         m_timer_ptr.reset(new tcp::timer(m_tcp_conn));
    00127         m_timer_ptr->start(m_read_timeout);
    00128     } else if (m_timer_ptr) {
    00129         m_timer_ptr.reset();
    00130     }
    00131     read_bytes();
    00132 }
    00133
    00134 void reader::handle_read_error(const boost::system::error_code& read_error)
    00135 {
    00136     // close the connection, forcing the client to establish a new one
    00137     m_tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);   // make sure it will get closed
    00138
    00139     // check if this is just a message with unknown content length
    00140     if (! check_premature_eof(get_message())) {
    00141         boost::system::error_code ec;   // clear error code
    00142         finished_reading(ec);
    00143         return;
    00144     }
    00145
    00146     // only log errors if the parsing has already begun
    00147     if (get_total_bytes_read() > 0) {
    00148         if (read_error == boost::asio::error::operation_aborted) {
    00149             // if the operation was aborted, the acceptor was stopped,
    00150             // which means another thread is shutting-down the server
    00151             PION_LOG_INFO(m_logger, "HTTP " << (is_parsing_request() ? "request" : "response")
    00152                           << " parsing aborted (shutting down)");
    00153         } else {
    00154             PION_LOG_INFO(m_logger, "HTTP " << (is_parsing_request() ? "request" : "response")
    00155                           << " parsing aborted (" << read_error.message() << ')');
    00156         }
    00157     }
    00158
    00159     finished_reading(read_error);
    00160 }
    00161
    00162 }   // end namespace http
    00163 }   // end namespace pion