pion-net  4.0.9
net/src/HTTPReader.cpp
00001 // ------------------------------------------------------------------
00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
00003 // ------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.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/net/HTTPReader.hpp>
00013 #include <pion/net/HTTPRequest.hpp>
00014 
00015 
00016 namespace pion {    // begin namespace pion
00017 namespace net {     // begin namespace net (Pion Network Library)
00018 
00019 
00020 // HTTPReader static members
00021     
00022 const boost::uint32_t       HTTPReader::DEFAULT_READ_TIMEOUT = 10;
00023 
00024 
00025 // HTTPReader member functions
00026 
00027 void HTTPReader::receive(void)
00028 {
00029     if (m_tcp_conn->getPipelined()) {
00030         // there are pipelined messages available in the connection's read buffer
00031         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // default to close the connection
00032         m_tcp_conn->loadReadPosition(m_read_ptr, m_read_end_ptr);
00033         consumeBytes();
00034     } else {
00035         // no pipelined messages available in the read buffer -> read bytes from the socket
00036         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // default to close the connection
00037         readBytesWithTimeout();
00038     }
00039 }
00040 
00041 void HTTPReader::consumeBytes(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         handleReadError(read_error);
00053         return;
00054     }
00055     
00056     PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " bytes from HTTP "
00057                    << (isParsingRequest() ? "request" : "response"));
00058 
00059     // set pointers for new HTTP header data to be consumed
00060     setReadBuffer(m_tcp_conn->getReadBuffer().data(), bytes_read);
00061 
00062     consumeBytes();
00063 }
00064 
00065 
00066 void HTTPReader::consumeBytes(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(getMessage(), 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 (getMessage().checkKeepAlive()) {
00089             if ( eof() ) {
00090                 // the connection should be kept alive, but does not have pipelined messages
00091                 m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
00092             } else {
00093                 // the connection has pipelined messages
00094                 m_tcp_conn->setLifecycle(TCPConnection::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->saveReadPosition(m_read_ptr, m_read_end_ptr);
00100 
00101                 PION_LOG_DEBUG(m_logger, "HTTP pipelined "
00102                                << (isParsingRequest() ? "request (" : "response (")
00103                                << bytes_available() << " bytes available)");
00104             }
00105         } else {
00106             m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00107         }
00108 
00109         // we have finished parsing the HTTP message
00110         finishedReading(ec);
00111 
00112     } else if (result == false) {
00113         // the message is invalid or an error occured
00114         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // make sure it will get closed
00115         getMessage().setIsValid(false);
00116         finishedReading(ec);
00117     } else {
00118         // not yet finished parsing the message -> read more data
00119         readBytesWithTimeout();
00120     }
00121 }
00122 
00123 void HTTPReader::readBytesWithTimeout(void)
00124 {
00125     if (m_read_timeout > 0) {
00126         m_timer_ptr.reset(new TCPTimer(m_tcp_conn));
00127         m_timer_ptr->start(m_read_timeout);
00128     } else if (m_timer_ptr) {
00129         m_timer_ptr.reset();
00130     }
00131     readBytes();
00132 }
00133 
00134 void HTTPReader::handleReadError(const boost::system::error_code& read_error)
00135 {
00136     // close the connection, forcing the client to establish a new one
00137     m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // make sure it will get closed
00138 
00139     // check if this is just a message with unknown content length
00140     if (! checkPrematureEOF(getMessage())) {
00141         boost::system::error_code ec;   // clear error code
00142         finishedReading(ec);
00143         return;
00144     }
00145     
00146     // only log errors if the parsing has already begun
00147     if (getTotalBytesRead() > 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 " << (isParsingRequest() ? "request" : "response")
00152                           << " parsing aborted (shutting down)");
00153         } else {
00154             PION_LOG_INFO(m_logger, "HTTP " << (isParsingRequest() ? "request" : "response")
00155                           << " parsing aborted (" << read_error.message() << ')');
00156         }
00157     }
00158 
00159     finishedReading(read_error);
00160 }
00161 
00162 }   // end namespace net
00163 }   // end namespace pion
00164