00001
00002
00003
00004
00005
00006
00007
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 #define BOOST_SYSTEM_NOEXCEPT BOOST_NOEXCEPT
00027 #endif
00028
00029
00030 namespace pion {
00031
00032
00033 namespace tcp {
00034
00035 class connection;
00036 }
00037
00038
00039 namespace http {
00040
00041
00042
00043 class parser;
00044
00045
00049 class PION_API message
00050 : public http::types
00051 {
00052 public:
00053
00055 typedef std::vector<boost::asio::const_buffer> write_buffers_t;
00056
00058 typedef std::vector<char> chunk_cache_t;
00059
00061 struct receive_error_t
00062 : public boost::system::error_category
00063 {
00064 virtual ~receive_error_t() {}
00065 virtual inline const char *name() const BOOST_SYSTEM_NOEXCEPT { return "receive_error_t"; }
00066 virtual inline std::string message(int ev) const {
00067 std::string result;
00068 switch(ev) {
00069 case 1:
00070 result = "HTTP message parsing error";
00071 break;
00072 default:
00073 result = "Unknown receive error";
00074 break;
00075 }
00076 return result;
00077 }
00078 };
00079
00081 enum data_status_t
00082 {
00083 STATUS_NONE,
00084 STATUS_TRUNCATED,
00085 STATUS_PARTIAL,
00086 STATUS_OK
00087 };
00088
00090 message(void)
00091 : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
00092 m_do_not_send_content_length(false),
00093 m_version_major(1), m_version_minor(1), m_content_length(0), m_content_buf(),
00094 m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false)
00095 {}
00096
00098 message(const message& http_msg)
00099 : m_first_line(http_msg.m_first_line),
00100 m_is_valid(http_msg.m_is_valid),
00101 m_is_chunked(http_msg.m_is_chunked),
00102 m_chunks_supported(http_msg.m_chunks_supported),
00103 m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
00104 m_remote_ip(http_msg.m_remote_ip),
00105 m_version_major(http_msg.m_version_major),
00106 m_version_minor(http_msg.m_version_minor),
00107 m_content_length(http_msg.m_content_length),
00108 m_content_buf(http_msg.m_content_buf),
00109 m_chunk_cache(http_msg.m_chunk_cache),
00110 m_headers(http_msg.m_headers),
00111 m_status(http_msg.m_status),
00112 m_has_missing_packets(http_msg.m_has_missing_packets),
00113 m_has_data_after_missing(http_msg.m_has_data_after_missing)
00114 {}
00115
00117 inline message& operator=(const message& http_msg) {
00118 m_first_line = http_msg.m_first_line;
00119 m_is_valid = http_msg.m_is_valid;
00120 m_is_chunked = http_msg.m_is_chunked;
00121 m_chunks_supported = http_msg.m_chunks_supported;
00122 m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
00123 m_remote_ip = http_msg.m_remote_ip;
00124 m_version_major = http_msg.m_version_major;
00125 m_version_minor = http_msg.m_version_minor;
00126 m_content_length = http_msg.m_content_length;
00127 m_content_buf = http_msg.m_content_buf;
00128 m_chunk_cache = http_msg.m_chunk_cache;
00129 m_headers = http_msg.m_headers;
00130 m_status = http_msg.m_status;
00131 m_has_missing_packets = http_msg.m_has_missing_packets;
00132 m_has_data_after_missing = http_msg.m_has_data_after_missing;
00133 return *this;
00134 }
00135
00137 virtual ~message() {}
00138
00140 virtual void clear(void) {
00141 clear_first_line();
00142 m_is_valid = m_is_chunked = m_chunks_supported
00143 = m_do_not_send_content_length = false;
00144 m_remote_ip = boost::asio::ip::address_v4(0);
00145 m_version_major = m_version_minor = 1;
00146 m_content_length = 0;
00147 m_content_buf.clear();
00148 m_chunk_cache.clear();
00149 m_headers.clear();
00150 m_cookie_params.clear();
00151 m_status = STATUS_NONE;
00152 m_has_missing_packets = false;
00153 m_has_data_after_missing = false;
00154 }
00155
00157 virtual bool is_content_length_implied(void) const = 0;
00158
00160 inline bool is_valid(void) const { return m_is_valid; }
00161
00163 inline bool get_chunks_supported(void) const { return m_chunks_supported; }
00164
00166 inline boost::asio::ip::address& get_remote_ip(void) {
00167 return m_remote_ip;
00168 }
00169
00171 inline boost::uint16_t get_version_major(void) const { return m_version_major; }
00172
00174 inline boost::uint16_t get_version_minor(void) const { return m_version_minor; }
00175
00177 inline std::string get_version_string(void) const {
00178 std::string http_version(STRING_HTTP_VERSION);
00179 http_version += boost::lexical_cast<std::string>(get_version_major());
00180 http_version += '.';
00181 http_version += boost::lexical_cast<std::string>(get_version_minor());
00182 return http_version;
00183 }
00184
00186 inline boost::uint64_t get_content_length(void) const { return m_content_length; }
00187
00189 inline bool is_chunked(void) const { return m_is_chunked; }
00190
00192 bool is_content_buffer_allocated() const { return !m_content_buf.is_empty(); }
00193
00195 inline std::size_t get_content_buffer_size() const { return m_content_buf.size(); }
00196
00198 inline char *get_content(void) { return m_content_buf.get(); }
00199
00201 inline const char *get_content(void) const { return m_content_buf.get(); }
00202
00204 inline chunk_cache_t& get_chunk_cache(void) { return m_chunk_cache; }
00205
00207 inline const std::string& get_header(const std::string& key) const {
00208 return get_value(m_headers, key);
00209 }
00210
00212 inline ihash_multimap& get_headers(void) {
00213 return m_headers;
00214 }
00215
00217 inline bool has_header(const std::string& key) const {
00218 return(m_headers.find(key) != m_headers.end());
00219 }
00220
00223 inline const std::string& get_cookie(const std::string& key) const {
00224 return get_value(m_cookie_params, key);
00225 }
00226
00228 inline ihash_multimap& get_cookies(void) {
00229 return m_cookie_params;
00230 }
00231
00234 inline bool has_cookie(const std::string& key) const {
00235 return(m_cookie_params.find(key) != m_cookie_params.end());
00236 }
00237
00240 inline void add_cookie(const std::string& key, const std::string& value) {
00241 m_cookie_params.insert(std::make_pair(key, value));
00242 }
00243
00246 inline void change_cookie(const std::string& key, const std::string& value) {
00247 change_value(m_cookie_params, key, value);
00248 }
00249
00252 inline void delete_cookie(const std::string& key) {
00253 delete_value(m_cookie_params, key);
00254 }
00255
00257 inline const std::string& get_first_line(void) const {
00258 if (m_first_line.empty())
00259 update_first_line();
00260 return m_first_line;
00261 }
00262
00264 inline bool has_missing_packets() const { return m_has_missing_packets; }
00265
00267 inline void set_missing_packets(bool newVal) { m_has_missing_packets = newVal; }
00268
00270 inline bool has_data_after_missing_packets() const { return m_has_data_after_missing; }
00271
00272 inline void set_data_after_missing_packet(bool newVal) { m_has_data_after_missing = newVal; }
00273
00275 inline void set_is_valid(bool b = true) { m_is_valid = b; }
00276
00278 inline void set_chunks_supported(bool b) { m_chunks_supported = b; }
00279
00281 inline void set_remote_ip(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
00282
00284 inline void set_version_major(const boost::uint16_t n) {
00285 m_version_major = n;
00286 clear_first_line();
00287 }
00288
00290 inline void set_version_minor(const boost::uint16_t n) {
00291 m_version_minor = n;
00292 clear_first_line();
00293 }
00294
00296 inline void set_content_length(const boost::uint64_t n) { m_content_length = n; }
00297
00299 inline void set_do_not_send_content_length(void) { m_do_not_send_content_length = true; }
00300
00302 inline data_status_t get_status() const { return m_status; }
00303
00305 inline void set_status(data_status_t newVal) { m_status = newVal; }
00306
00308 inline void update_content_length_using_header(void) {
00309 ihash_multimap::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
00310 if (i == m_headers.end()) {
00311 m_content_length = 0;
00312 } else {
00313 std::string trimmed_length(i->second);
00314 boost::algorithm::trim(trimmed_length);
00315 m_content_length = boost::lexical_cast<boost::uint64_t>(trimmed_length);
00316 }
00317 }
00318
00320 inline void update_transfer_encoding_using_header(void) {
00321 m_is_chunked = false;
00322 ihash_multimap::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
00323 if (i != m_headers.end()) {
00324
00325 m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
00326
00327 }
00328 }
00329
00332 inline char *create_content_buffer(void) {
00333 m_content_buf.resize(m_content_length);
00334 return m_content_buf.get();
00335 }
00336
00338 inline void set_content(const std::string& content) {
00339 set_content_length(content.size());
00340 create_content_buffer();
00341 memcpy(m_content_buf.get(), content.c_str(), content.size());
00342 }
00343
00345 inline void clear_content(void) {
00346 set_content_length(0);
00347 create_content_buffer();
00348 delete_value(m_headers, HEADER_CONTENT_TYPE);
00349 }
00350
00352 inline void set_content_type(const std::string& type) {
00353 change_value(m_headers, HEADER_CONTENT_TYPE, type);
00354 }
00355
00357 inline void add_header(const std::string& key, const std::string& value) {
00358 m_headers.insert(std::make_pair(key, value));
00359 }
00360
00362 inline void change_header(const std::string& key, const std::string& value) {
00363 change_value(m_headers, key, value);
00364 }
00365
00367 inline void delete_header(const std::string& key) {
00368 delete_value(m_headers, key);
00369 }
00370
00372 inline bool check_keep_alive(void) const {
00373 return (get_header(HEADER_CONNECTION) != "close"
00374 && (get_version_major() > 1
00375 || (get_version_major() >= 1 && get_version_minor() >= 1)) );
00376 }
00377
00385 inline void prepare_buffers_for_send(write_buffers_t& write_buffers,
00386 const bool keep_alive,
00387 const bool using_chunks)
00388 {
00389
00390 prepare_headers_for_send(keep_alive, using_chunks);
00391
00392 write_buffers.push_back(boost::asio::buffer(get_first_line()));
00393 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00394
00395 append_cookie_headers();
00396
00397 append_headers(write_buffers);
00398 }
00399
00400
00410 std::size_t send(tcp::connection& tcp_conn,
00411 boost::system::error_code& ec,
00412 bool headers_only = false);
00413
00423 std::size_t receive(tcp::connection& tcp_conn,
00424 boost::system::error_code& ec,
00425 parser& http_parser);
00426
00437 std::size_t receive(tcp::connection& tcp_conn,
00438 boost::system::error_code& ec,
00439 bool headers_only = false,
00440 std::size_t max_content_length = static_cast<size_t>(-1));
00441
00451 std::size_t write(std::ostream& out,
00452 boost::system::error_code& ec,
00453 bool headers_only = false);
00454
00464 std::size_t read(std::istream& in,
00465 boost::system::error_code& ec,
00466 parser& http_parser);
00467
00478 std::size_t read(std::istream& in,
00479 boost::system::error_code& ec,
00480 bool headers_only = false,
00481 std::size_t max_content_length = static_cast<size_t>(-1));
00482
00486 void concatenate_chunks(void);
00487
00488
00489 protected:
00490
00492 class content_buffer_t {
00493 public:
00495 ~content_buffer_t() {}
00496
00498 content_buffer_t() : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty) {}
00499
00501 content_buffer_t(const content_buffer_t& buf)
00502 : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty)
00503 {
00504 if (buf.size()) {
00505 resize(buf.size());
00506 memcpy(get(), buf.get(), buf.size());
00507 }
00508 }
00509
00511 content_buffer_t& operator=(const content_buffer_t& buf) {
00512 if (buf.size()) {
00513 resize(buf.size());
00514 memcpy(get(), buf.get(), buf.size());
00515 } else {
00516 clear();
00517 }
00518 return *this;
00519 }
00520
00522 inline bool is_empty() const { return m_len == 0; }
00523
00525 inline std::size_t size() const { return m_len; }
00526
00528 inline const char *get() const { return m_ptr; }
00529
00531 inline char *get() { return m_ptr; }
00532
00534 inline void resize(std::size_t len) {
00535 m_len = len;
00536 if (len == 0) {
00537 m_buf.reset();
00538 m_ptr = &m_empty;
00539 } else {
00540 m_buf.reset(new char[len+1]);
00541 m_buf[len] = '\0';
00542 m_ptr = m_buf.get();
00543 }
00544 }
00545
00547 inline void clear() { resize(0); }
00548
00549 private:
00550 boost::scoped_array<char> m_buf;
00551 std::size_t m_len;
00552 char m_empty;
00553 char *m_ptr;
00554 };
00555
00562 inline void prepare_headers_for_send(const bool keep_alive,
00563 const bool using_chunks)
00564 {
00565 change_header(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
00566 if (using_chunks) {
00567 if (get_chunks_supported())
00568 change_header(HEADER_TRANSFER_ENCODING, "chunked");
00569 } else if (! m_do_not_send_content_length) {
00570 change_header(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(get_content_length()));
00571 }
00572 }
00573
00579 inline void append_headers(write_buffers_t& write_buffers) {
00580
00581 for (ihash_multimap::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
00582 write_buffers.push_back(boost::asio::buffer(i->first));
00583 write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
00584 write_buffers.push_back(boost::asio::buffer(i->second));
00585 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00586 }
00587
00588 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00589 }
00590
00592 virtual void append_cookie_headers(void) {}
00593
00602 template <typename DictionaryType>
00603 inline static const std::string& get_value(const DictionaryType& dict,
00604 const std::string& key)
00605 {
00606 typename DictionaryType::const_iterator i = dict.find(key);
00607 return ( (i==dict.end()) ? STRING_EMPTY : i->second );
00608 }
00609
00619 template <typename DictionaryType>
00620 inline static void change_value(DictionaryType& dict,
00621 const std::string& key, const std::string& value)
00622
00623 {
00624
00625 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00626 result_pair = dict.equal_range(key);
00627 if (result_pair.first == dict.end()) {
00628
00629 dict.insert(std::make_pair(key, value));
00630 } else {
00631
00632 result_pair.first->second = value;
00633
00634 typename DictionaryType::iterator i;
00635 ++(result_pair.first);
00636 while (result_pair.first != result_pair.second) {
00637 i = result_pair.first;
00638 ++(result_pair.first);
00639 dict.erase(i);
00640 }
00641 }
00642 }
00643
00650 template <typename DictionaryType>
00651 inline static void delete_value(DictionaryType& dict,
00652 const std::string& key)
00653 {
00654 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00655 result_pair = dict.equal_range(key);
00656 if (result_pair.first != dict.end())
00657 dict.erase(result_pair.first, result_pair.second);
00658 }
00659
00662 inline void clear_first_line(void) const {
00663 if (! m_first_line.empty())
00664 m_first_line.clear();
00665 }
00666
00668 virtual void update_first_line(void) const = 0;
00669
00672 mutable std::string m_first_line;
00673
00674
00675 private:
00676
00678 static const boost::regex REGEX_ICASE_CHUNKED;
00679
00681 bool m_is_valid;
00682
00684 bool m_is_chunked;
00685
00687 bool m_chunks_supported;
00688
00690 bool m_do_not_send_content_length;
00691
00693 boost::asio::ip::address m_remote_ip;
00694
00696 boost::uint16_t m_version_major;
00697
00699 boost::uint16_t m_version_minor;
00700
00702 boost::uint64_t m_content_length;
00703
00705 content_buffer_t m_content_buf;
00706
00708 chunk_cache_t m_chunk_cache;
00709
00711 ihash_multimap m_headers;
00712
00714 ihash_multimap m_cookie_params;
00715
00717 data_status_t m_status;
00718
00720 bool m_has_missing_packets;
00721
00723 bool m_has_data_after_missing;
00724 };
00725
00726
00727 }
00728 }
00729
00730 #endif