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 member functions
00021 
00022 void HTTPReader::receive(void)
00023 {
00024     if (m_tcp_conn->getPipelined()) {
00025         // there are pipelined messages available in the connection's read buffer
00026         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // default to close the connection
00027         m_tcp_conn->loadReadPosition(m_read_ptr, m_read_end_ptr);
00028         consumeBytes();
00029     } else {
00030         // no pipelined messages available in the read buffer -> read bytes from the socket
00031         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // default to close the connection
00032         readBytes();
00033     }
00034 }
00035 
00036 void HTTPReader::consumeBytes(const boost::system::error_code& read_error,
00037                               std::size_t bytes_read)
00038 {
00039     if (read_error) {
00040         // a read error occured
00041         handleReadError(read_error);
00042         return;
00043     }
00044     
00045     PION_LOG_DEBUG(m_logger, "Read " << bytes_read << " bytes from HTTP "
00046                    << (isParsingRequest() ? "request" : "response"));
00047 
00048     // set pointers for new HTTP header data to be consumed
00049     setReadBuffer(m_tcp_conn->getReadBuffer().data(), bytes_read);
00050 
00051     consumeBytes();
00052 }
00053 
00054 
00055 void HTTPReader::consumeBytes(void)
00056 {
00057     // parse the bytes read from the last operation
00058     //
00059     // note that boost::tribool may have one of THREE states:
00060     //
00061     // false: encountered an error while parsing message
00062     // true: finished successfully parsing the message
00063     // indeterminate: parsed bytes, but the message is not yet finished
00064     //
00065     boost::tribool result = parse(getMessage());
00066     
00067     if (gcount() > 0) {
00068         // parsed > 0 bytes in HTTP headers
00069         PION_LOG_DEBUG(m_logger, "Parsed " << gcount() << " HTTP bytes");
00070     }
00071 
00072     if (result == true) {
00073         // finished reading HTTP message and it is valid
00074 
00075         // set the connection's lifecycle type
00076         if (getMessage().checkKeepAlive()) {
00077             if ( eof() ) {
00078                 // the connection should be kept alive, but does not have pipelined messages
00079                 m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
00080             } else {
00081                 // the connection has pipelined messages
00082                 m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_PIPELINED);
00083 
00084                 // save the read position as a bookmark so that it can be retrieved
00085                 // by a new HTTP parser, which will be created after the current
00086                 // message has been handled
00087                 m_tcp_conn->saveReadPosition(m_read_ptr, m_read_end_ptr);
00088 
00089                 PION_LOG_DEBUG(m_logger, "HTTP pipelined "
00090                                << (isParsingRequest() ? "request (" : "response (")
00091                                << bytes_available() << " bytes available)");
00092             }
00093         } else {
00094             m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00095         }
00096 
00097         // we have finished parsing the HTTP message
00098         finishedReading();
00099 
00100     } else if (result == false) {
00101         // the message is invalid or an error occured
00102 
00103     #ifndef NDEBUG
00104         // display extra error information if debug mode is enabled
00105         std::string bad_message;
00106         m_read_ptr = m_tcp_conn->getReadBuffer().data();
00107         while (m_read_ptr < m_read_end_ptr && bad_message.size() < 50) {
00108 #ifndef _MSC_VER
00109 // There's a bug in MSVC's implementation of isprint().
00110             if (!isprint(*m_read_ptr) || *m_read_ptr == '\n' || *m_read_ptr=='\r')
00111                 bad_message += '.';
00112             else bad_message += *m_read_ptr;
00113 #endif
00114             ++m_read_ptr;
00115         }
00116         PION_LOG_ERROR(m_logger, "Bad " << (isParsingRequest() ? "request" : "response")
00117                        << " debug: " << bad_message);
00118     #endif
00119 
00120         m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // make sure it will get closed
00121         getMessage().setIsValid(false);
00122         finishedReading();
00123         
00124     } else {
00125         // not yet finished parsing the message -> read more data
00126         
00127         readBytes();
00128     }
00129 }
00130 
00131 void HTTPReader::handleReadError(const boost::system::error_code& read_error)
00132 {
00133     // close the connection, forcing the client to establish a new one
00134     m_tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);   // make sure it will get closed
00135 
00136     // check if this is just a message with unknown content length
00137     if (! checkPrematureEOF(getMessage())) {
00138         finishedReading();
00139         return;
00140     }
00141     
00142     // only log errors if the parsing has already begun
00143     if (getTotalBytesRead() > 0) {
00144         if (read_error == boost::asio::error::operation_aborted) {
00145             // if the operation was aborted, the acceptor was stopped,
00146             // which means another thread is shutting-down the server
00147             PION_LOG_INFO(m_logger, "HTTP " << (isParsingRequest() ? "request" : "response")
00148                           << " parsing aborted (shutting down)");
00149         } else {
00150             PION_LOG_INFO(m_logger, "HTTP " << (isParsingRequest() ? "request" : "response")
00151                           << " parsing aborted (" << read_error.message() << ')');
00152         }
00153     }
00154 
00155     // do not trigger the callback when there are errors -> currently no way to propagate them
00156     m_tcp_conn->finish();
00157 }
00158 
00159 }   // end namespace net
00160 }   // end namespace pion
00161 

Generated on Fri Dec 4 08:54:29 2009 for pion-net by  doxygen 1.4.7