pion  5.0.6
src/http_message.cpp
00001 // ---------------------------------------------------------------------
00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
00003 // ---------------------------------------------------------------------
00004 // Copyright (C) 2007-2014 Splunk Inc.  (https://github.com/splunk/pion)
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/assert.hpp>
00014 #include <boost/regex.hpp>
00015 #include <boost/logic/tribool.hpp>
00016 #include <pion/http/message.hpp>
00017 #include <pion/http/request.hpp>
00018 #include <pion/http/parser.hpp>
00019 #include <pion/tcp/connection.hpp>
00020 
00021 
00022 namespace pion {    // begin namespace pion
00023 namespace http {    // begin namespace http
00024 
00025 
00026 // static members of message
00027 
00028 const boost::regex  message::REGEX_ICASE_CHUNKED(".*chunked.*", boost::regex::icase);
00029 
00030 
00031 // message member functions
00032 
00033 std::size_t message::send(tcp::connection& tcp_conn,
00034                           boost::system::error_code& ec, bool headers_only)
00035 {
00036     // initialize write buffers for send operation using HTTP headers
00037     write_buffers_t write_buffers;
00038     prepare_buffers_for_send(write_buffers, tcp_conn.get_keep_alive(), false);
00039 
00040     // append payload content to write buffers (if there is any)
00041     if (!headers_only && get_content_length() > 0 && get_content() != NULL)
00042         write_buffers.push_back(boost::asio::buffer(get_content(), get_content_length()));
00043 
00044     // send the message and return the result
00045     return tcp_conn.write(write_buffers, ec);
00046 }
00047 
00048 std::size_t message::receive(tcp::connection& tcp_conn,
00049                              boost::system::error_code& ec,
00050                              parser& http_parser)
00051 {
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.get_pipelined()) {
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.load_read_pos(read_ptr, read_end_ptr);
00062         last_bytes_read = (read_end_ptr - read_ptr);
00063         http_parser.set_read_buffer(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         BOOST_ASSERT(last_bytes_read > 0);
00069         http_parser.set_read_buffer(tcp_conn.get_read_buffer().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, ec);
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.check_premature_eof(*this)) {
00084                 // premature EOF encountered
00085                 if (! ec)
00086                     ec = make_error_code(boost::system::errc::io_error);
00087                 return http_parser.get_total_bytes_read();
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.set_read_buffer(tcp_conn.get_read_buffer().data(), last_bytes_read);
00102     }
00103     
00104     if (parse_result == false) {
00105         // an error occurred while parsing the message headers
00106         return http_parser.get_total_bytes_read();
00107     }
00108 
00109     // set the connection's lifecycle type
00110     if (!force_connection_closed && check_keep_alive()) {
00111         if ( http_parser.eof() ) {
00112             // the connection should be kept alive, but does not have pipelined messages
00113             tcp_conn.set_lifecycle(tcp::connection::LIFECYCLE_KEEPALIVE);
00114         } else {
00115             // the connection has pipelined messages
00116             tcp_conn.set_lifecycle(tcp::connection::LIFECYCLE_PIPELINED);
00117             
00118             // save the read position as a bookmark so that it can be retrieved
00119             // by a new HTTP parser, which will be created after the current
00120             // message has been handled
00121             const char *read_ptr;
00122             const char *read_end_ptr;
00123             http_parser.load_read_pos(read_ptr, read_end_ptr);
00124             tcp_conn.save_read_pos(read_ptr, read_end_ptr);
00125         }
00126     } else {
00127         // default to close the connection
00128         tcp_conn.set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
00129         
00130         // save the read position as a bookmark so that it can be retrieved
00131         // by a new HTTP parser
00132         if (http_parser.get_parse_headers_only()) {
00133             const char *read_ptr;
00134             const char *read_end_ptr;
00135             http_parser.load_read_pos(read_ptr, read_end_ptr);
00136             tcp_conn.save_read_pos(read_ptr, read_end_ptr);
00137         }
00138     }
00139 
00140     return (http_parser.get_total_bytes_read());
00141 }
00142 
00143 std::size_t message::receive(tcp::connection& tcp_conn,
00144                              boost::system::error_code& ec,
00145                              bool headers_only,
00146                              std::size_t max_content_length)
00147 {
00148     http::parser http_parser(dynamic_cast<http::request*>(this) != NULL);
00149     http_parser.parse_headers_only(headers_only);
00150     http_parser.set_max_content_length(max_content_length);
00151     return receive(tcp_conn, ec, http_parser);
00152 }
00153 
00154 std::size_t message::write(std::ostream& out,
00155     boost::system::error_code& ec, bool headers_only)
00156 {
00157     // reset error_code
00158     ec.clear();
00159 
00160     // initialize write buffers for send operation using HTTP headers
00161     write_buffers_t write_buffers;
00162     prepare_buffers_for_send(write_buffers, true, false);
00163 
00164     // append payload content to write buffers (if there is any)
00165     if (!headers_only && get_content_length() > 0 && get_content() != NULL)
00166         write_buffers.push_back(boost::asio::buffer(get_content(), get_content_length()));
00167 
00168     // write message to the output stream
00169     std::size_t bytes_out = 0;
00170     for (write_buffers_t::const_iterator i=write_buffers.begin(); i!=write_buffers.end(); ++i) {
00171         const char *ptr = boost::asio::buffer_cast<const char*>(*i);
00172         size_t len = boost::asio::buffer_size(*i);
00173         out.write(ptr, len);
00174         bytes_out += len;
00175     }
00176 
00177     return bytes_out;
00178 }
00179 
00180 std::size_t message::read(std::istream& in,
00181                           boost::system::error_code& ec,
00182                           parser& http_parser)
00183 {
00184     // make sure that we start out with an empty message & clear error_code
00185     clear();
00186     ec.clear();
00187     
00188     // parse data from file one byte at a time
00189     boost::tribool parse_result;
00190     char c;
00191     while (in) {
00192         in.read(&c, 1);
00193         if ( ! in ) {
00194             ec = make_error_code(boost::system::errc::io_error);
00195             break;
00196         }
00197         http_parser.set_read_buffer(&c, 1);
00198         parse_result = http_parser.parse(*this, ec);
00199         if (! boost::indeterminate(parse_result)) break;
00200     }
00201 
00202     if (boost::indeterminate(parse_result)) {
00203         if (http_parser.check_premature_eof(*this)) {
00204             // premature EOF encountered
00205             if (! ec)
00206                 ec = make_error_code(boost::system::errc::io_error);
00207         } else {
00208             // EOF reached when content length unknown
00209             // assume it is the correct end of content
00210             // and everything is OK
00211             parse_result = true;
00212             ec.clear();
00213         }
00214     }
00215     
00216     return (http_parser.get_total_bytes_read());
00217 }
00218 
00219 std::size_t message::read(std::istream& in,
00220                           boost::system::error_code& ec,
00221                           bool headers_only,
00222                           std::size_t max_content_length)
00223 {
00224     http::parser http_parser(dynamic_cast<http::request*>(this) != NULL);
00225     http_parser.parse_headers_only(headers_only);
00226     http_parser.set_max_content_length(max_content_length);
00227     return read(in, ec, http_parser);
00228 }
00229 
00230 void message::concatenate_chunks(void)
00231 {
00232     set_content_length(m_chunk_cache.size());
00233     char *post_buffer = create_content_buffer();
00234     if (m_chunk_cache.size() > 0)
00235         std::copy(m_chunk_cache.begin(), m_chunk_cache.end(), post_buffer);
00236 }
00237 
00238 
00239 }   // end namespace http
00240 }   // end namespace pion