pion  5.0.6
message.hpp
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 #ifndef __PION_HTTP_MESSAGE_HEADER__
11 #define __PION_HTTP_MESSAGE_HEADER__
12 
13 #include <iosfwd>
14 #include <vector>
15 #include <cstring>
16 #include <boost/cstdint.hpp>
17 #include <boost/asio.hpp>
18 #include <boost/scoped_array.hpp>
19 #include <boost/lexical_cast.hpp>
20 #include <boost/algorithm/string/trim.hpp>
21 #include <boost/regex.hpp>
22 #include <pion/config.hpp>
23 #include <pion/http/types.hpp>
24 
25 #ifndef BOOST_SYSTEM_NOEXCEPT
26  #define BOOST_SYSTEM_NOEXCEPT BOOST_NOEXCEPT
27 #endif
28 
29 
30 namespace pion { // begin namespace pion
31 
32 
33 namespace tcp {
34  // forward declaration for class used by send() and receive()
35  class connection;
36 }
37 
38 
39 namespace http { // begin namespace http
40 
41 
42 // forward declaration of parser class
43 class parser;
44 
45 
49 class PION_API message
50  : public http::types
51 {
52 public:
53 
55  typedef std::vector<boost::asio::const_buffer> write_buffers_t;
56 
58  typedef std::vector<char> chunk_cache_t;
59 
62  : public boost::system::error_category
63  {
64  virtual ~receive_error_t() {}
65  virtual inline const char *name() const BOOST_SYSTEM_NOEXCEPT { return "receive_error_t"; }
66  virtual inline std::string message(int ev) const {
67  std::string result;
68  switch(ev) {
69  case 1:
70  result = "HTTP message parsing error";
71  break;
72  default:
73  result = "Unknown receive error";
74  break;
75  }
76  return result;
77  }
78  };
79 
82  {
83  STATUS_NONE, // no data received (i.e. all lost)
84  STATUS_TRUNCATED, // one or more missing packets at the end
85  STATUS_PARTIAL, // one or more missing packets but NOT at the end
86  STATUS_OK // no missing packets
87  };
88 
90  message(void)
91  : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
92  m_do_not_send_content_length(false),
93  m_version_major(1), m_version_minor(1), m_content_length(0), m_content_buf(),
94  m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false)
95  {}
96 
98  message(const message& http_msg)
99  : m_first_line(http_msg.m_first_line),
100  m_is_valid(http_msg.m_is_valid),
101  m_is_chunked(http_msg.m_is_chunked),
102  m_chunks_supported(http_msg.m_chunks_supported),
103  m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
104  m_remote_ip(http_msg.m_remote_ip),
105  m_version_major(http_msg.m_version_major),
106  m_version_minor(http_msg.m_version_minor),
107  m_content_length(http_msg.m_content_length),
108  m_content_buf(http_msg.m_content_buf),
109  m_chunk_cache(http_msg.m_chunk_cache),
110  m_headers(http_msg.m_headers),
111  m_status(http_msg.m_status),
112  m_has_missing_packets(http_msg.m_has_missing_packets),
113  m_has_data_after_missing(http_msg.m_has_data_after_missing)
114  {}
115 
117  inline message& operator=(const message& http_msg) {
118  m_first_line = http_msg.m_first_line;
119  m_is_valid = http_msg.m_is_valid;
120  m_is_chunked = http_msg.m_is_chunked;
121  m_chunks_supported = http_msg.m_chunks_supported;
122  m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
123  m_remote_ip = http_msg.m_remote_ip;
124  m_version_major = http_msg.m_version_major;
125  m_version_minor = http_msg.m_version_minor;
126  m_content_length = http_msg.m_content_length;
127  m_content_buf = http_msg.m_content_buf;
128  m_chunk_cache = http_msg.m_chunk_cache;
129  m_headers = http_msg.m_headers;
130  m_status = http_msg.m_status;
131  m_has_missing_packets = http_msg.m_has_missing_packets;
132  m_has_data_after_missing = http_msg.m_has_data_after_missing;
133  return *this;
134  }
135 
137  virtual ~message() {}
138 
140  virtual void clear(void) {
141  clear_first_line();
142  m_is_valid = m_is_chunked = m_chunks_supported
143  = m_do_not_send_content_length = false;
144  m_remote_ip = boost::asio::ip::address_v4(0);
145  m_version_major = m_version_minor = 1;
146  m_content_length = 0;
147  m_content_buf.clear();
148  m_chunk_cache.clear();
149  m_headers.clear();
150  m_cookie_params.clear();
151  m_status = STATUS_NONE;
152  m_has_missing_packets = false;
153  m_has_data_after_missing = false;
154  }
155 
157  virtual bool is_content_length_implied(void) const = 0;
158 
160  inline bool is_valid(void) const { return m_is_valid; }
161 
163  inline bool get_chunks_supported(void) const { return m_chunks_supported; }
164 
166  inline boost::asio::ip::address& get_remote_ip(void) {
167  return m_remote_ip;
168  }
169 
171  inline boost::uint16_t get_version_major(void) const { return m_version_major; }
172 
174  inline boost::uint16_t get_version_minor(void) const { return m_version_minor; }
175 
177  inline std::string get_version_string(void) const {
178  std::string http_version(STRING_HTTP_VERSION);
179  http_version += boost::lexical_cast<std::string>(get_version_major());
180  http_version += '.';
181  http_version += boost::lexical_cast<std::string>(get_version_minor());
182  return http_version;
183  }
184 
186  inline size_t get_content_length(void) const { return m_content_length; }
187 
189  inline bool is_chunked(void) const { return m_is_chunked; }
190 
192  bool is_content_buffer_allocated() const { return !m_content_buf.is_empty(); }
193 
195  inline std::size_t get_content_buffer_size() const { return m_content_buf.size(); }
196 
198  inline char *get_content(void) { return m_content_buf.get(); }
199 
201  inline const char *get_content(void) const { return m_content_buf.get(); }
202 
204  inline chunk_cache_t& get_chunk_cache(void) { return m_chunk_cache; }
205 
207  inline const std::string& get_header(const std::string& key) const {
208  return get_value(m_headers, key);
209  }
210 
212  inline ihash_multimap& get_headers(void) {
213  return m_headers;
214  }
215 
217  inline bool has_header(const std::string& key) const {
218  return(m_headers.find(key) != m_headers.end());
219  }
220 
223  inline const std::string& get_cookie(const std::string& key) const {
224  return get_value(m_cookie_params, key);
225  }
226 
228  inline ihash_multimap& get_cookies(void) {
229  return m_cookie_params;
230  }
231 
234  inline bool has_cookie(const std::string& key) const {
235  return(m_cookie_params.find(key) != m_cookie_params.end());
236  }
237 
240  inline void add_cookie(const std::string& key, const std::string& value) {
241  m_cookie_params.insert(std::make_pair(key, value));
242  }
243 
246  inline void change_cookie(const std::string& key, const std::string& value) {
247  change_value(m_cookie_params, key, value);
248  }
249 
252  inline void delete_cookie(const std::string& key) {
253  delete_value(m_cookie_params, key);
254  }
255 
257  inline const std::string& get_first_line(void) const {
258  if (m_first_line.empty())
259  update_first_line();
260  return m_first_line;
261  }
262 
264  inline bool has_missing_packets() const { return m_has_missing_packets; }
265 
267  inline void set_missing_packets(bool newVal) { m_has_missing_packets = newVal; }
268 
270  inline bool has_data_after_missing_packets() const { return m_has_data_after_missing; }
271 
272  inline void set_data_after_missing_packet(bool newVal) { m_has_data_after_missing = newVal; }
273 
275  inline void set_is_valid(bool b = true) { m_is_valid = b; }
276 
278  inline void set_chunks_supported(bool b) { m_chunks_supported = b; }
279 
281  inline void set_remote_ip(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
282 
284  inline void set_version_major(const boost::uint16_t n) {
285  m_version_major = n;
286  clear_first_line();
287  }
288 
290  inline void set_version_minor(const boost::uint16_t n) {
291  m_version_minor = n;
292  clear_first_line();
293  }
294 
296  inline void set_content_length(size_t n) { m_content_length = n; }
297 
299  inline void set_do_not_send_content_length(void) { m_do_not_send_content_length = true; }
300 
302  inline data_status_t get_status() const { return m_status; }
303 
305  inline void set_status(data_status_t newVal) { m_status = newVal; }
306 
309  ihash_multimap::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
310  if (i == m_headers.end()) {
311  m_content_length = 0;
312  } else {
313  std::string trimmed_length(i->second);
314  boost::algorithm::trim(trimmed_length);
315  m_content_length = boost::lexical_cast<size_t>(trimmed_length);
316  }
317  }
318 
321  m_is_chunked = false;
322  ihash_multimap::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
323  if (i != m_headers.end()) {
324  // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
325  m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
326  // ignoring other possible values for now
327  }
328  }
329 
332  inline char *create_content_buffer(void) {
333  m_content_buf.resize(m_content_length);
334  return m_content_buf.get();
335  }
336 
338  inline void set_content(const std::string& content) {
339  set_content_length(content.size());
340  create_content_buffer();
341  memcpy(m_content_buf.get(), content.c_str(), content.size());
342  }
343 
345  inline void clear_content(void) {
346  set_content_length(0);
347  create_content_buffer();
348  delete_value(m_headers, HEADER_CONTENT_TYPE);
349  }
350 
352  inline void set_content_type(const std::string& type) {
353  change_value(m_headers, HEADER_CONTENT_TYPE, type);
354  }
355 
357  inline void add_header(const std::string& key, const std::string& value) {
358  m_headers.insert(std::make_pair(key, value));
359  }
360 
362  inline void change_header(const std::string& key, const std::string& value) {
363  change_value(m_headers, key, value);
364  }
365 
367  inline void delete_header(const std::string& key) {
368  delete_value(m_headers, key);
369  }
370 
372  inline bool check_keep_alive(void) const {
373  return (get_header(HEADER_CONNECTION) != "close"
374  && (get_version_major() > 1
375  || (get_version_major() >= 1 && get_version_minor() >= 1)) );
376  }
377 
385  inline void prepare_buffers_for_send(write_buffers_t& write_buffers,
386  const bool keep_alive,
387  const bool using_chunks)
388  {
389  // update message headers
390  prepare_headers_for_send(keep_alive, using_chunks);
391  // add first message line
392  write_buffers.push_back(boost::asio::buffer(get_first_line()));
393  write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
394  // append cookie headers (if any)
395  append_cookie_headers();
396  // append HTTP headers
397  append_headers(write_buffers);
398  }
399 
400 
410  std::size_t send(tcp::connection& tcp_conn,
411  boost::system::error_code& ec,
412  bool headers_only = false);
413 
423  std::size_t receive(tcp::connection& tcp_conn,
424  boost::system::error_code& ec,
425  parser& http_parser);
426 
437  std::size_t receive(tcp::connection& tcp_conn,
438  boost::system::error_code& ec,
439  bool headers_only = false,
440  std::size_t max_content_length = static_cast<size_t>(-1));
441 
451  std::size_t write(std::ostream& out,
452  boost::system::error_code& ec,
453  bool headers_only = false);
454 
464  std::size_t read(std::istream& in,
465  boost::system::error_code& ec,
466  parser& http_parser);
467 
478  std::size_t read(std::istream& in,
479  boost::system::error_code& ec,
480  bool headers_only = false,
481  std::size_t max_content_length = static_cast<size_t>(-1));
482 
486  void concatenate_chunks(void);
487 
488 
489 protected:
490 
493  public:
496 
498  content_buffer_t() : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty) {}
499 
502  : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty)
503  {
504  if (buf.size()) {
505  resize(buf.size());
506  memcpy(get(), buf.get(), buf.size());
507  }
508  }
509 
512  if (buf.size()) {
513  resize(buf.size());
514  memcpy(get(), buf.get(), buf.size());
515  } else {
516  clear();
517  }
518  return *this;
519  }
520 
522  inline bool is_empty() const { return m_len == 0; }
523 
525  inline std::size_t size() const { return m_len; }
526 
528  inline const char *get() const { return m_ptr; }
529 
531  inline char *get() { return m_ptr; }
532 
534  inline void resize(std::size_t len) {
535  m_len = len;
536  if (len == 0) {
537  m_buf.reset();
538  m_ptr = &m_empty;
539  } else {
540  m_buf.reset(new char[len+1]);
541  m_buf[len] = '\0';
542  m_ptr = m_buf.get();
543  }
544  }
545 
547  inline void clear() { resize(0); }
548 
549  private:
550  boost::scoped_array<char> m_buf;
551  std::size_t m_len;
552  char m_empty;
553  char *m_ptr;
554  };
555 
562  inline void prepare_headers_for_send(const bool keep_alive,
563  const bool using_chunks)
564  {
565  change_header(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
566  if (using_chunks) {
567  if (get_chunks_supported())
568  change_header(HEADER_TRANSFER_ENCODING, "chunked");
569  } else if (! m_do_not_send_content_length) {
570  change_header(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(get_content_length()));
571  }
572  }
573 
579  inline void append_headers(write_buffers_t& write_buffers) {
580  // add HTTP headers
581  for (ihash_multimap::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
582  write_buffers.push_back(boost::asio::buffer(i->first));
583  write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
584  write_buffers.push_back(boost::asio::buffer(i->second));
585  write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
586  }
587  // add an extra CRLF to end HTTP headers
588  write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
589  }
590 
592  virtual void append_cookie_headers(void) {}
593 
602  template <typename DictionaryType>
603  inline static const std::string& get_value(const DictionaryType& dict,
604  const std::string& key)
605  {
606  typename DictionaryType::const_iterator i = dict.find(key);
607  return ( (i==dict.end()) ? STRING_EMPTY : i->second );
608  }
609 
619  template <typename DictionaryType>
620  inline static void change_value(DictionaryType& dict,
621  const std::string& key, const std::string& value)
622 
623  {
624  // retrieve all current values for key
625  std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
626  result_pair = dict.equal_range(key);
627  if (result_pair.first == dict.end()) {
628  // no values exist -> add a new key
629  dict.insert(std::make_pair(key, value));
630  } else {
631  // set the first value found for the key to the new one
632  result_pair.first->second = value;
633  // remove any remaining values
634  typename DictionaryType::iterator i;
635  ++(result_pair.first);
636  while (result_pair.first != result_pair.second) {
637  i = result_pair.first;
638  ++(result_pair.first);
639  dict.erase(i);
640  }
641  }
642  }
643 
650  template <typename DictionaryType>
651  inline static void delete_value(DictionaryType& dict,
652  const std::string& key)
653  {
654  std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
655  result_pair = dict.equal_range(key);
656  if (result_pair.first != dict.end())
657  dict.erase(result_pair.first, result_pair.second);
658  }
659 
662  inline void clear_first_line(void) const {
663  if (! m_first_line.empty())
664  m_first_line.clear();
665  }
666 
668  virtual void update_first_line(void) const = 0;
669 
672  mutable std::string m_first_line;
673 
674 
675 private:
676 
678  static const boost::regex REGEX_ICASE_CHUNKED;
679 
681  bool m_is_valid;
682 
684  bool m_is_chunked;
685 
687  bool m_chunks_supported;
688 
690  bool m_do_not_send_content_length;
691 
693  boost::asio::ip::address m_remote_ip;
694 
696  boost::uint16_t m_version_major;
697 
699  boost::uint16_t m_version_minor;
700 
702  size_t m_content_length;
703 
705  content_buffer_t m_content_buf;
706 
708  chunk_cache_t m_chunk_cache;
709 
711  ihash_multimap m_headers;
712 
714  ihash_multimap m_cookie_params;
715 
717  data_status_t m_status;
718 
720  bool m_has_missing_packets;
721 
723  bool m_has_data_after_missing;
724 };
725 
726 
727 } // end namespace http
728 } // end namespace pion
729 
730 #endif
bool check_keep_alive(void) const
returns true if the HTTP connection may be kept alive
Definition: message.hpp:372
void update_content_length_using_header(void)
sets the length of the payload content using the Content-Length header
Definition: message.hpp:308
virtual ~message()
virtual destructor
Definition: message.hpp:137
std::size_t get_content_buffer_size() const
returns size of allocated buffer
Definition: message.hpp:195
message & operator=(const message &http_msg)
assignment operator
Definition: message.hpp:117
data_status_t get_status() const
return the data receival status
Definition: message.hpp:302
void change_cookie(const std::string &key, const std::string &value)
Definition: message.hpp:246
const char * get() const
returns const pointer to data
Definition: message.hpp:528
void add_header(const std::string &key, const std::string &value)
adds a value for the HTTP header named key
Definition: message.hpp:357
void add_cookie(const std::string &key, const std::string &value)
Definition: message.hpp:240
virtual void clear(void)
clears all message data
Definition: message.hpp:140
boost::asio::ip::address & get_remote_ip(void)
returns IP address of the remote endpoint
Definition: message.hpp:166
void set_is_valid(bool b=true)
sets whether or not the message is valid
Definition: message.hpp:275
const std::string & get_header(const std::string &key) const
returns a value for the header if any are defined; otherwise, an empty string
Definition: message.hpp:207
void set_version_minor(const boost::uint16_t n)
sets the minor HTTP version number
Definition: message.hpp:290
void set_version_major(const boost::uint16_t n)
sets the major HTTP version number
Definition: message.hpp:284
boost::uint16_t get_version_major(void) const
returns the major HTTP version number
Definition: message.hpp:171
void set_remote_ip(const boost::asio::ip::address &ip)
sets IP address of the remote endpoint
Definition: message.hpp:281
void clear_first_line(void) const
Definition: message.hpp:662
size_t get_content_length(void) const
returns the length of the payload content (in bytes)
Definition: message.hpp:186
void set_missing_packets(bool newVal)
set to true when missing packets detected
Definition: message.hpp:267
void append_headers(write_buffers_t &write_buffers)
Definition: message.hpp:579
void change_header(const std::string &key, const std::string &value)
changes the value for the HTTP header named key
Definition: message.hpp:362
static const std::string & get_value(const DictionaryType &dict, const std::string &key)
Definition: message.hpp:603
void set_chunks_supported(bool b)
set to true if chunked transfer encodings are supported
Definition: message.hpp:278
ihash_multimap & get_headers(void)
returns a reference to the HTTP headers
Definition: message.hpp:212
bool has_cookie(const std::string &key) const
Definition: message.hpp:234
void set_content(const std::string &content)
resets payload content to match the value of a string
Definition: message.hpp:338
void set_content_length(size_t n)
sets the length of the payload content (in bytes)
Definition: message.hpp:296
content_buffer_t & operator=(const content_buffer_t &buf)
assignment operator
Definition: message.hpp:511
void set_content_type(const std::string &type)
sets the content type for the message payload
Definition: message.hpp:352
std::vector< char > chunk_cache_t
used to cache chunked data
Definition: message.hpp:58
static void change_value(DictionaryType &dict, const std::string &key, const std::string &value)
Definition: message.hpp:620
message(const message &http_msg)
copy constructor
Definition: message.hpp:98
~content_buffer_t()
simple destructor
Definition: message.hpp:495
std::string get_version_string(void) const
returns a string representation of the HTTP version (i.e. "HTTP/1.1")
Definition: message.hpp:177
content_buffer_t()
default constructor
Definition: message.hpp:498
bool is_valid(void) const
returns true if the message is valid
Definition: message.hpp:160
a simple helper class used to manage a fixed-size payload content buffer
Definition: message.hpp:492
bool has_missing_packets() const
true if there were missing packets
Definition: message.hpp:264
void delete_header(const std::string &key)
removes all values for the HTTP header named key
Definition: message.hpp:367
message(void)
constructs a new HTTP message object
Definition: message.hpp:90
void prepare_headers_for_send(const bool keep_alive, const bool using_chunks)
Definition: message.hpp:562
std::string m_first_line
Definition: message.hpp:672
chunk_cache_t & get_chunk_cache(void)
returns a reference to the chunk cache
Definition: message.hpp:204
void resize(std::size_t len)
changes the size of the content buffer
Definition: message.hpp:534
void set_do_not_send_content_length(void)
if called, the content-length will not be sent in the HTTP headers
Definition: message.hpp:299
boost::uint16_t get_version_minor(void) const
returns the minor HTTP version number
Definition: message.hpp:174
bool is_content_buffer_allocated() const
returns true if buffer for content is allocated
Definition: message.hpp:192
const char * get_content(void) const
returns a const pointer to the payload content, or empty string if there is none
Definition: message.hpp:201
bool get_chunks_supported(void) const
returns true if chunked transfer encodings are supported
Definition: message.hpp:163
virtual void append_cookie_headers(void)
appends HTTP headers for any cookies defined by the http::message
Definition: message.hpp:592
std::size_t size() const
returns size in bytes
Definition: message.hpp:525
content_buffer_t(const content_buffer_t &buf)
copy constructor
Definition: message.hpp:501
void delete_cookie(const std::string &key)
Definition: message.hpp:252
bool is_empty() const
returns true if buffer is empty
Definition: message.hpp:522
void update_transfer_encoding_using_header(void)
sets the transfer coding using the Transfer-Encoding header
Definition: message.hpp:320
data type for library errors returned during receive() operations
Definition: message.hpp:61
char * create_content_buffer(void)
Definition: message.hpp:332
static void delete_value(DictionaryType &dict, const std::string &key)
Definition: message.hpp:651
data_status_t
defines message data integrity status codes
Definition: message.hpp:81
const std::string & get_first_line(void) const
returns a string containing the first line for the HTTP message
Definition: message.hpp:257
void clear_content(void)
clears payload content buffer
Definition: message.hpp:345
char * get_content(void)
returns a pointer to the payload content, or empty string if there is none
Definition: message.hpp:198
bool is_chunked(void) const
returns true if the message content is chunked
Definition: message.hpp:189
bool has_header(const std::string &key) const
returns true if at least one value for the header is defined
Definition: message.hpp:217
const std::string & get_cookie(const std::string &key) const
Definition: message.hpp:223
void prepare_buffers_for_send(write_buffers_t &write_buffers, const bool keep_alive, const bool using_chunks)
Definition: message.hpp:385
bool has_data_after_missing_packets() const
true if more data seen after the missing packets
Definition: message.hpp:270
void clear()
clears the content buffer
Definition: message.hpp:547
ihash_multimap & get_cookies(void)
returns the cookie parameters
Definition: message.hpp:228
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:55