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