pion  5.0.6
http_message.cpp
1 // ---------------------------------------------------------------------
2 // pion: a Boost C++ framework for building lightweight HTTP interfaces
3 // ---------------------------------------------------------------------
4 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #include <iostream>
11 #include <algorithm>
12 #include <boost/asio.hpp>
13 #include <boost/assert.hpp>
14 #include <boost/regex.hpp>
15 #include <boost/logic/tribool.hpp>
16 #include <pion/http/message.hpp>
17 #include <pion/http/request.hpp>
18 #include <pion/http/parser.hpp>
19 #include <pion/tcp/connection.hpp>
20 
21 
22 namespace pion { // begin namespace pion
23 namespace http { // begin namespace http
24 
25 
26 // static members of message
27 
28 const boost::regex message::REGEX_ICASE_CHUNKED(".*chunked.*", boost::regex::icase);
29 
30 
31 // message member functions
32 
33 std::size_t message::send(tcp::connection& tcp_conn,
34  boost::system::error_code& ec, bool headers_only)
35 {
36  // initialize write buffers for send operation using HTTP headers
37  write_buffers_t write_buffers;
38  prepare_buffers_for_send(write_buffers, tcp_conn.get_keep_alive(), false);
39 
40  // append payload content to write buffers (if there is any)
41  if (!headers_only && get_content_length() > 0 && get_content() != NULL)
42  write_buffers.push_back(boost::asio::buffer(get_content(), get_content_length()));
43 
44  // send the message and return the result
45  return tcp_conn.write(write_buffers, ec);
46 }
47 
48 std::size_t message::receive(tcp::connection& tcp_conn,
49  boost::system::error_code& ec,
50  parser& http_parser)
51 {
52  std::size_t last_bytes_read = 0;
53 
54  // make sure that we start out with an empty message
55  clear();
56 
57  if (tcp_conn.get_pipelined()) {
58  // there are pipelined messages available in the connection's read buffer
59  const char *read_ptr;
60  const char *read_end_ptr;
61  tcp_conn.load_read_pos(read_ptr, read_end_ptr);
62  last_bytes_read = (read_end_ptr - read_ptr);
63  http_parser.set_read_buffer(read_ptr, last_bytes_read);
64  } else {
65  // read buffer is empty (not pipelined) -> read some bytes from the connection
66  last_bytes_read = tcp_conn.read_some(ec);
67  if (ec) return 0;
68  BOOST_ASSERT(last_bytes_read > 0);
69  http_parser.set_read_buffer(tcp_conn.get_read_buffer().data(), last_bytes_read);
70  }
71 
72  // incrementally read and parse bytes from the connection
73  bool force_connection_closed = false;
74  boost::tribool parse_result;
75  while (true) {
76  // parse bytes available in the read buffer
77  parse_result = http_parser.parse(*this, ec);
78  if (! boost::indeterminate(parse_result)) break;
79 
80  // read more bytes from the connection
81  last_bytes_read = tcp_conn.read_some(ec);
82  if (ec || last_bytes_read == 0) {
83  if (http_parser.check_premature_eof(*this)) {
84  // premature EOF encountered
85  if (! ec)
86  ec = make_error_code(boost::system::errc::io_error);
87  return http_parser.get_total_bytes_read();
88  } else {
89  // EOF reached when content length unknown
90  // assume it is the correct end of content
91  // and everything is OK
92  force_connection_closed = true;
93  parse_result = true;
94  ec.clear();
95  break;
96  }
97  break;
98  }
99 
100  // update the HTTP parser's read buffer
101  http_parser.set_read_buffer(tcp_conn.get_read_buffer().data(), last_bytes_read);
102  }
103 
104  if (parse_result == false) {
105  // an error occurred while parsing the message headers
106  return http_parser.get_total_bytes_read();
107  }
108 
109  // set the connection's lifecycle type
110  if (!force_connection_closed && check_keep_alive()) {
111  if ( http_parser.eof() ) {
112  // the connection should be kept alive, but does not have pipelined messages
113  tcp_conn.set_lifecycle(tcp::connection::LIFECYCLE_KEEPALIVE);
114  } else {
115  // the connection has pipelined messages
116  tcp_conn.set_lifecycle(tcp::connection::LIFECYCLE_PIPELINED);
117 
118  // save the read position as a bookmark so that it can be retrieved
119  // by a new HTTP parser, which will be created after the current
120  // message has been handled
121  const char *read_ptr;
122  const char *read_end_ptr;
123  http_parser.load_read_pos(read_ptr, read_end_ptr);
124  tcp_conn.save_read_pos(read_ptr, read_end_ptr);
125  }
126  } else {
127  // default to close the connection
128  tcp_conn.set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
129 
130  // save the read position as a bookmark so that it can be retrieved
131  // by a new HTTP parser
132  if (http_parser.get_parse_headers_only()) {
133  const char *read_ptr;
134  const char *read_end_ptr;
135  http_parser.load_read_pos(read_ptr, read_end_ptr);
136  tcp_conn.save_read_pos(read_ptr, read_end_ptr);
137  }
138  }
139 
140  return (http_parser.get_total_bytes_read());
141 }
142 
143 std::size_t message::receive(tcp::connection& tcp_conn,
144  boost::system::error_code& ec,
145  bool headers_only,
146  std::size_t max_content_length)
147 {
148  http::parser http_parser(dynamic_cast<http::request*>(this) != NULL);
149  http_parser.parse_headers_only(headers_only);
150  http_parser.set_max_content_length(max_content_length);
151  return receive(tcp_conn, ec, http_parser);
152 }
153 
154 std::size_t message::write(std::ostream& out,
155  boost::system::error_code& ec, bool headers_only)
156 {
157  // reset error_code
158  ec.clear();
159 
160  // initialize write buffers for send operation using HTTP headers
161  write_buffers_t write_buffers;
162  prepare_buffers_for_send(write_buffers, true, false);
163 
164  // append payload content to write buffers (if there is any)
165  if (!headers_only && get_content_length() > 0 && get_content() != NULL)
166  write_buffers.push_back(boost::asio::buffer(get_content(), get_content_length()));
167 
168  // write message to the output stream
169  std::size_t bytes_out = 0;
170  for (write_buffers_t::const_iterator i=write_buffers.begin(); i!=write_buffers.end(); ++i) {
171  const char *ptr = boost::asio::buffer_cast<const char*>(*i);
172  size_t len = boost::asio::buffer_size(*i);
173  out.write(ptr, len);
174  bytes_out += len;
175  }
176 
177  return bytes_out;
178 }
179 
180 std::size_t message::read(std::istream& in,
181  boost::system::error_code& ec,
182  parser& http_parser)
183 {
184  // make sure that we start out with an empty message & clear error_code
185  clear();
186  ec.clear();
187 
188  // parse data from file one byte at a time
189  boost::tribool parse_result;
190  char c;
191  while (in) {
192  in.read(&c, 1);
193  if ( ! in ) {
194  ec = make_error_code(boost::system::errc::io_error);
195  break;
196  }
197  http_parser.set_read_buffer(&c, 1);
198  parse_result = http_parser.parse(*this, ec);
199  if (! boost::indeterminate(parse_result)) break;
200  }
201 
202  if (boost::indeterminate(parse_result)) {
203  if (http_parser.check_premature_eof(*this)) {
204  // premature EOF encountered
205  if (! ec)
206  ec = make_error_code(boost::system::errc::io_error);
207  } else {
208  // EOF reached when content length unknown
209  // assume it is the correct end of content
210  // and everything is OK
211  parse_result = true;
212  ec.clear();
213  }
214  }
215 
216  return (http_parser.get_total_bytes_read());
217 }
218 
219 std::size_t message::read(std::istream& in,
220  boost::system::error_code& ec,
221  bool headers_only,
222  std::size_t max_content_length)
223 {
224  http::parser http_parser(dynamic_cast<http::request*>(this) != NULL);
225  http_parser.parse_headers_only(headers_only);
226  http_parser.set_max_content_length(max_content_length);
227  return read(in, ec, http_parser);
228 }
229 
231 {
232  set_content_length(m_chunk_cache.size());
233  char *post_buffer = create_content_buffer();
234  if (m_chunk_cache.size() > 0)
235  std::copy(m_chunk_cache.begin(), m_chunk_cache.end(), post_buffer);
236 }
237 
238 
239 } // end namespace http
240 } // end namespace pion
bool check_keep_alive(void) const
returns true if the HTTP connection may be kept alive
Definition: message.hpp:378
void save_read_pos(const char *read_ptr, const char *read_end_ptr)
Definition: connection.hpp:622
virtual void clear(void)
clears all message data
Definition: message.hpp:146
void set_max_content_length(std::size_t n)
sets the maximum length for HTTP payload content
Definition: parser.hpp:285
void concatenate_chunks(void)
bool eof(void) const
returns true if there are no more bytes available in the read buffer
Definition: parser.hpp:249
void set_content_length(size_t n)
sets the length of the payload content (in bytes)
Definition: message.hpp:302
std::size_t send(tcp::connection &tcp_conn, boost::system::error_code &ec, bool headers_only=false)
std::size_t read(std::istream &in, boost::system::error_code &ec, parser &http_parser)
std::size_t read_some(boost::system::error_code &ec)
Definition: connection.hpp:424
read_buffer_type & get_read_buffer(void)
returns the buffer used for reading data from the TCP connection
Definition: connection.hpp:614
boost::tribool parse(http::message &http_msg, boost::system::error_code &ec)
Definition: http_parser.cpp:46
bool get_parse_headers_only(void)
returns true if parsing headers only
Definition: parser.hpp:273
std::size_t write(std::ostream &out, boost::system::error_code &ec, bool headers_only=false)
bool get_pipelined(void) const
returns true if the HTTP requests are pipelined
Definition: connection.hpp:611
std::size_t get_total_bytes_read(void) const
returns the total number of bytes read while parsing the HTTP message
Definition: parser.hpp:258
void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const
Definition: parser.hpp:195
bool get_keep_alive(void) const
returns true if the connection should be kept alive
Definition: connection.hpp:608
size_t get_content_length(void) const
returns the length of the payload content (in bytes)
Definition: message.hpp:192
void set_lifecycle(lifecycle_type t)
sets the lifecycle type for the connection
Definition: connection.hpp:602
std::size_t receive(tcp::connection &tcp_conn, boost::system::error_code &ec, parser &http_parser)
void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const
Definition: connection.hpp:633
char * create_content_buffer(void)
Definition: message.hpp:338
char * get_content(void)
returns a pointer to the payload content, or empty string if there is none
Definition: message.hpp:204
void prepare_buffers_for_send(write_buffers_t &write_buffers, const bool keep_alive, const bool using_chunks)
Definition: message.hpp:391
std::size_t write(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Definition: connection.hpp:580
bool check_premature_eof(http::message &http_msg)
Definition: parser.hpp:208
std::vector< boost::asio::const_buffer > write_buffers_t
data type for I/O write buffers (these wrap existing data to be sent)
Definition: message.hpp:61
void parse_headers_only(bool b=true)
Definition: parser.hpp:222
void set_read_buffer(const char *ptr, size_t len)
Definition: parser.hpp:184