net/src/HTTPMessage.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 <algorithm>
00011 #include <boost/asio.hpp>
00012 #include <boost/regex.hpp>
00013 #include <boost/logic/tribool.hpp>
00014 #include <pion/net/HTTPMessage.hpp>
00015 #include <pion/net/HTTPRequest.hpp>
00016 #include <pion/net/HTTPParser.hpp>
00017 #include <pion/net/TCPConnection.hpp>
00018 
00019 
00020 namespace pion {    // begin namespace pion
00021 namespace net {     // begin namespace net (Pion Network Library)
00022 
00023 // static members of HTTPMessage
00024 
00025 const boost::regex      HTTPMessage::REGEX_ICASE_CHUNKED("chunked", boost::regex::icase);
00026 
00027 
00028 // HTTPMessage member functions
00029 
00030 std::size_t HTTPMessage::send(TCPConnection& tcp_conn,
00031                               boost::system::error_code& ec)
00032 {
00033     // initialize write buffers for send operation using HTTP headers
00034     WriteBuffers write_buffers;
00035     prepareBuffersForSend(write_buffers, tcp_conn.getKeepAlive(), false);
00036 
00037     // append payload content to write buffers (if there is any)
00038     if (getContentLength() > 0 && getContent() != NULL)
00039         write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength()));
00040 
00041     // send the message and return the result
00042     return tcp_conn.write(write_buffers, ec);
00043 }
00044 
00045 std::size_t HTTPMessage::receive(TCPConnection& tcp_conn,
00046                                  boost::system::error_code& ec)
00047 {
00048     static ReceiveError RECEIVE_ERROR;
00049     // assumption: this can only be either an HTTPRequest or an HTTPResponse
00050     const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL);
00051     HTTPParser http_parser(is_request);
00052     std::size_t last_bytes_read = 0;
00053 
00054     // make sure that we start out with an empty message
00055     clear();
00056 
00057     if (tcp_conn.getPipelined()) {
00058         // there are pipelined messages available in the connection's read buffer
00059         const char *read_ptr;
00060         const char *read_end_ptr;
00061         tcp_conn.loadReadPosition(read_ptr, read_end_ptr);
00062         last_bytes_read = (read_end_ptr - read_ptr);
00063         http_parser.setReadBuffer(read_ptr, last_bytes_read);
00064     } else {
00065         // read buffer is empty (not pipelined) -> read some bytes from the connection
00066         last_bytes_read = tcp_conn.read_some(ec);
00067         if (ec) return 0;
00068         PION_ASSERT(last_bytes_read > 0);
00069         http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read);
00070     }
00071 
00072     // incrementally read and parse bytes from the connection
00073     bool force_connection_closed = false;
00074     boost::tribool parse_result;
00075     while (true) {
00076         // parse bytes available in the read buffer
00077         parse_result = http_parser.parse(*this);
00078         if (! boost::indeterminate(parse_result)) break;
00079 
00080         // read more bytes from the connection
00081         last_bytes_read = tcp_conn.read_some(ec);
00082         if (ec || last_bytes_read == 0) {
00083             if (http_parser.checkPrematureEOF(*this)) {
00084                 // premature EOF encountered
00085                 if (! ec)
00086                     ec.assign(1, RECEIVE_ERROR);
00087                 return http_parser.getTotalBytesRead();
00088             } else {
00089                 // EOF reached when content length unknown
00090                 // assume it is the correct end of content
00091                 // and everything is OK
00092                 force_connection_closed = true;
00093                 parse_result = true;
00094                 ec.clear();
00095                 break;
00096             }
00097             break;
00098         }
00099 
00100         // update the HTTP parser's read buffer
00101         http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read);
00102     }
00103     
00104     if (parse_result == false) {
00105         // an error occurred while parsing the message headers
00106         ec.assign(1, RECEIVE_ERROR);
00107         return http_parser.getTotalBytesRead();
00108     }
00109 
00110     // set the connection's lifecycle type
00111     if (!force_connection_closed && checkKeepAlive()) {
00112         if ( http_parser.eof() ) {
00113             // the connection should be kept alive, but does not have pipelined messages
00114             tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
00115         } else {
00116             // the connection has pipelined messages
00117             tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_PIPELINED);
00118             
00119             // save the read position as a bookmark so that it can be retrieved
00120             // by a new HTTP parser, which will be created after the current
00121             // message has been handled
00122             const char *read_ptr;
00123             const char *read_end_ptr;
00124             http_parser.loadReadPosition(read_ptr, read_end_ptr);
00125             tcp_conn.saveReadPosition(read_ptr, read_end_ptr);
00126         }
00127     } else {
00128         // default to close the connection
00129         tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00130     }
00131 
00132     return (http_parser.getTotalBytesRead());
00133 }
00134     
00135 void HTTPMessage::concatenateChunks(void)
00136 {
00137     setContentLength(m_chunk_cache.size());
00138     char *post_buffer = createContentBuffer();
00139     if (m_chunk_cache.size() > 0)
00140         std::copy(m_chunk_cache.begin(), m_chunk_cache.end(), post_buffer);
00141 }
00142     
00143 }   // end namespace net
00144 }   // end namespace pion

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