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
00026 namespace pion {
00027
00028
00029 namespace tcp {
00030
00031 class connection;
00032 }
00033
00034
00035 namespace http {
00036
00037
00038
00039 class parser;
00040
00041
00045 class PION_API message
00046 : public http::types
00047 {
00048 public:
00049
00051 typedef std::vector<boost::asio::const_buffer> write_buffers_t;
00052
00054 typedef std::vector<char> chunk_cache_t;
00055
00057 struct receive_error_t
00058 : public boost::system::error_category
00059 {
00060 virtual ~receive_error_t() {}
00061 virtual inline const char *name() const { return "receive_error_t"; }
00062 virtual inline std::string message(int ev) const {
00063 std::string result;
00064 switch(ev) {
00065 case 1:
00066 result = "HTTP message parsing error";
00067 break;
00068 default:
00069 result = "Unknown receive error";
00070 break;
00071 }
00072 return result;
00073 }
00074 };
00075
00077 enum data_status_t
00078 {
00079 STATUS_NONE,
00080 STATUS_TRUNCATED,
00081 STATUS_PARTIAL,
00082 STATUS_OK
00083 };
00084
00086 message(void)
00087 : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
00088 m_do_not_send_content_length(false),
00089 m_version_major(1), m_version_minor(1), m_content_length(0), m_content_buf(),
00090 m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false)
00091 {}
00092
00094 message(const message& http_msg)
00095 : m_first_line(http_msg.m_first_line),
00096 m_is_valid(http_msg.m_is_valid),
00097 m_is_chunked(http_msg.m_is_chunked),
00098 m_chunks_supported(http_msg.m_chunks_supported),
00099 m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
00100 m_remote_ip(http_msg.m_remote_ip),
00101 m_version_major(http_msg.m_version_major),
00102 m_version_minor(http_msg.m_version_minor),
00103 m_content_length(http_msg.m_content_length),
00104 m_content_buf(http_msg.m_content_buf),
00105 m_chunk_cache(http_msg.m_chunk_cache),
00106 m_headers(http_msg.m_headers),
00107 m_status(http_msg.m_status),
00108 m_has_missing_packets(http_msg.m_has_missing_packets),
00109 m_has_data_after_missing(http_msg.m_has_data_after_missing)
00110 {}
00111
00113 inline message& operator=(const message& http_msg) {
00114 m_first_line = http_msg.m_first_line;
00115 m_is_valid = http_msg.m_is_valid;
00116 m_is_chunked = http_msg.m_is_chunked;
00117 m_chunks_supported = http_msg.m_chunks_supported;
00118 m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
00119 m_remote_ip = http_msg.m_remote_ip;
00120 m_version_major = http_msg.m_version_major;
00121 m_version_minor = http_msg.m_version_minor;
00122 m_content_length = http_msg.m_content_length;
00123 m_content_buf = http_msg.m_content_buf;
00124 m_chunk_cache = http_msg.m_chunk_cache;
00125 m_headers = http_msg.m_headers;
00126 m_status = http_msg.m_status;
00127 m_has_missing_packets = http_msg.m_has_missing_packets;
00128 m_has_data_after_missing = http_msg.m_has_data_after_missing;
00129 return *this;
00130 }
00131
00133 virtual ~message() {}
00134
00136 virtual void clear(void) {
00137 clear_first_line();
00138 m_is_valid = m_is_chunked = m_chunks_supported
00139 = m_do_not_send_content_length = false;
00140 m_remote_ip = boost::asio::ip::address_v4(0);
00141 m_version_major = m_version_minor = 1;
00142 m_content_length = 0;
00143 m_content_buf.clear();
00144 m_chunk_cache.clear();
00145 m_headers.clear();
00146 m_cookie_params.clear();
00147 m_status = STATUS_NONE;
00148 m_has_missing_packets = false;
00149 m_has_data_after_missing = false;
00150 }
00151
00153 virtual bool is_content_length_implied(void) const = 0;
00154
00156 inline bool is_valid(void) const { return m_is_valid; }
00157
00159 inline bool get_chunks_supported(void) const { return m_chunks_supported; }
00160
00162 inline boost::asio::ip::address& get_remote_ip(void) {
00163 return m_remote_ip;
00164 }
00165
00167 inline boost::uint16_t get_version_major(void) const { return m_version_major; }
00168
00170 inline boost::uint16_t get_version_minor(void) const { return m_version_minor; }
00171
00173 inline std::string get_version_string(void) const {
00174 std::string http_version(STRING_HTTP_VERSION);
00175 http_version += boost::lexical_cast<std::string>(get_version_major());
00176 http_version += '.';
00177 http_version += boost::lexical_cast<std::string>(get_version_minor());
00178 return http_version;
00179 }
00180
00182 inline boost::uint64_t get_content_length(void) const { return m_content_length; }
00183
00185 inline bool is_chunked(void) const { return m_is_chunked; }
00186
00188 bool is_content_buffer_allocated() const { return !m_content_buf.is_empty(); }
00189
00191 inline std::size_t get_content_buffer_size() const { return m_content_buf.size(); }
00192
00194 inline char *get_content(void) { return m_content_buf.get(); }
00195
00197 inline const char *get_content(void) const { return m_content_buf.get(); }
00198
00200 inline chunk_cache_t& get_chunk_cache(void) { return m_chunk_cache; }
00201
00203 inline const std::string& get_header(const std::string& key) const {
00204 return get_value(m_headers, key);
00205 }
00206
00208 inline ihash_multimap& get_headers(void) {
00209 return m_headers;
00210 }
00211
00213 inline bool has_header(const std::string& key) const {
00214 return(m_headers.find(key) != m_headers.end());
00215 }
00216
00219 inline const std::string& get_cookie(const std::string& key) const {
00220 return get_value(m_cookie_params, key);
00221 }
00222
00224 inline ihash_multimap& get_cookies(void) {
00225 return m_cookie_params;
00226 }
00227
00230 inline bool has_cookie(const std::string& key) const {
00231 return(m_cookie_params.find(key) != m_cookie_params.end());
00232 }
00233
00236 inline void add_cookie(const std::string& key, const std::string& value) {
00237 m_cookie_params.insert(std::make_pair(key, value));
00238 }
00239
00242 inline void change_cookie(const std::string& key, const std::string& value) {
00243 change_value(m_cookie_params, key, value);
00244 }
00245
00248 inline void delete_cookie(const std::string& key) {
00249 delete_value(m_cookie_params, key);
00250 }
00251
00253 inline const std::string& get_first_line(void) const {
00254 if (m_first_line.empty())
00255 update_first_line();
00256 return m_first_line;
00257 }
00258
00260 inline bool has_missing_packets() const { return m_has_missing_packets; }
00261
00263 inline void set_missing_packets(bool newVal) { m_has_missing_packets = newVal; }
00264
00266 inline bool has_data_after_missing_packets() const { return m_has_data_after_missing; }
00267
00268 inline void set_data_after_missing_packet(bool newVal) { m_has_data_after_missing = newVal; }
00269
00271 inline void set_is_valid(bool b = true) { m_is_valid = b; }
00272
00274 inline void set_chunks_supported(bool b) { m_chunks_supported = b; }
00275
00277 inline void set_remote_ip(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
00278
00280 inline void set_version_major(const boost::uint16_t n) {
00281 m_version_major = n;
00282 clear_first_line();
00283 }
00284
00286 inline void set_version_minor(const boost::uint16_t n) {
00287 m_version_minor = n;
00288 clear_first_line();
00289 }
00290
00292 inline void set_content_length(const boost::uint64_t n) { m_content_length = n; }
00293
00295 inline void set_do_not_send_content_length(void) { m_do_not_send_content_length = true; }
00296
00298 inline data_status_t get_status() const { return m_status; }
00299
00301 inline void set_status(data_status_t newVal) { m_status = newVal; }
00302
00304 inline void update_content_length_using_header(void) {
00305 ihash_multimap::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
00306 if (i == m_headers.end()) {
00307 m_content_length = 0;
00308 } else {
00309 std::string trimmed_length(i->second);
00310 boost::algorithm::trim(trimmed_length);
00311 m_content_length = boost::lexical_cast<boost::uint64_t>(trimmed_length);
00312 }
00313 }
00314
00316 inline void update_transfer_encoding_using_header(void) {
00317 m_is_chunked = false;
00318 ihash_multimap::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
00319 if (i != m_headers.end()) {
00320
00321 m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
00322
00323 }
00324 }
00325
00328 inline char *create_content_buffer(void) {
00329 m_content_buf.resize(m_content_length);
00330 return m_content_buf.get();
00331 }
00332
00334 inline void set_content(const std::string& content) {
00335 set_content_length(content.size());
00336 create_content_buffer();
00337 memcpy(m_content_buf.get(), content.c_str(), content.size());
00338 }
00339
00341 inline void clear_content(void) {
00342 set_content_length(0);
00343 create_content_buffer();
00344 delete_value(m_headers, HEADER_CONTENT_TYPE);
00345 }
00346
00348 inline void set_content_type(const std::string& type) {
00349 change_value(m_headers, HEADER_CONTENT_TYPE, type);
00350 }
00351
00353 inline void add_header(const std::string& key, const std::string& value) {
00354 m_headers.insert(std::make_pair(key, value));
00355 }
00356
00358 inline void change_header(const std::string& key, const std::string& value) {
00359 change_value(m_headers, key, value);
00360 }
00361
00363 inline void delete_header(const std::string& key) {
00364 delete_value(m_headers, key);
00365 }
00366
00368 inline bool check_keep_alive(void) const {
00369 return (get_header(HEADER_CONNECTION) != "close"
00370 && (get_version_major() > 1
00371 || (get_version_major() >= 1 && get_version_minor() >= 1)) );
00372 }
00373
00381 inline void prepare_buffers_for_send(write_buffers_t& write_buffers,
00382 const bool keep_alive,
00383 const bool using_chunks)
00384 {
00385
00386 prepare_headers_for_send(keep_alive, using_chunks);
00387
00388 write_buffers.push_back(boost::asio::buffer(get_first_line()));
00389 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00390
00391 append_cookie_headers();
00392
00393 append_headers(write_buffers);
00394 }
00395
00396
00406 std::size_t send(tcp::connection& tcp_conn,
00407 boost::system::error_code& ec,
00408 bool headers_only = false);
00409
00419 std::size_t receive(tcp::connection& tcp_conn,
00420 boost::system::error_code& ec,
00421 parser& http_parser);
00422
00433 std::size_t receive(tcp::connection& tcp_conn,
00434 boost::system::error_code& ec,
00435 bool headers_only = false,
00436 std::size_t max_content_length = static_cast<size_t>(-1));
00437
00447 std::size_t write(std::ostream& out,
00448 boost::system::error_code& ec,
00449 bool headers_only = false);
00450
00460 std::size_t read(std::istream& in,
00461 boost::system::error_code& ec,
00462 parser& http_parser);
00463
00474 std::size_t read(std::istream& in,
00475 boost::system::error_code& ec,
00476 bool headers_only = false,
00477 std::size_t max_content_length = static_cast<size_t>(-1));
00478
00482 void concatenate_chunks(void);
00483
00484
00485 protected:
00486
00488 class content_buffer_t {
00489 public:
00491 ~content_buffer_t() {}
00492
00494 content_buffer_t() : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty) {}
00495
00497 content_buffer_t(const content_buffer_t& buf)
00498 : m_buf(), m_len(0), m_empty(0), m_ptr(&m_empty)
00499 {
00500 if (buf.size()) {
00501 resize(buf.size());
00502 memcpy(get(), buf.get(), buf.size());
00503 }
00504 }
00505
00507 content_buffer_t& operator=(const content_buffer_t& buf) {
00508 if (buf.size()) {
00509 resize(buf.size());
00510 memcpy(get(), buf.get(), buf.size());
00511 } else {
00512 clear();
00513 }
00514 return *this;
00515 }
00516
00518 inline bool is_empty() const { return m_len == 0; }
00519
00521 inline std::size_t size() const { return m_len; }
00522
00524 inline const char *get() const { return m_ptr; }
00525
00527 inline char *get() { return m_ptr; }
00528
00530 inline void resize(std::size_t len) {
00531 m_len = len;
00532 if (len == 0) {
00533 m_buf.reset();
00534 m_ptr = &m_empty;
00535 } else {
00536 m_buf.reset(new char[len+1]);
00537 m_buf[len] = '\0';
00538 m_ptr = m_buf.get();
00539 }
00540 }
00541
00543 inline void clear() { resize(0); }
00544
00545 private:
00546 boost::scoped_array<char> m_buf;
00547 std::size_t m_len;
00548 char m_empty;
00549 char *m_ptr;
00550 };
00551
00558 inline void prepare_headers_for_send(const bool keep_alive,
00559 const bool using_chunks)
00560 {
00561 change_header(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
00562 if (using_chunks) {
00563 if (get_chunks_supported())
00564 change_header(HEADER_TRANSFER_ENCODING, "chunked");
00565 } else if (! m_do_not_send_content_length) {
00566 change_header(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(get_content_length()));
00567 }
00568 }
00569
00575 inline void append_headers(write_buffers_t& write_buffers) {
00576
00577 for (ihash_multimap::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
00578 write_buffers.push_back(boost::asio::buffer(i->first));
00579 write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
00580 write_buffers.push_back(boost::asio::buffer(i->second));
00581 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00582 }
00583
00584 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00585 }
00586
00588 virtual void append_cookie_headers(void) {}
00589
00598 template <typename DictionaryType>
00599 inline static const std::string& get_value(const DictionaryType& dict,
00600 const std::string& key)
00601 {
00602 typename DictionaryType::const_iterator i = dict.find(key);
00603 return ( (i==dict.end()) ? STRING_EMPTY : i->second );
00604 }
00605
00615 template <typename DictionaryType>
00616 inline static void change_value(DictionaryType& dict,
00617 const std::string& key, const std::string& value)
00618
00619 {
00620
00621 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00622 result_pair = dict.equal_range(key);
00623 if (result_pair.first == dict.end()) {
00624
00625 dict.insert(std::make_pair(key, value));
00626 } else {
00627
00628 result_pair.first->second = value;
00629
00630 typename DictionaryType::iterator i;
00631 ++(result_pair.first);
00632 while (result_pair.first != result_pair.second) {
00633 i = result_pair.first;
00634 ++(result_pair.first);
00635 dict.erase(i);
00636 }
00637 }
00638 }
00639
00646 template <typename DictionaryType>
00647 inline static void delete_value(DictionaryType& dict,
00648 const std::string& key)
00649 {
00650 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00651 result_pair = dict.equal_range(key);
00652 if (result_pair.first != dict.end())
00653 dict.erase(result_pair.first, result_pair.second);
00654 }
00655
00658 inline void clear_first_line(void) const {
00659 if (! m_first_line.empty())
00660 m_first_line.clear();
00661 }
00662
00664 virtual void update_first_line(void) const = 0;
00665
00668 mutable std::string m_first_line;
00669
00670
00671 private:
00672
00674 static const boost::regex REGEX_ICASE_CHUNKED;
00675
00677 bool m_is_valid;
00678
00680 bool m_is_chunked;
00681
00683 bool m_chunks_supported;
00684
00686 bool m_do_not_send_content_length;
00687
00689 boost::asio::ip::address m_remote_ip;
00690
00692 boost::uint16_t m_version_major;
00693
00695 boost::uint16_t m_version_minor;
00696
00698 boost::uint64_t m_content_length;
00699
00701 content_buffer_t m_content_buf;
00702
00704 chunk_cache_t m_chunk_cache;
00705
00707 ihash_multimap m_headers;
00708
00710 ihash_multimap m_cookie_params;
00711
00713 data_status_t m_status;
00714
00716 bool m_has_missing_packets;
00717
00719 bool m_has_data_after_missing;
00720 };
00721
00722
00723 }
00724 }
00725
00726 #endif