pion-net  4.0.9
HTTPMessage.hpp
1 // ------------------------------------------------------------------
2 // pion-net: a C++ framework for building lightweight HTTP interfaces
3 // ------------------------------------------------------------------
4 // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com)
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_HTTPMESSAGE_HEADER__
11 #define __PION_HTTPMESSAGE_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/PionConfig.hpp>
23 #include <pion/net/HTTPTypes.hpp>
24 
25 
26 namespace pion { // begin namespace pion
27 namespace net { // begin namespace net (Pion Network Library)
28 
29 
30 // forward declaration for class used by send() and receive()
31 class TCPConnection;
32 
33 
37 class PION_NET_API HTTPMessage
38  : public HTTPTypes
39 {
40 public:
41 
43  typedef std::vector<boost::asio::const_buffer> WriteBuffers;
44 
46  typedef std::vector<char> ChunkCache;
47 
49  struct ReceiveError
50  : public boost::system::error_category
51  {
52  virtual ~ReceiveError() {}
53  virtual inline const char *name() const { return "ReceiveError"; }
54  virtual inline std::string message(int ev) const {
55  std::string result;
56  switch(ev) {
57  case 1:
58  result = "HTTP message parsing error";
59  break;
60  default:
61  result = "Unknown receive error";
62  break;
63  }
64  return result;
65  }
66  };
67 
70  {
71  STATUS_NONE, // no data received (i.e. all lost)
72  STATUS_TRUNCATED, // one or more missing packets at the end
73  STATUS_PARTIAL, // one or more missing packets but NOT at the end
74  STATUS_OK // no missing packets
75  };
76 
79  : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
80  m_do_not_send_content_length(false),
81  m_version_major(1), m_version_minor(1), m_content_length(0),
82  m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false)
83  {}
84 
86  HTTPMessage(const HTTPMessage& http_msg)
87  : m_first_line(http_msg.m_first_line),
88  m_is_valid(http_msg.m_is_valid),
89  m_is_chunked(http_msg.m_is_chunked),
90  m_chunks_supported(http_msg.m_chunks_supported),
91  m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
92  m_remote_ip(http_msg.m_remote_ip),
93  m_version_major(http_msg.m_version_major),
94  m_version_minor(http_msg.m_version_minor),
95  m_content_length(http_msg.m_content_length),
96  m_chunk_cache(http_msg.m_chunk_cache),
97  m_headers(http_msg.m_headers),
98  m_status(http_msg.m_status),
99  m_has_missing_packets(http_msg.m_has_missing_packets),
100  m_has_data_after_missing(http_msg.m_has_data_after_missing)
101  {
102  if (http_msg.m_content_buf) {
103  char *ptr = createContentBuffer();
104  memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
105  }
106  }
107 
109  inline HTTPMessage& operator=(const HTTPMessage& http_msg) {
110  m_first_line = http_msg.m_first_line;
111  m_is_valid = http_msg.m_is_valid;
112  m_is_chunked = http_msg.m_is_chunked;
113  m_chunks_supported = http_msg.m_chunks_supported;
114  m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
115  m_remote_ip = http_msg.m_remote_ip;
116  m_version_major = http_msg.m_version_major;
117  m_version_minor = http_msg.m_version_minor;
118  m_content_length = http_msg.m_content_length;
119  m_chunk_cache = http_msg.m_chunk_cache;
120  m_headers = http_msg.m_headers;
121  m_status = http_msg.m_status;
122  m_has_missing_packets = http_msg.m_has_missing_packets;
123  m_has_data_after_missing = http_msg.m_has_data_after_missing;
124  if (http_msg.m_content_buf) {
125  char *ptr = createContentBuffer();
126  memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
127  }
128  return *this;
129  }
130 
132  virtual ~HTTPMessage() {}
133 
135  virtual void clear(void) {
136  clearFirstLine();
137  m_is_valid = m_is_chunked = m_chunks_supported
138  = m_do_not_send_content_length = false;
139  m_remote_ip = boost::asio::ip::address_v4(0);
140  m_version_major = m_version_minor = 1;
141  m_content_length = 0;
142  m_content_buf.reset();
143  m_chunk_cache.clear();
144  m_headers.clear();
145  m_cookie_params.clear();
146  m_status = STATUS_NONE;
147  m_has_missing_packets = false;
148  m_has_data_after_missing = false;
149  }
150 
152  virtual bool isContentLengthImplied(void) const = 0;
153 
155  inline bool isValid(void) const { return m_is_valid; }
156 
158  inline bool getChunksSupported(void) const { return m_chunks_supported; }
159 
161  inline boost::asio::ip::address& getRemoteIp(void) {
162  return m_remote_ip;
163  }
164 
166  inline boost::uint16_t getVersionMajor(void) const { return m_version_major; }
167 
169  inline boost::uint16_t getVersionMinor(void) const { return m_version_minor; }
170 
172  inline std::string getVersionString(void) const {
173  std::string http_version(STRING_HTTP_VERSION);
174  http_version += boost::lexical_cast<std::string>(getVersionMajor());
175  http_version += '.';
176  http_version += boost::lexical_cast<std::string>(getVersionMinor());
177  return http_version;
178  }
179 
181  inline std::size_t getContentLength(void) const { return m_content_length; }
182 
184  inline bool isChunked(void) const { return m_is_chunked; }
185 
187  inline char *getContent(void) { return m_content_buf.get(); }
188 
190  inline const char *getContent(void) const { return m_content_buf.get(); }
191 
193  inline ChunkCache& getChunkCache(void) { return m_chunk_cache; }
194 
196  inline const std::string& getHeader(const std::string& key) const {
197  return getValue(m_headers, key);
198  }
199 
201  inline Headers& getHeaders(void) {
202  return m_headers;
203  }
204 
206  inline bool hasHeader(const std::string& key) const {
207  return(m_headers.find(key) != m_headers.end());
208  }
209 
212  inline const std::string& getCookie(const std::string& key) const {
213  return getValue(m_cookie_params, key);
214  }
215 
218  return m_cookie_params;
219  }
220 
223  inline bool hasCookie(const std::string& key) const {
224  return(m_cookie_params.find(key) != m_cookie_params.end());
225  }
226 
229  inline void addCookie(const std::string& key, const std::string& value) {
230  m_cookie_params.insert(std::make_pair(key, value));
231  }
232 
235  inline void changeCookie(const std::string& key, const std::string& value) {
236  changeValue(m_cookie_params, key, value);
237  }
238 
241  inline void deleteCookie(const std::string& key) {
242  deleteValue(m_cookie_params, key);
243  }
244 
246  inline const std::string& getFirstLine(void) const {
247  if (m_first_line.empty())
248  updateFirstLine();
249  return m_first_line;
250  }
251 
253  inline bool hasMissingPackets() const { return m_has_missing_packets; }
254 
256  inline void setMissingPackets(bool newVal) { m_has_missing_packets = newVal; }
257 
259  inline bool hasDataAfterMissingPackets() const { return m_has_data_after_missing; }
260 
261  inline void setDataAfterMissingPacket(bool newVal) { m_has_data_after_missing = newVal; }
262 
264  inline void setIsValid(bool b = true) { m_is_valid = b; }
265 
267  inline void setChunksSupported(bool b) { m_chunks_supported = b; }
268 
270  inline void setRemoteIp(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
271 
273  inline void setVersionMajor(const boost::uint16_t n) {
274  m_version_major = n;
275  clearFirstLine();
276  }
277 
279  inline void setVersionMinor(const boost::uint16_t n) {
280  m_version_minor = n;
281  clearFirstLine();
282  }
283 
285  inline void setContentLength(const std::size_t n) { m_content_length = n; }
286 
288  inline void setDoNotSendContentLength(void) { m_do_not_send_content_length = true; }
289 
291  inline DataStatus getStatus() const { return m_status; }
292 
294  inline void setStatus(DataStatus newVal) { m_status = newVal; }
295 
297  inline void updateContentLengthUsingHeader(void) {
298  Headers::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
299  if (i == m_headers.end()) {
300  m_content_length = 0;
301  } else {
302  std::string trimmed_length(i->second);
303  boost::algorithm::trim(trimmed_length);
304  m_content_length = boost::lexical_cast<std::size_t>(trimmed_length);
305  }
306  }
307 
310  m_is_chunked = false;
311  Headers::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
312  if (i != m_headers.end()) {
313  // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
314  m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
315  // ignoring other possible values for now
316  }
317  }
318 
321  inline char *createContentBuffer(void) {
322  m_content_buf.reset(new char[m_content_length + 1]);
323  m_content_buf[m_content_length] = '\0';
324  return m_content_buf.get();
325  }
326 
328  inline void setContent(const std::string& content) {
329  setContentLength(content.size());
330  createContentBuffer();
331  memcpy(m_content_buf.get(), content.c_str(), content.size());
332  }
333 
335  inline void clearContent(void) {
336  setContentLength(0);
337  createContentBuffer();
338  deleteValue(m_headers, HEADER_CONTENT_TYPE);
339  }
340 
342  inline void setContentType(const std::string& type) {
343  changeValue(m_headers, HEADER_CONTENT_TYPE, type);
344  }
345 
347  inline void addHeader(const std::string& key, const std::string& value) {
348  m_headers.insert(std::make_pair(key, value));
349  }
350 
352  inline void changeHeader(const std::string& key, const std::string& value) {
353  changeValue(m_headers, key, value);
354  }
355 
357  inline void deleteHeader(const std::string& key) {
358  deleteValue(m_headers, key);
359  }
360 
362  inline bool checkKeepAlive(void) const {
363  return (getHeader(HEADER_CONNECTION) != "close"
364  && (getVersionMajor() > 1
365  || (getVersionMajor() >= 1 && getVersionMinor() >= 1)) );
366  }
367 
375  inline void prepareBuffersForSend(WriteBuffers& write_buffers,
376  const bool keep_alive,
377  const bool using_chunks)
378  {
379  // update message headers
380  prepareHeadersForSend(keep_alive, using_chunks);
381  // add first message line
382  write_buffers.push_back(boost::asio::buffer(getFirstLine()));
383  write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
384  // append HTTP headers
385  appendHeaders(write_buffers);
386  }
387 
388 
398  std::size_t send(TCPConnection& tcp_conn, boost::system::error_code& ec,
399  bool headers_only = false);
400 
410  std::size_t receive(TCPConnection& tcp_conn, boost::system::error_code& ec,
411  bool headers_only = false);
412 
422  std::size_t write(std::ostream& out, boost::system::error_code& ec,
423  bool headers_only = false);
424 
434  std::size_t read(std::istream& in, boost::system::error_code& ec,
435  bool headers_only = false);
436 
440  void concatenateChunks(void);
441 
442 
443 protected:
444 
451  inline void prepareHeadersForSend(const bool keep_alive,
452  const bool using_chunks)
453  {
454  changeHeader(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
455  if (using_chunks) {
456  if (getChunksSupported())
457  changeHeader(HEADER_TRANSFER_ENCODING, "chunked");
458  } else if (! m_do_not_send_content_length) {
459  changeHeader(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(getContentLength()));
460  }
461  }
462 
468  inline void appendHeaders(WriteBuffers& write_buffers) {
469  // add HTTP headers
470  for (Headers::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
471  write_buffers.push_back(boost::asio::buffer(i->first));
472  write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
473  write_buffers.push_back(boost::asio::buffer(i->second));
474  write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
475  }
476  // add an extra CRLF to end HTTP headers
477  write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
478  }
479 
488  template <typename DictionaryType>
489  inline static const std::string& getValue(const DictionaryType& dict,
490  const std::string& key)
491  {
492  typename DictionaryType::const_iterator i = dict.find(key);
493  return ( (i==dict.end()) ? STRING_EMPTY : i->second );
494  }
495 
505  template <typename DictionaryType>
506  inline static void changeValue(DictionaryType& dict,
507  const std::string& key, const std::string& value)
508 
509  {
510  // retrieve all current values for key
511  std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
512  result_pair = dict.equal_range(key);
513  if (result_pair.first == dict.end()) {
514  // no values exist -> add a new key
515  dict.insert(std::make_pair(key, value));
516  } else {
517  // set the first value found for the key to the new one
518  result_pair.first->second = value;
519  // remove any remaining values
520  typename DictionaryType::iterator i;
521  ++(result_pair.first);
522  while (result_pair.first != result_pair.second) {
523  i = result_pair.first;
524  ++(result_pair.first);
525  dict.erase(i);
526  }
527  }
528  }
529 
536  template <typename DictionaryType>
537  inline static void deleteValue(DictionaryType& dict,
538  const std::string& key)
539  {
540  std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
541  result_pair = dict.equal_range(key);
542  if (result_pair.first != dict.end())
543  dict.erase(result_pair.first, result_pair.second);
544  }
545 
548  inline void clearFirstLine(void) const {
549  if (! m_first_line.empty())
550  m_first_line.clear();
551  }
552 
554  virtual void updateFirstLine(void) const = 0;
555 
556 
559  mutable std::string m_first_line;
560 
561 
562 private:
563 
565  static const boost::regex REGEX_ICASE_CHUNKED;
566 
568  bool m_is_valid;
569 
571  bool m_is_chunked;
572 
574  bool m_chunks_supported;
575 
577  bool m_do_not_send_content_length;
578 
580  boost::asio::ip::address m_remote_ip;
581 
583  boost::uint16_t m_version_major;
584 
586  boost::uint16_t m_version_minor;
587 
589  std::size_t m_content_length;
590 
592  boost::scoped_array<char> m_content_buf;
593 
595  ChunkCache m_chunk_cache;
596 
598  Headers m_headers;
599 
601  CookieParams m_cookie_params;
602 
604  DataStatus m_status;
605 
607  bool m_has_missing_packets;
608 
610  bool m_has_data_after_missing;
611 };
612 
613 
614 } // end namespace net
615 } // end namespace pion
616 
617 #endif