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::tribool result = parse(getMessage());
00077     
00078     if (gcount() > 0) {
00079         // parsed > 0 bytes in HTTP headers
00080         PION_LOG_DEBUG(m_logger, "Parsed " << gcount() << " HTTP bytes");
00081     }
00082 
00083     if (result == true) {
00084         // finished reading HTTP message and it is valid
00085 
00086         // set the connection's lifecycle type
00087         if (getMessage().checkKeepAlive()) {
00088             if ( eof() ) {
00089                 // the connection should be kept alive, but does not have pipelined messages
00090                 m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
00091             } else {
00092                 // the connection has pipelined messages
00093                 m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_PIPELINED);
00094 
00095                 // save the read position as a bookmark so that it can be retrieved
00096                 // by a new HTTP parser, which will be created after the current
00097                 // message has been handled
00098                 m_tcp_conn->saveReadPosition(m_read_ptr, m_read_end_ptr);
00099 
00100                 PION_LOG_DEBUG(m_logger, "HTTP pipelined "
00101                                << (isParsingRequest() ? "request (" : "response (")
00102                                << bytes_available() << " bytes available)");
00103             }
00104         } else {
00105             m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00106         }
00107 
00108         // we have finished parsing the HTTP message
00109         finishedReading();
00110 
00111     } else if (result == false) {
00112         // the message is invalid or an error occured
00113 
00114     #ifndef NDEBUG
00115         // display extra error information if debug mode is enabled
00116         std::string bad_message;
00117         m_read_ptr = m_tcp_conn->getReadBuffer().data();
00118         while (m_read_ptr < m_read_end_ptr && bad_message.size() < 50) {
00119 #ifndef _MSC_VER
00120 // There's a bug in MSVC's implementation of isprint().
00121             if (!isprint(*m_read_ptr) || *m_read_ptr == '\n' || *m_read_ptr=='\r')
00122                 bad_message += '.';
00123             else bad_message += *m_read_ptr;
00124 #endif
00125             ++m_read_ptr;
00126         }
00127         PION_LOG_ERROR(m_logger, "Bad " << (isParsingRequest() ? "request" : "response")
00128                        << " debug: " << bad_message);
00129     #endif
00130 
00131         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // make sure it will get closed
00132         getMessage().setIsValid(false);
00133         finishedReading();
00134         
00135     } else {
00136         // not yet finished parsing the message -> read more data
00137         readBytesWithTimeout();
00138     }
00139 }
00140 
00141 void HTTPReader::readBytesWithTimeout(void)
00142 {
00143     if (m_read_timeout > 0) {
00144         m_timer_ptr.reset(new TCPTimer(m_tcp_conn));
00145         m_timer_ptr->start(m_read_timeout);
00146     } else if (m_timer_ptr) {
00147         m_timer_ptr.reset();
00148     }
00149     readBytes();
00150 }
00151 
00152 void HTTPReader::handleReadError(const boost::system::error_code& read_error)
00153 {
00154     // close the connection, forcing the client to establish a new one
00155     m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // make sure it will get closed
00156 
00157     // check if this is just a message with unknown content length
00158     if (! checkPrematureEOF(getMessage())) {
00159         finishedReading();
00160         return;
00161     }
00162     
00163     // only log errors if the parsing has already begun
00164     if (getTotalBytesRead() > 0) {
00165         if (read_error == boost::asio::error::operation_aborted) {
00166             // if the operation was aborted, the acceptor was stopped,
00167             // which means another thread is shutting-down the server
00168             PION_LOG_INFO(m_logger, "HTTP " << (isParsingRequest() ? "request" : "response")
00169                           << " parsing aborted (shutting down)");
00170         } else {
00171             PION_LOG_INFO(m_logger, "HTTP " << (isParsingRequest() ? "request" : "response")
00172                           << " parsing aborted (" << read_error.message() << ')');
00173         }
00174     }
00175 
00176     // do not trigger the callback when there are errors -> currently no way to propagate them
00177     m_tcp_conn->finish();
00178 }
00179 
00180 }   // end namespace net
00181 }   // end namespace pion
00182 

Generated on Fri Apr 30 14:48:53 2010 for pion-net by  doxygen 1.4.7