pion-net
4.0.9
|
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