- Main Page
- Classes
- Files

- File List

# 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