pion  5.0.6
include/pion/http/message.hpp
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