# 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 <iostream>
    00011 #include <algorithm>
    00012 #include <boost/asio.hpp>
    00013 #include <boost/regex.hpp>
    00014 #include <boost/logic/tribool.hpp>
    00015 #include <pion/net/HTTPMessage.hpp>
    00016 #include <pion/net/HTTPRequest.hpp>
    00017 #include <pion/net/HTTPParser.hpp>
    00018 #include <pion/net/TCPConnection.hpp>
    00019
    00020
    00021 namespace pion {    // begin namespace pion
    00022 namespace net {     // begin namespace net (Pion Network Library)
    00023
    00024 // static members of HTTPMessage
    00025
    00026 const boost::regex      HTTPMessage::REGEX_ICASE_CHUNKED(".*chunked.*", boost::regex::icase);
    00027
    00028
    00029 // HTTPMessage member functions
    00030
    00031 std::size_t HTTPMessage::send(TCPConnection& tcp_conn,
    00032                               boost::system::error_code& ec,
    00033                               bool headers_only)
    00034 {
    00035     // initialize write buffers for send operation using HTTP headers
    00036     WriteBuffers write_buffers;
    00037     prepareBuffersForSend(write_buffers, tcp_conn.getKeepAlive(), false);
    00038
    00039     // append payload content to write buffers (if there is any)
    00040     if (!headers_only && getContentLength() > 0 && getContent() != NULL)
    00041         write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength()));
    00042
    00043     // send the message and return the result
    00044     return tcp_conn.write(write_buffers, ec);
    00045 }
    00046
    00047 std::size_t HTTPMessage::receive(TCPConnection& tcp_conn,
    00048                                  boost::system::error_code& ec,
    00049                                  bool headers_only)
    00050 {
    00051     // assumption: this can only be either an HTTPRequest or an HTTPResponse
    00052     const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL);
    00053     HTTPParser http_parser(is_request);
    00054     http_parser.parseHeadersOnly(headers_only);
    00055     std::size_t last_bytes_read = 0;
    00056
    00057     // make sure that we start out with an empty message
    00058     clear();
    00059
    00060     if (tcp_conn.getPipelined()) {
    00061         // there are pipelined messages available in the connection's read buffer
    00062         const char *read_ptr;
    00063         const char *read_end_ptr;
    00064         tcp_conn.loadReadPosition(read_ptr, read_end_ptr);
    00065         last_bytes_read = (read_end_ptr - read_ptr);
    00066         http_parser.setReadBuffer(read_ptr, last_bytes_read);
    00067     } else {
    00068         // read buffer is empty (not pipelined) -> read some bytes from the connection
    00069         last_bytes_read = tcp_conn.read_some(ec);
    00070         if (ec) return 0;
    00071         PION_ASSERT(last_bytes_read > 0);
    00072         http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read);
    00073     }
    00074
    00075     // incrementally read and parse bytes from the connection
    00076     bool force_connection_closed = false;
    00077     boost::tribool parse_result;
    00078     while (true) {
    00079         // parse bytes available in the read buffer
    00080         parse_result = http_parser.parse(*this, ec);
    00081         if (! boost::indeterminate(parse_result)) break;
    00082
    00083         // read more bytes from the connection
    00084         last_bytes_read = tcp_conn.read_some(ec);
    00085         if (ec || last_bytes_read == 0) {
    00086             if (http_parser.checkPrematureEOF(*this)) {
    00087                 // premature EOF encountered
    00088                 if (! ec)
    00089                     ec = make_error_code(boost::system::errc::io_error);
    00090                 return http_parser.getTotalBytesRead();
    00091             } else {
    00092                 // EOF reached when content length unknown
    00093                 // assume it is the correct end of content
    00094                 // and everything is OK
    00095                 force_connection_closed = true;
    00096                 parse_result = true;
    00097                 ec.clear();
    00098                 break;
    00099             }
    00100             break;
    00101         }
    00102
    00103         // update the HTTP parser's read buffer
    00104         http_parser.setReadBuffer(tcp_conn.getReadBuffer().data(), last_bytes_read);
    00105     }
    00106
    00107     if (parse_result == false) {
    00108         // an error occurred while parsing the message headers
    00109         return http_parser.getTotalBytesRead();
    00110     }
    00111
    00112     // set the connection's lifecycle type
    00113     if (!force_connection_closed && checkKeepAlive()) {
    00114         if ( http_parser.eof() ) {
    00115             // the connection should be kept alive, but does not have pipelined messages
    00116             tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_KEEPALIVE);
    00117         } else {
    00118             // the connection has pipelined messages
    00119             tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_PIPELINED);
    00120
    00121             // save the read position as a bookmark so that it can be retrieved
    00122             // by a new HTTP parser, which will be created after the current
    00123             // message has been handled
    00124             const char *read_ptr;
    00125             const char *read_end_ptr;
    00126             http_parser.loadReadPosition(read_ptr, read_end_ptr);
    00127             tcp_conn.saveReadPosition(read_ptr, read_end_ptr);
    00128         }
    00129     } else {
    00130         // default to close the connection
    00131         tcp_conn.setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
    00132     }
    00133
    00134     return (http_parser.getTotalBytesRead());
    00135 }
    00136
    00137 std::size_t HTTPMessage::write(std::ostream& out,
    00138     boost::system::error_code& ec, bool headers_only)
    00139 {
    00140     // reset error_code
    00141     ec.clear();
    00142
    00143     // initialize write buffers for send operation using HTTP headers
    00144     WriteBuffers write_buffers;
    00145     prepareBuffersForSend(write_buffers, true, false);
    00146
    00147     // append payload content to write buffers (if there is any)
    00148     if (!headers_only && getContentLength() > 0 && getContent() != NULL)
    00149         write_buffers.push_back(boost::asio::buffer(getContent(), getContentLength()));
    00150
    00151     // write message to the output stream
    00152     std::size_t bytes_out = 0;
    00153     for (WriteBuffers::const_iterator i=write_buffers.begin(); i!=write_buffers.end(); ++i) {
    00154         const char *ptr = boost::asio::buffer_cast<const char*>(*i);
    00155         size_t len = boost::asio::buffer_size(*i);
    00156         out.write(ptr, len);
    00157         bytes_out += len;
    00158     }
    00159
    00160     return bytes_out;
    00161 }
    00162
    00163 std::size_t HTTPMessage::read(std::istream& in,
    00164     boost::system::error_code& ec, bool headers_only)
    00165 {
    00166     // make sure that we start out with an empty message & clear error_code
    00167     clear();
    00168     ec.clear();
    00169
    00170     // assumption: this can only be either an HTTPRequest or an HTTPResponse
    00171     const bool is_request = (dynamic_cast<HTTPRequest*>(this) != NULL);
    00172     HTTPParser http_parser(is_request);
    00173     http_parser.parseHeadersOnly(headers_only);
    00174
    00175     // parse data from file one byte at a time
    00176     boost::tribool parse_result;
    00177     char c;
    00178     while (in) {
    00179         in.read(&c, 1);
    00180         if ( ! in ) {
    00181             ec = make_error_code(boost::system::errc::io_error);
    00182             break;
    00183         }
    00184         http_parser.setReadBuffer(&c, 1);
    00185         parse_result = http_parser.parse(*this, ec);
    00186         if (! boost::indeterminate(parse_result)) break;
    00187     }
    00188
    00189     if (boost::indeterminate(parse_result)) {
    00190         if (http_parser.checkPrematureEOF(*this)) {
    00191             // premature EOF encountered
    00192             if (! ec)
    00193                 ec = make_error_code(boost::system::errc::io_error);
    00194         } else {
    00195             // EOF reached when content length unknown
    00196             // assume it is the correct end of content
    00197             // and everything is OK
    00198             parse_result = true;
    00199             ec.clear();
    00200         }
    00201     }
    00202
    00203     return (http_parser.getTotalBytesRead());
    00204 }
    00205
    00206 void HTTPMessage::concatenateChunks(void)
    00207 {
    00208     setContentLength(m_chunk_cache.size());
    00209     char *post_buffer = createContentBuffer();
    00210     if (m_chunk_cache.size() > 0)
    00211         std::copy(m_chunk_cache.begin(), m_chunk_cache.end(), post_buffer);
    00212 }
    00213
    00214 }   // end namespace net
    00215 }   // end namespace pion