- Main Page
- Classes
- Files

- File List

# include/pion/http/message.hpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.com)
    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     #define BOOST_SYSTEM_NOEXCEPT BOOST_NOEXCEPT
    00027 #endif
    00028
    00029
    00030 namespace pion {    // begin namespace pion
    00031
    00032
    00033 namespace tcp {
    00034     // forward declaration for class used by send() and receive()
    00035     class connection;
    00036 }
    00037
    00038
    00039 namespace http {    // begin namespace http
    00040
    00041
    00042 // forward declaration of parser class
    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,        // no data received (i.e. all lost)
    00084         STATUS_TRUNCATED,   // one or more missing packets at the end
    00085         STATUS_PARTIAL,     // one or more missing packets but NOT at the end
    00086         STATUS_OK           // no missing packets
    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             // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
    00325             m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
    00326             // ignoring other possible values for now
    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         // update message headers
    00390         prepare_headers_for_send(keep_alive, using_chunks);
    00391         // add first message line
    00392         write_buffers.push_back(boost::asio::buffer(get_first_line()));
    00393         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
    00394         // append cookie headers (if any)
    00395         append_cookie_headers();
    00396         // append HTTP headers
    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         // add HTTP headers
    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         // add an extra CRLF to end HTTP headers
    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         // retrieve all current values for key
    00625         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
    00626             result_pair = dict.equal_range(key);
    00627         if (result_pair.first == dict.end()) {
    00628             // no values exist -> add a new key
    00629             dict.insert(std::make_pair(key, value));
    00630         } else {
    00631             // set the first value found for the key to the new one
    00632             result_pair.first->second = value;
    00633             // remove any remaining values
    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 }   // end namespace http
    00728 }   // end namespace pion
    00729
    00730 #endif