pion
5.0.6
|
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