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 #ifndef __PION_HTTP_MESSAGE_HEADER__ 00011 #define __PION_HTTP_MESSAGE_HEADER__ 00012 00013 #include <iosfwd> 00014 #include <vector> 00015 #include <cstring> 00016 #include <boost/cstdint.hpp> 00017 #include <boost/asio.hpp> 00018 #include <boost/scoped_array.hpp> 00019 #include <boost/lexical_cast.hpp> 00020 #include <boost/algorithm/string/trim.hpp> 00021 #include <boost/regex.hpp> 00022 #include <pion/config.hpp> 00023 #include <pion/http/types.hpp> 00024 00025 #ifndef BOOST_SYSTEM_NOEXCEPT 00026 // if 'BOOST_NOEXCEPT' is not defined, as with some older versions of 00027 // boost, setting it to nothing should be harmless. 00028 #ifndef BOOST_NOEXCEPT 00029 #define BOOST_SYSTEM_NOEXCEPT 00030 #else 00031 #define BOOST_SYSTEM_NOEXCEPT BOOST_NOEXCEPT 00032 #endif 00033 #endif 00034 00035 00036 namespace pion { // begin namespace pion 00037 00038 00039 namespace tcp { 00040 // forward declaration for class used by send() and receive() 00041 class connection; 00042 } 00043 00044 00045 namespace http { // begin namespace http 00046 00047 00048 // forward declaration of parser class 00049 class parser; 00050 00051 00055 class PION_API message 00056 : public http::types 00057 { 00058 public: 00059 00061 typedef std::vector<boost::asio::const_buffer> write_buffers_t; 00062 00064 typedef std::vector<char> chunk_cache_t; 00065 00067 struct receive_error_t 00068 : public boost::system::error_category 00069 { 00070 virtual ~receive_error_t() {} 00071 virtual inline const char *name() const BOOST_SYSTEM_NOEXCEPT { return "receive_error_t"; } 00072 virtual inline std::string message(int ev) const { 00073 std::string result; 00074 switch(ev) { 00075 case 1: 00076 result = "HTTP message parsing error"; 00077 break; 00078 default: 00079 result = "Unknown receive error"; 00080 break; 00081 } 00082 return result; 00083 } 00084 }; 00085 00087 enum data_status_t 00088 { 00089 STATUS_NONE, // no data received (i.e. all lost) 00090 STATUS_TRUNCATED, // one or more missing packets at the end 00091 STATUS_PARTIAL, // one or more missing packets but NOT at the end 00092 STATUS_OK // no missing packets 00093 }; 00094 00096 message(void) 00097 : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false), 00098 m_do_not_send_content_length(false), 00099 m_version_major(1), m_version_minor(1), m_content_length(0), m_content_buf(), 00100 m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false) 00101 {} 00102 00104 message(const message& http_msg) 00105 : m_first_line(http_msg.m_first_line), 00106 m_is_valid(http_msg.m_is_valid), 00107 m_is_chunked(http_msg.m_is_chunked), 00108 m_chunks_supported(http_msg.m_chunks_supported), 00109 m_do_not_send_content_length(http_msg.m_do_not_send_content_length), 00110 m_remote_ip(http_msg.m_remote_ip), 00111 m_version_major(http_msg.m_version_major), 00112 m_version_minor(http_msg.m_version_minor), 00113 m_content_length(http_msg.m_content_length), 00114 m_content_buf(http_msg.m_content_buf), 00115 m_chunk_cache(http_msg.m_chunk_cache), 00116 m_headers(http_msg.m_headers), 00117 m_status(http_msg.m_status), 00118 m_has_missing_packets(http_msg.m_has_missing_packets), 00119 m_has_data_after_missing(http_msg.m_has_data_after_missing) 00120 {} 00121 00123 inline message& operator=(const message& http_msg) { 00124 m_first_line = http_msg.m_first_line; 00125 m_is_valid = http_msg.m_is_valid; 00126 m_is_chunked = http_msg.m_is_chunked; 00127 m_chunks_supported = http_msg.m_chunks_supported; 00128 m_do_not_send_content_length = http_msg.m_do_not_send_content_length; 00129 m_remote_ip = http_msg.m_remote_ip; 00130 m_version_major = http_msg.m_version_major; 00131 m_version_minor = http_msg.m_version_minor; 00132 m_content_length = http_msg.m_content_length; 00133 m_content_buf = http_msg.m_content_buf; 00134 m_chunk_cache = http_msg.m_chunk_cache; 00135 m_headers = http_msg.m_headers; 00136 m_status = http_msg.m_status; 00137 m_has_missing_packets = http_msg.m_has_missing_packets; 00138 m_has_data_after_missing = http_msg.m_has_data_after_missing; 00139 return *this; 00140 } 00141 00143 virtual ~message() {} 00144 00146 virtual void clear(void) { 00147 clear_first_line(); 00148 m_is_valid = m_is_chunked = m_chunks_supported 00149 = m_do_not_send_content_length = false; 00150 m_remote_ip = boost::asio::ip::address_v4(0); 00151 m_version_major = m_version_minor = 1; 00152 m_content_length = 0; 00153 m_content_buf.clear(); 00154 m_chunk_cache.clear(); 00155 m_headers.clear(); 00156 m_cookie_params.clear(); 00157 m_status = STATUS_NONE; 00158 m_has_missing_packets = false; 00159 m_has_data_after_missing = false; 00160 } 00161 00163 virtual bool is_content_length_implied(void) const = 0; 00164 00166 inline bool is_valid(void) const { return m_is_valid; } 00167 00169 inline bool get_chunks_supported(void) const { return m_chunks_supported; } 00170 00172 inline boost::asio::ip::address& get_remote_ip(void) { 00173 return m_remote_ip; 00174 } 00175 00177 inline boost::uint16_t get_version_major(void) const { return m_version_major; } 00178 00180 inline boost::uint16_t get_version_minor(void) const { return m_version_minor; } 00181 00183 inline std::string get_version_string(void) const { 00184 std::string http_version(STRING_HTTP_VERSION); 00185 http_version += boost::lexical_cast<std::string>(get_version_major()); 00186 http_version += '.'; 00187 http_version += boost::lexical_cast<std::string>(get_version_minor()); 00188 return http_version; 00189 } 00190 00192 inline size_t get_content_length(void) const { return m_content_length; } 00193 00195 inline bool is_chunked(void) const { return m_is_chunked; } 00196 00198 bool is_content_buffer_allocated() const { return !m_content_buf.is_empty(); } 00199 00201 inline std::size_t get_content_buffer_size() const { return m_content_buf.size(); } 00202 00204 inline char *get_content(void) { return m_content_buf.get(); } 00205 00207 inline const char *get_content(void) const { return m_content_buf.get(); } 00208 00210 inline chunk_cache_t& get_chunk_cache(void) { return m_chunk_cache; } 00211 00213 inline const std::string& get_header(const std::string& key) const { 00214 return get_value(m_headers, key); 00215 } 00216 00218 inline ihash_multimap& get_headers(void) { 00219 return m_headers; 00220 } 00221 00223 inline bool has_header(const std::string& key) const { 00224 return(m_headers.find(key) != m_headers.end()); 00225 } 00226 00229 inline const std::string& get_cookie(const std::string& key) const { 00230 return get_value(m_cookie_params, key); 00231 } 00232 00234 inline ihash_multimap& get_cookies(void) { 00235 return m_cookie_params; 00236 } 00237 00240 inline bool has_cookie(const std::string& key) const { 00241 return(m_cookie_params.find(key) != m_cookie_params.end()); 00242 } 00243 00246 inline void add_cookie(const std::string& key, const std::string& value) { 00247 m_cookie_params.insert(std::make_pair(key, value)); 00248 } 00249 00252 inline void change_cookie(const std::string& key, const std::string& value) { 00253 change_value(m_cookie_params, key, value); 00254 } 00255 00258 inline void delete_cookie(const std::string& key) { 00259 delete_value(m_cookie_params, key); 00260 } 00261 00263 inline const std::string& get_first_line(void) const { 00264 if (m_first_line.empty()) 00265 update_first_line(); 00266 return m_first_line; 00267 } 00268 00270 inline bool has_missing_packets() const { return m_has_missing_packets; } 00271 00273 inline void set_missing_packets(bool newVal) { m_has_missing_packets = newVal; } 00274 00276 inline bool has_data_after_missing_packets() const { return m_has_data_after_missing; } 00277 00278 inline void set_data_after_missing_packet(bool newVal) { m_has_data_after_missing = newVal; } 00279 00281 inline void set_is_valid(bool b = true) { m_is_valid = b; } 00282 00284 inline void set_chunks_supported(bool b) { m_chunks_supported = b; } 00285 00287 inline void set_remote_ip(const boost::asio::ip::address& ip) { m_remote_ip = ip; } 00288 00290 inline void set_version_major(const boost::uint16_t n) { 00291 m_version_major = n; 00292 clear_first_line(); 00293 } 00294 00296 inline void set_version_minor(const boost::uint16_t n) { 00297 m_version_minor = n; 00298 clear_first_line(); 00299 } 00300 00302 inline void set_content_length(size_t n) { m_content_length = n; } 00303 00305 inline void set_do_not_send_content_length(void) { m_do_not_send_content_length = true; } 00306 00308 inline data_status_t get_status() const { return m_status; } 00309 00311 inline void set_status(data_status_t newVal) { m_status = newVal; } 00312 00314 inline void update_content_length_using_header(void) { 00315 ihash_multimap::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH); 00316 if (i == m_headers.end()) { 00317 m_content_length = 0; 00318 } else { 00319 std::string trimmed_length(i->second); 00320 boost::algorithm::trim(trimmed_length); 00321 m_content_length = boost::lexical_cast<size_t>(trimmed_length); 00322 } 00323 } 00324 00326 inline void update_transfer_encoding_using_header(void) { 00327 m_is_chunked = false; 00328 ihash_multimap::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING); 00329 if (i != m_headers.end()) { 00330 // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive. 00331 m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED); 00332 // ignoring other possible values for now 00333 } 00334 } 00335 00338 inline char *create_content_buffer(void) { 00339 m_content_buf.resize(m_content_length); 00340 return m_content_buf.get(); 00341 } 00342 00344 inline void set_content(const std::string& content) { 00345 set_content_length(content.size()); 00346 create_content_buffer(); 00347 memcpy(m_content_buf.get(), content.c_str(), content.size()); 00348 } 00349 00351 inline void clear_content(void) { 00352 set_content_length(0); 00353 create_content_buffer(); 00354 delete_value(m_headers, HEADER_CONTENT_TYPE); 00355 } 00356 00358 inline void set_content_type(const std::string& type) { 00359 change_value(m_headers, HEADER_CONTENT_TYPE, type); 00360 } 00361 00363 inline void add_header(const std::string& key, const std::string& value) { 00364 m_headers.insert(std::make_pair(key, value)); 00365 } 00366 00368 inline void change_header(const std::string& key, const std::string& value) { 00369 change_value(m_headers, key, value); 00370 } 00371 00373 inline void delete_header(const std::string& key) { 00374 delete_value(m_headers, key); 00375 } 00376 00378 inline bool check_keep_alive(void) const { 00379 return (get_header(HEADER_CONNECTION) != "close" 00380 && (get_version_major() > 1 00381 || (get_version_major() >= 1 && get_version_minor() >= 1)) ); 00382 } 00383 00391 inline void prepare_buffers_for_send(write_buffers_t& write_buffers, 00392 const bool keep_alive, 00393 const bool using_chunks) 00394 { 00395 // update message headers 00396 prepare_headers_for_send(keep_alive, using_chunks); 00397 // add first message line 00398 write_buffers.push_back(boost::asio::buffer(get_first_line())); 00399 write_buffers.push_back(boost::asio::buffer(STRING_CRLF)); 00400 // append cookie headers (if any) 00401 append_cookie_headers(); 00402 // append HTTP headers 00403 append_headers(write_buffers); 00404 } 00405 00406 00416 std::size_t send(tcp::connection& tcp_conn, 00417 boost::system::error_code& ec, 00418 bool headers_only = false); 00419 00429 std::size_t receive(tcp::connection& tcp_conn, 00430 boost::system::error_code& ec, 00431 parser& http_parser); 00432 00443 std::size_t receive(tcp::connection& tcp_conn, 00444 boost::system::error_code& ec, 00445 bool headers_only = false, 00446 std::size_t max_content_length = static_cast<size_t>(-1)); 00447 00457 std::size_t write(std::ostream& out, 00458 boost::system::error_code& ec, 00459 bool headers_only = false); 00460 00470 std::size_t read(std::istream& in, 00471 boost::system::error_code& ec, 00472 parser& http_parser); 00473 00484 std::size_t read(std::istream& in, 00485 boost::system::error_code& ec, 00486 bool headers_only = false, 00487 std::size_t max_content_length = static_cast<size_t>(-1)); 00488 00492 void concatenate_chunks(void); 00493 00494 00495 protected: 00496 00498 class content_buffer_t { 00499 public: 00501 ~content_buffer_t() {} 00502 00504 content_buffer_t() : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty) {} 00505 00507 content_buffer_t(const content_buffer_t& buf) 00508 : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty) 00509 { 00510 if (buf.size()) { 00511 resize(buf.size()); 00512 memcpy(get(), buf.get(), buf.size()); 00513 } 00514 } 00515 00517 content_buffer_t& operator=(const content_buffer_t& buf) { 00518 if (buf.size()) { 00519 resize(buf.size()); 00520 memcpy(get(), buf.get(), buf.size()); 00521 } else { 00522 clear(); 00523 } 00524 return *this; 00525 } 00526 00528 inline bool is_empty() const { return m_len == 0; } 00529 00531 inline std::size_t size() const { return m_len; } 00532 00534 inline const char *get() const { return m_ptr; } 00535 00537 inline char *get() { return m_ptr; } 00538 00540 inline void resize(std::size_t len) { 00541 m_len = len; 00542 if (len == 0) { 00543 m_buf.reset(); 00544 m_ptr = &m_empty; 00545 } else { 00546 m_buf.reset(new char[len+1]); 00547 m_buf[len] = '\0'; 00548 m_ptr = m_buf.get(); 00549 } 00550 } 00551 00553 inline void clear() { resize(0); } 00554 00555 private: 00556 boost::scoped_array<char> m_buf; 00557 std::size_t m_len; 00558 char m_empty; 00559 char *m_ptr; 00560 }; 00561 00568 inline void prepare_headers_for_send(const bool keep_alive, 00569 const bool using_chunks) 00570 { 00571 change_header(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") ); 00572 if (using_chunks) { 00573 if (get_chunks_supported()) 00574 change_header(HEADER_TRANSFER_ENCODING, "chunked"); 00575 } else if (! m_do_not_send_content_length) { 00576 change_header(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(get_content_length())); 00577 } 00578 } 00579 00585 inline void append_headers(write_buffers_t& write_buffers) { 00586 // add HTTP headers 00587 for (ihash_multimap::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) { 00588 write_buffers.push_back(boost::asio::buffer(i->first)); 00589 write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER)); 00590 write_buffers.push_back(boost::asio::buffer(i->second)); 00591 write_buffers.push_back(boost::asio::buffer(STRING_CRLF)); 00592 } 00593 // add an extra CRLF to end HTTP headers 00594 write_buffers.push_back(boost::asio::buffer(STRING_CRLF)); 00595 } 00596 00598 virtual void append_cookie_headers(void) {} 00599 00608 template <typename DictionaryType> 00609 inline static const std::string& get_value(const DictionaryType& dict, 00610 const std::string& key) 00611 { 00612 typename DictionaryType::const_iterator i = dict.find(key); 00613 return ( (i==dict.end()) ? STRING_EMPTY : i->second ); 00614 } 00615 00625 template <typename DictionaryType> 00626 inline static void change_value(DictionaryType& dict, 00627 const std::string& key, const std::string& value) 00628 00629 { 00630 // retrieve all current values for key 00631 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator> 00632 result_pair = dict.equal_range(key); 00633 if (result_pair.first == dict.end()) { 00634 // no values exist -> add a new key 00635 dict.insert(std::make_pair(key, value)); 00636 } else { 00637 // set the first value found for the key to the new one 00638 result_pair.first->second = value; 00639 // remove any remaining values 00640 typename DictionaryType::iterator i; 00641 ++(result_pair.first); 00642 while (result_pair.first != result_pair.second) { 00643 i = result_pair.first; 00644 ++(result_pair.first); 00645 dict.erase(i); 00646 } 00647 } 00648 } 00649 00656 template <typename DictionaryType> 00657 inline static void delete_value(DictionaryType& dict, 00658 const std::string& key) 00659 { 00660 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator> 00661 result_pair = dict.equal_range(key); 00662 if (result_pair.first != dict.end()) 00663 dict.erase(result_pair.first, result_pair.second); 00664 } 00665 00668 inline void clear_first_line(void) const { 00669 if (! m_first_line.empty()) 00670 m_first_line.clear(); 00671 } 00672 00674 virtual void update_first_line(void) const = 0; 00675 00678 mutable std::string m_first_line; 00679 00680 00681 private: 00682 00684 static const boost::regex REGEX_ICASE_CHUNKED; 00685 00687 bool m_is_valid; 00688 00690 bool m_is_chunked; 00691 00693 bool m_chunks_supported; 00694 00696 bool m_do_not_send_content_length; 00697 00699 boost::asio::ip::address m_remote_ip; 00700 00702 boost::uint16_t m_version_major; 00703 00705 boost::uint16_t m_version_minor; 00706 00708 size_t m_content_length; 00709 00711 content_buffer_t m_content_buf; 00712 00714 chunk_cache_t m_chunk_cache; 00715 00717 ihash_multimap m_headers; 00718 00720 ihash_multimap m_cookie_params; 00721 00723 data_status_t m_status; 00724 00726 bool m_has_missing_packets; 00727 00729 bool m_has_data_after_missing; 00730 }; 00731 00732 00733 } // end namespace http 00734 } // end namespace pion 00735 00736 #endif