staticlib_pion  2.0.2
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
http_message.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015, alex at staticlibs.net
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // ---------------------------------------------------------------------
18 // pion: a Boost C++ framework for building lightweight HTTP interfaces
19 // ---------------------------------------------------------------------
20 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
21 //
22 // Distributed under the Boost Software License, Version 1.0.
23 // See http://www.boost.org/LICENSE_1_0.txt
24 //
25 
26 #ifndef STATICLIB_PION_HTTP_MESSAGE_HPP
27 #define STATICLIB_PION_HTTP_MESSAGE_HPP
28 
29 #include <cstring>
30 #include <cstdint>
31 #include <iosfwd>
32 #include <memory>
33 #include <string>
34 #include <unordered_map>
35 #include <vector>
36 
37 #include "asio.hpp"
38 
39 #include "staticlib/config.hpp"
40 #include "staticlib/utils.hpp"
41 
43 
44 namespace staticlib {
45 namespace pion {
46 
47 // forward declaration for class used by send() and receive()
48 class tcp_connection;
49 // forward declaration of parser class
50 class http_parser;
51 
55 class http_message {
56 public:
57 
61 // using chunk_cache_type = std::vector<char>;
62 
71 
76 
81 
86  };
87 
91  struct receive_error_t : public asio::error_category {
95  virtual ~receive_error_t() STATICLIB_NOEXCEPT { }
96 
102  virtual const char* name() const STATICLIB_NOEXCEPT {
103  return "receive_error_t";
104  }
105 
112  virtual std::string message(int ev) const {
113  std::string result;
114  switch (ev) {
115  case 1:
116  result = "HTTP message parsing error";
117  break;
118  default:
119  result = "Unknown receive error";
120  break;
121  }
122  return result;
123  }
124  };
125 
126  // generic strings used by HTTP
127  static const std::string STRING_EMPTY;
128  static const std::string STRING_CRLF;
129  static const std::string STRING_HTTP_VERSION;
130  static const std::string HEADER_NAME_VALUE_DELIMITER;
131  static const std::string COOKIE_NAME_VALUE_DELIMITER;
132 
133  // common HTTP header names
134  static const std::string HEADER_HOST;
135  static const std::string HEADER_COOKIE;
136  static const std::string HEADER_SET_COOKIE;
137  static const std::string HEADER_CONNECTION;
138  static const std::string HEADER_CONTENT_TYPE;
139  static const std::string HEADER_CONTENT_LENGTH;
140  static const std::string HEADER_CONTENT_LOCATION;
141  static const std::string HEADER_CONTENT_ENCODING;
142  static const std::string HEADER_CONTENT_DISPOSITION;
143  static const std::string HEADER_LAST_MODIFIED;
144  static const std::string HEADER_IF_MODIFIED_SINCE;
145  static const std::string HEADER_TRANSFER_ENCODING;
146  static const std::string HEADER_LOCATION;
147  static const std::string HEADER_AUTHORIZATION;
148  static const std::string HEADER_REFERER;
149  static const std::string HEADER_USER_AGENT;
150  static const std::string HEADER_X_FORWARDED_FOR;
151  static const std::string HEADER_CLIENT_IP;
152 
153  // common HTTP content types
154  static const std::string CONTENT_TYPE_HTML;
155  static const std::string CONTENT_TYPE_TEXT;
156  static const std::string CONTENT_TYPE_XML;
157  static const std::string CONTENT_TYPE_URLENCODED;
158  static const std::string CONTENT_TYPE_MULTIPART_FORM_DATA;
159 
160  // common HTTP request methods
161  static const std::string REQUEST_METHOD_HEAD;
162  static const std::string REQUEST_METHOD_GET;
163  static const std::string REQUEST_METHOD_PUT;
164  static const std::string REQUEST_METHOD_POST;
165  static const std::string REQUEST_METHOD_DELETE;
166  static const std::string REQUEST_METHOD_OPTIONS;
167 
168  // common HTTP response messages
169  static const std::string RESPONSE_MESSAGE_OK;
170  static const std::string RESPONSE_MESSAGE_CREATED;
171  static const std::string RESPONSE_MESSAGE_ACCEPTED;
172  static const std::string RESPONSE_MESSAGE_NO_CONTENT;
173  static const std::string RESPONSE_MESSAGE_FOUND;
174  static const std::string RESPONSE_MESSAGE_UNAUTHORIZED;
175  static const std::string RESPONSE_MESSAGE_FORBIDDEN;
176  static const std::string RESPONSE_MESSAGE_NOT_FOUND;
177  static const std::string RESPONSE_MESSAGE_METHOD_NOT_ALLOWED;
178  static const std::string RESPONSE_MESSAGE_NOT_MODIFIED;
179  static const std::string RESPONSE_MESSAGE_BAD_REQUEST;
180  static const std::string RESPONSE_MESSAGE_SERVER_ERROR;
181  static const std::string RESPONSE_MESSAGE_NOT_IMPLEMENTED;
182  static const std::string RESPONSE_MESSAGE_CONTINUE;
183 
184  // common HTTP response codes
185  static const unsigned int RESPONSE_CODE_OK;
186  static const unsigned int RESPONSE_CODE_CREATED;
187  static const unsigned int RESPONSE_CODE_ACCEPTED;
188  static const unsigned int RESPONSE_CODE_NO_CONTENT;
189  static const unsigned int RESPONSE_CODE_FOUND;
190  static const unsigned int RESPONSE_CODE_UNAUTHORIZED;
191  static const unsigned int RESPONSE_CODE_FORBIDDEN;
192  static const unsigned int RESPONSE_CODE_NOT_FOUND;
193  static const unsigned int RESPONSE_CODE_METHOD_NOT_ALLOWED;
194  static const unsigned int RESPONSE_CODE_NOT_MODIFIED;
195  static const unsigned int RESPONSE_CODE_BAD_REQUEST;
196  static const unsigned int RESPONSE_CODE_SERVER_ERROR;
197  static const unsigned int RESPONSE_CODE_NOT_IMPLEMENTED;
198  static const unsigned int RESPONSE_CODE_CONTINUE;
199 
200  // response to "Expect: 100-Continue" header
201  static const std::string RESPONSE_FULLMESSAGE_100_CONTINUE;
202 
203 protected:
204 
209  mutable std::string m_first_line;
210 
215  std::unique_ptr<char[]> m_buf;
216  std::size_t m_len;
217  char m_empty;
218  char *m_ptr;
219  public:
224 
229  m_buf(),
230  m_len(0),
231  m_empty(0),
232  m_ptr(&m_empty) { }
233 
238  m_buf(),
239  m_len(0),
240  m_empty(0),
241  m_ptr(&m_empty) {
242  if (buf.size()) {
243  resize(buf.size());
244  memcpy(get(), buf.get(), buf.size());
245  }
246  }
247 
252  if (buf.size()) {
253  resize(buf.size());
254  memcpy(get(), buf.get(), buf.size());
255  } else {
256  clear();
257  }
258  return *this;
259  }
260 
264  bool is_empty() const {
265  return m_len == 0;
266  }
267 
271  std::size_t size() const {
272  return m_len;
273  }
274 
278  const char *get() const {
279  return m_ptr;
280  }
281 
285  char *get() {
286  return m_ptr;
287  }
288 
292  void resize(std::size_t len) {
293  m_len = len;
294  if (len == 0) {
295  m_buf.reset();
296  m_ptr = &m_empty;
297  } else {
298  m_buf.reset(new char[len + 1]);
299  memset(m_buf.get(), '\0', len + 1);
300  m_ptr = m_buf.get();
301  }
302  }
303 
307  void clear() {
308  resize(0);
309  }
310  };
311 
312 private:
313 
317  using headers_type = std::unordered_multimap<std::string, std::string, algorithm::ihash, algorithm::iequal_to>;
318 
322  bool m_is_valid;
323 
327  bool m_is_chunked;
328 
332  bool m_chunks_supported;
333 
337  bool m_do_not_send_content_length;
338 
342  asio::ip::address m_remote_ip;
343 
347  uint16_t m_version_major;
348 
352  uint16_t m_version_minor;
353 
357  size_t m_content_length;
358 
362  content_buffer_t m_content_buf;
363 
367  std::vector<char> m_chunk_cache;
368 
372  headers_type m_headers;
373 
377  headers_type m_cookie_params;
378 
382  data_status_type m_status;
383 
387  bool m_has_missing_packets;
388 
392  bool m_has_data_after_missing;
393 
394 public:
395 
400  m_is_valid(false),
401  m_is_chunked(false),
402  m_chunks_supported(false),
403  m_do_not_send_content_length(false),
404  m_version_major(1),
405  m_version_minor(1),
406  m_content_length(0),
407  m_content_buf(),
408  m_status(STATUS_NONE),
409  m_has_missing_packets(false),
410  m_has_data_after_missing(false) { }
411 
415  http_message(const http_message& http_msg) :
416  m_first_line(http_msg.m_first_line),
417  m_is_valid(http_msg.m_is_valid),
418  m_is_chunked(http_msg.m_is_chunked),
419  m_chunks_supported(http_msg.m_chunks_supported),
420  m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
421  m_remote_ip(http_msg.m_remote_ip),
422  m_version_major(http_msg.m_version_major),
423  m_version_minor(http_msg.m_version_minor),
424  m_content_length(http_msg.m_content_length),
425  m_content_buf(http_msg.m_content_buf),
426  m_chunk_cache(http_msg.m_chunk_cache),
427  m_headers(http_msg.m_headers),
428  m_status(http_msg.m_status),
429  m_has_missing_packets(http_msg.m_has_missing_packets),
430  m_has_data_after_missing(http_msg.m_has_data_after_missing) { }
431 
438  http_message& operator=(const http_message& http_msg) {
439  m_first_line = http_msg.m_first_line;
440  m_is_valid = http_msg.m_is_valid;
441  m_is_chunked = http_msg.m_is_chunked;
442  m_chunks_supported = http_msg.m_chunks_supported;
443  m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
444  m_remote_ip = http_msg.m_remote_ip;
445  m_version_major = http_msg.m_version_major;
446  m_version_minor = http_msg.m_version_minor;
447  m_content_length = http_msg.m_content_length;
448  m_content_buf = http_msg.m_content_buf;
449  m_chunk_cache = http_msg.m_chunk_cache;
450  m_headers = http_msg.m_headers;
451  m_status = http_msg.m_status;
452  m_has_missing_packets = http_msg.m_has_missing_packets;
453  m_has_data_after_missing = http_msg.m_has_data_after_missing;
454  return *this;
455  }
456 
460  virtual ~http_message() STATICLIB_NOEXCEPT { }
461 
465  virtual void clear() {
467  m_is_valid = m_is_chunked = m_chunks_supported
468  = m_do_not_send_content_length = false;
469  m_remote_ip = asio::ip::address_v4(0);
470  m_version_major = m_version_minor = 1;
471  m_content_length = 0;
472  m_content_buf.clear();
473  m_chunk_cache.clear();
474  m_headers.clear();
475  m_cookie_params.clear();
476  m_status = STATUS_NONE;
477  m_has_missing_packets = false;
478  m_has_data_after_missing = false;
479  }
480 
486  virtual bool is_content_length_implied(void) const = 0;
487 
491  bool is_valid() const {
492  return m_is_valid;
493  }
494 
498  bool get_chunks_supported() const {
499  return m_chunks_supported;
500  }
501 
505  const asio::ip::address& get_remote_ip() const {
506  return m_remote_ip;
507  }
508 
514  uint16_t get_version_major() const {
515  return m_version_major;
516  }
517 
521  uint16_t get_version_minor() const {
522  return m_version_minor;
523  }
524 
528  std::string get_version_string() const {
529  std::string http_version(STRING_HTTP_VERSION);
530  http_version += sl::support::to_string(get_version_major());
531  http_version += '.';
532  http_version += sl::support::to_string(get_version_minor());
533  return http_version;
534  }
535 
541  size_t get_content_length() const {
542  return m_content_length;
543  }
544 
550  bool is_chunked(void) const {
551  return m_is_chunked;
552  }
553 
558  return !m_content_buf.is_empty();
559  }
560 
564  std::size_t get_content_buffer_size() const {
565  return m_content_buf.size();
566  }
567 
571  char *get_content() {
572  return m_content_buf.get();
573  }
574 
580  const char *get_content() const {
581  return m_content_buf.get();
582  }
583 
587  std::vector<char>& get_chunk_cache() {
588  return m_chunk_cache;
589  }
590 
594  const std::string& get_header(const std::string& key) const {
595  return get_value(m_headers, key);
596  }
597 
598  bool has_header_value(const std::string& key, const std::string& value) const {
599  auto range = m_headers.equal_range(key);
600  for (auto it = range.first; it != range.second; ++it) {
601  if (std::string::npos == it->second.find(',')) {
602  if (sl::utils::iequals(value, it->second)) {
603  return true;
604  }
605  } else {
606  auto vec = sl::utils::split(it->second, ',');
607  for (auto& el : vec) {
608  auto trimmed = sl::utils::trim(el);
609  if (sl::utils::iequals(value, trimmed)) {
610  return true;
611  }
612  }
613  }
614  }
615  return false;
616  }
617 
623  headers_type& get_headers() {
624  return m_headers;
625  }
626 
633  bool has_header(const std::string& key) const {
634  return (m_headers.find(key) != m_headers.end());
635  }
636 
644  const std::string& get_cookie(const std::string& key) const {
645  return get_value(m_cookie_params, key);
646  }
647 
653  headers_type& get_cookies() {
654  return m_cookie_params;
655  }
656 
664  bool has_cookie(const std::string& key) const {
665  return (m_cookie_params.find(key) != m_cookie_params.end());
666  }
667 
675  void add_cookie(const std::string& key, const std::string& value) {
676  m_cookie_params.insert(std::make_pair(key, value));
677  }
678 
686  void change_cookie(const std::string& key, const std::string& value) {
687  change_value(m_cookie_params, key, value);
688  }
689 
696  void delete_cookie(const std::string& key) {
697  delete_value(m_cookie_params, key);
698  }
699 
705  const std::string& get_first_line() const {
706  if (m_first_line.empty()) {
708  }
709  return m_first_line;
710  }
711 
717  bool has_missing_packets() const {
718  return m_has_missing_packets;
719  }
720 
726  void set_missing_packets(bool newVal) {
727  m_has_missing_packets = newVal;
728  }
729 
736  return m_has_data_after_missing;
737  }
738 
744  void set_data_after_missing_packet(bool newVal) {
745  m_has_data_after_missing = newVal;
746  }
747 
753  void set_is_valid(bool b = true) {
754  m_is_valid = b;
755  }
756 
762  void set_chunks_supported(bool b) {
763  m_chunks_supported = b;
764  }
765 
771  void set_remote_ip(const asio::ip::address& ip) {
772  m_remote_ip = ip;
773  }
774 
780  void set_version_major(const uint16_t n) {
781  m_version_major = n;
783  }
784 
790  void set_version_minor(const uint16_t n) {
791  m_version_minor = n;
793  }
794 
800  void set_content_length(size_t n) {
801  m_content_length = n;
802  }
803 
808  m_do_not_send_content_length = true;
809  }
810 
817  return m_status;
818  }
819 
826  m_status = newVal;
827  }
828 
833  auto i = m_headers.find(HEADER_CONTENT_LENGTH);
834  if (i == m_headers.end()) {
835  m_content_length = 0;
836  } else {
837  std::string trimmed_length(i->second);
838  sl::utils::trim(trimmed_length);
839  m_content_length = static_cast<size_t>(sl::utils::parse_uint64(trimmed_length));
840  }
841  }
842 
847  m_is_chunked = false;
848  auto i = m_headers.find(HEADER_TRANSFER_ENCODING);
849  if (i != m_headers.end()) {
850  // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
851  // comparing only lower and camel variants for simplicity
852  if (std::string::npos != i->second.find("chunked") ||
853  std::string::npos != i->second.find("Chunked")) {
854  m_is_chunked = true;
855  } else {
856  m_is_chunked = false;
857  }
858  // ignoring other possible values for now
859  }
860  }
861 
869  m_content_buf.resize(m_content_length);
870  return m_content_buf.get();
871  }
872 
878  void set_content(const std::string& content) {
879  set_content_length(content.size());
881  memcpy(m_content_buf.get(), content.c_str(), content.size());
882  }
883 
887  void clear_content() {
890  delete_value(m_headers, HEADER_CONTENT_TYPE);
891  }
892 
898  void set_content_type(const std::string& type) {
899  change_value(m_headers, HEADER_CONTENT_TYPE, type);
900  }
901 
908  void add_header(const std::string& key, const std::string& value) {
909  m_headers.insert(std::make_pair(key, value));
910  }
911 
918  void change_header(const std::string& key, const std::string& value) {
919  change_value(m_headers, key, value);
920  }
921 
927  void delete_header(const std::string& key) {
928  delete_value(m_headers, key);
929  }
930 
936  bool check_keep_alive() const {
937  return (get_header(HEADER_CONNECTION) != "close"
938  && (get_version_major() > 1
939  || (get_version_major() >= 1 && get_version_minor() >= 1)));
940  }
941 
949  void prepare_buffers_for_send(std::vector<asio::const_buffer>& write_buffers, const bool keep_alive,
950  const bool using_chunks) {
951  // update message headers
952  prepare_headers_for_send(keep_alive, using_chunks);
953  // add first message line
954  write_buffers.push_back(asio::buffer(get_first_line()));
955  write_buffers.push_back(asio::buffer(STRING_CRLF));
956  // append cookie headers (if any)
958  // append HTTP headers
959  append_headers(write_buffers);
960  }
961 
966  set_content_length(m_chunk_cache.size());
967  char *post_buffer = create_content_buffer();
968  if (m_chunk_cache.size() > 0) {
969  std::memcpy(post_buffer, m_chunk_cache.data(), m_chunk_cache.size());
970  }
971  }
972 
973 protected:
974 
981  std::string make_query_string(const std::unordered_multimap<std::string, std::string,
982  algorithm::ihash, algorithm::iequal_to>& query_params) {
983  std::string query_string;
984  for (headers_type::const_iterator i = query_params.begin();
985  i != query_params.end(); ++i) {
986  if (i != query_params.begin()) {
987  query_string += '&';
988  }
989  query_string += sl::utils::url_encode(i->first);
990  query_string += '=';
991  query_string += sl::utils::url_encode(i->second);
992  }
993  return query_string;
994  }
995 
1007  std::string make_set_cookie_header(const std::string& name, const std::string& value,
1008  const std::string& path, const bool has_max_age = false, const unsigned long max_age = 0) {
1009  // note: according to RFC6265, attributes should not be quoted
1010  std::string set_cookie_header(name);
1011  set_cookie_header += "=\"";
1012  set_cookie_header += value;
1013  set_cookie_header += "\"; Version=1";
1014  if (!path.empty()) {
1015  set_cookie_header += "; Path=";
1016  set_cookie_header += path;
1017  }
1018  if (has_max_age) {
1019  set_cookie_header += "; Max-Age=";
1020  set_cookie_header += sl::support::to_string(max_age);
1021  }
1022  return set_cookie_header;
1023  }
1024 
1031  void prepare_headers_for_send(const bool keep_alive, const bool using_chunks) {
1032  change_header(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close"));
1033  if (using_chunks) {
1034  if (get_chunks_supported()) {
1036  }
1037  } else if (!m_do_not_send_content_length) {
1038  change_header(HEADER_CONTENT_LENGTH, sl::support::to_string(get_content_length()));
1039  }
1040  }
1041 
1047  void append_headers(std::vector<asio::const_buffer>& write_buffers) {
1048  // add HTTP headers
1049  for (headers_type::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
1050  write_buffers.push_back(asio::buffer(i->first));
1051  write_buffers.push_back(asio::buffer(HEADER_NAME_VALUE_DELIMITER));
1052  write_buffers.push_back(asio::buffer(i->second));
1053  write_buffers.push_back(asio::buffer(STRING_CRLF));
1054  }
1055  // add an extra CRLF to end HTTP headers
1056  write_buffers.push_back(asio::buffer(STRING_CRLF));
1057  }
1058 
1063 
1072  static const std::string& get_value(const headers_type& dict, const std::string& key) {
1073  auto i = dict.find(key);
1074  return ( (i==dict.end()) ? STRING_EMPTY : i->second );
1075  }
1076 
1086  inline static void change_value(headers_type& dict, const std::string& key,
1087  const std::string& value) {
1088  // retrieve all current values for key
1089  auto result_pair = dict.equal_range(key);
1090  if (result_pair.first == dict.end()) {
1091  // no values exist -> add a new key
1092  dict.insert(std::make_pair(key, value));
1093  } else {
1094  // set the first value found for the key to the new one
1095  result_pair.first->second = value;
1096  // remove any remaining values
1097  headers_type::iterator i;
1098  ++(result_pair.first);
1099  while (result_pair.first != result_pair.second) {
1100  i = result_pair.first;
1101  ++(result_pair.first);
1102  dict.erase(i);
1103  }
1104  }
1105  }
1106 
1113  inline static void delete_value(headers_type& dict, const std::string& key) {
1114  auto result_pair = dict.equal_range(key);
1115  if (result_pair.first != dict.end()) {
1116  dict.erase(result_pair.first, result_pair.second);
1117  }
1118  }
1119 
1124  void clear_first_line() const {
1125  if (!m_first_line.empty()) {
1126  m_first_line.clear();
1127  }
1128  }
1129 
1133  virtual void update_first_line() const = 0;
1134 
1135 };
1136 
1137 
1138 } // namespace
1139 }
1140 
1141 #endif // STATICLIB_PION_HTTP_MESSAGE_HPP