# net/include/pion/net/HTTPMessage.hpp

    00001 // ------------------------------------------------------------------
    00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
    00003 // ------------------------------------------------------------------
    00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.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_HTTPMESSAGE_HEADER__
    00011 #define __PION_HTTPMESSAGE_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/PionConfig.hpp>
    00023 #include <pion/net/HTTPTypes.hpp>
    00024
    00025
    00026 namespace pion {    // begin namespace pion
    00027 namespace net {     // begin namespace net (Pion Network Library)
    00028
    00029
    00030 // forward declaration for class used by send() and receive()
    00031 class TCPConnection;
    00032
    00033
    00037 class PION_NET_API HTTPMessage
    00038     : public HTTPTypes
    00039 {
    00040 public:
    00041
    00043     typedef std::vector<boost::asio::const_buffer>  WriteBuffers;
    00044
    00046     typedef std::vector<char>   ChunkCache;
    00047
    00049     struct ReceiveError
    00050         : public boost::system::error_category
    00051     {
    00052         virtual ~ReceiveError() {}
    00053         virtual inline const char *name() const { return "ReceiveError"; }
    00054         virtual inline std::string message(int ev) const {
    00055             std::string result;
    00056             switch(ev) {
    00057                 case 1:
    00058                     result = "HTTP message parsing error";
    00059                     break;
    00060                 default:
    00061                     result = "Unknown receive error";
    00062                     break;
    00063             }
    00064             return result;
    00065         }
    00066     };
    00067
    00069     enum DataStatus
    00070     {
    00071         STATUS_NONE,        // no data received (i.e. all lost)
    00072         STATUS_TRUNCATED,   // one or more missing packets at the end
    00073         STATUS_PARTIAL,     // one or more missing packets but NOT at the end
    00074         STATUS_OK           // no missing packets
    00075     };
    00076
    00078     HTTPMessage(void)
    00079         : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
    00080         m_do_not_send_content_length(false),
    00081         m_version_major(1), m_version_minor(1), m_content_length(0),
    00082         m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false)
    00083     {}
    00084
    00086     HTTPMessage(const HTTPMessage& http_msg)
    00087         : m_first_line(http_msg.m_first_line),
    00088         m_is_valid(http_msg.m_is_valid),
    00089         m_is_chunked(http_msg.m_is_chunked),
    00090         m_chunks_supported(http_msg.m_chunks_supported),
    00091         m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
    00092         m_remote_ip(http_msg.m_remote_ip),
    00093         m_version_major(http_msg.m_version_major),
    00094         m_version_minor(http_msg.m_version_minor),
    00095         m_content_length(http_msg.m_content_length),
    00096         m_chunk_cache(http_msg.m_chunk_cache),
    00097         m_headers(http_msg.m_headers),
    00098         m_status(http_msg.m_status),
    00099         m_has_missing_packets(http_msg.m_has_missing_packets),
    00100         m_has_data_after_missing(http_msg.m_has_data_after_missing)
    00101     {
    00102         if (http_msg.m_content_buf) {
    00103             char *ptr = createContentBuffer();
    00104             memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
    00105         }
    00106     }
    00107
    00109     inline HTTPMessage& operator=(const HTTPMessage& http_msg) {
    00110         m_first_line = http_msg.m_first_line;
    00111         m_is_valid = http_msg.m_is_valid;
    00112         m_is_chunked = http_msg.m_is_chunked;
    00113         m_chunks_supported = http_msg.m_chunks_supported;
    00114         m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
    00115         m_remote_ip = http_msg.m_remote_ip;
    00116         m_version_major = http_msg.m_version_major;
    00117         m_version_minor = http_msg.m_version_minor;
    00118         m_content_length = http_msg.m_content_length;
    00119         m_chunk_cache = http_msg.m_chunk_cache;
    00120         m_headers = http_msg.m_headers;
    00121         m_status = http_msg.m_status;
    00122         m_has_missing_packets = http_msg.m_has_missing_packets;
    00123         m_has_data_after_missing = http_msg.m_has_data_after_missing;
    00124         if (http_msg.m_content_buf) {
    00125             char *ptr = createContentBuffer();
    00126             memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
    00127         }
    00128         return *this;
    00129     }
    00130
    00132     virtual ~HTTPMessage() {}
    00133
    00135     virtual void clear(void) {
    00136         clearFirstLine();
    00137         m_is_valid = m_is_chunked = m_chunks_supported
    00138             = m_do_not_send_content_length = false;
    00139         m_remote_ip = boost::asio::ip::address_v4(0);
    00140         m_version_major = m_version_minor = 1;
    00141         m_content_length = 0;
    00142         m_content_buf.reset();
    00143         m_chunk_cache.clear();
    00144         m_headers.clear();
    00145         m_cookie_params.clear();
    00146         m_status = STATUS_NONE;
    00147         m_has_missing_packets = false;
    00148         m_has_data_after_missing = false;
    00149     }
    00150
    00152     virtual bool isContentLengthImplied(void) const = 0;
    00153
    00155     inline bool isValid(void) const { return m_is_valid; }
    00156
    00158     inline bool getChunksSupported(void) const { return m_chunks_supported; }
    00159
    00161     inline boost::asio::ip::address& getRemoteIp(void) {
    00162         return m_remote_ip;
    00163     }
    00164
    00166     inline boost::uint16_t getVersionMajor(void) const { return m_version_major; }
    00167
    00169     inline boost::uint16_t getVersionMinor(void) const { return m_version_minor; }
    00170
    00172     inline std::string getVersionString(void) const {
    00173         std::string http_version(STRING_HTTP_VERSION);
    00174         http_version += boost::lexical_cast<std::string>(getVersionMajor());
    00175         http_version += '.';
    00176         http_version += boost::lexical_cast<std::string>(getVersionMinor());
    00177         return http_version;
    00178     }
    00179
    00181     inline std::size_t getContentLength(void) const { return m_content_length; }
    00182
    00184     inline bool isChunked(void) const { return m_is_chunked; }
    00185
    00187     inline char *getContent(void) { return m_content_buf.get(); }
    00188
    00190     inline const char *getContent(void) const { return m_content_buf.get(); }
    00191
    00193     inline ChunkCache& getChunkCache(void) { return m_chunk_cache; }
    00194
    00196     inline const std::string& getHeader(const std::string& key) const {
    00197         return getValue(m_headers, key);
    00198     }
    00199
    00201     inline Headers& getHeaders(void) {
    00202         return m_headers;
    00203     }
    00204
    00206     inline bool hasHeader(const std::string& key) const {
    00207         return(m_headers.find(key) != m_headers.end());
    00208     }
    00209
    00212     inline const std::string& getCookie(const std::string& key) const {
    00213         return getValue(m_cookie_params, key);
    00214     }
    00215
    00217     inline CookieParams& getCookieParams(void) {
    00218         return m_cookie_params;
    00219     }
    00220
    00223     inline bool hasCookie(const std::string& key) const {
    00224         return(m_cookie_params.find(key) != m_cookie_params.end());
    00225     }
    00226
    00229     inline void addCookie(const std::string& key, const std::string& value) {
    00230         m_cookie_params.insert(std::make_pair(key, value));
    00231     }
    00232
    00235     inline void changeCookie(const std::string& key, const std::string& value) {
    00236         changeValue(m_cookie_params, key, value);
    00237     }
    00238
    00241     inline void deleteCookie(const std::string& key) {
    00242         deleteValue(m_cookie_params, key);
    00243     }
    00244
    00246     inline const std::string& getFirstLine(void) const {
    00247         if (m_first_line.empty())
    00248             updateFirstLine();
    00249         return m_first_line;
    00250     }
    00251
    00253     inline bool hasMissingPackets() const { return m_has_missing_packets; }
    00254
    00256     inline void setMissingPackets(bool newVal) { m_has_missing_packets = newVal; }
    00257
    00259     inline bool hasDataAfterMissingPackets() const { return m_has_data_after_missing; }
    00260
    00261     inline void setDataAfterMissingPacket(bool newVal) { m_has_data_after_missing = newVal; }
    00262
    00264     inline void setIsValid(bool b = true) { m_is_valid = b; }
    00265
    00267     inline void setChunksSupported(bool b) { m_chunks_supported = b; }
    00268
    00270     inline void setRemoteIp(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
    00271
    00273     inline void setVersionMajor(const boost::uint16_t n) {
    00274         m_version_major = n;
    00275         clearFirstLine();
    00276     }
    00277
    00279     inline void setVersionMinor(const boost::uint16_t n) {
    00280         m_version_minor = n;
    00281         clearFirstLine();
    00282     }
    00283
    00285     inline void setContentLength(const std::size_t n) { m_content_length = n; }
    00286
    00288     inline void setDoNotSendContentLength(void) { m_do_not_send_content_length = true; }
    00289
    00291     inline DataStatus getStatus() const { return m_status; }
    00292
    00294     inline void setStatus(DataStatus newVal) { m_status = newVal; }
    00295
    00297     inline void updateContentLengthUsingHeader(void) {
    00298         Headers::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
    00299         if (i == m_headers.end()) {
    00300             m_content_length = 0;
    00301         } else {
    00302             std::string trimmed_length(i->second);
    00303             boost::algorithm::trim(trimmed_length);
    00304             m_content_length = boost::lexical_cast<std::size_t>(trimmed_length);
    00305         }
    00306     }
    00307
    00309     inline void updateTransferCodingUsingHeader(void) {
    00310         m_is_chunked = false;
    00311         Headers::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
    00312         if (i != m_headers.end()) {
    00313             // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
    00314             m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
    00315             // ignoring other possible values for now
    00316         }
    00317     }
    00318
    00321     inline char *createContentBuffer(void) {
    00322         m_content_buf.reset(new char[m_content_length + 1]);
    00323         m_content_buf[m_content_length] = '\0';
    00324         return m_content_buf.get();
    00325     }
    00326
    00328     inline void setContent(const std::string& content) {
    00329         setContentLength(content.size());
    00330         createContentBuffer();
    00331         memcpy(m_content_buf.get(), content.c_str(), content.size());
    00332     }
    00333
    00335     inline void clearContent(void) {
    00336         setContentLength(0);
    00337         createContentBuffer();
    00338         deleteValue(m_headers, HEADER_CONTENT_TYPE);
    00339     }
    00340
    00342     inline void setContentType(const std::string& type) {
    00343         changeValue(m_headers, HEADER_CONTENT_TYPE, type);
    00344     }
    00345
    00347     inline void addHeader(const std::string& key, const std::string& value) {
    00348         m_headers.insert(std::make_pair(key, value));
    00349     }
    00350
    00352     inline void changeHeader(const std::string& key, const std::string& value) {
    00353         changeValue(m_headers, key, value);
    00354     }
    00355
    00357     inline void deleteHeader(const std::string& key) {
    00358         deleteValue(m_headers, key);
    00359     }
    00360
    00362     inline bool checkKeepAlive(void) const {
    00363         return (getHeader(HEADER_CONNECTION) != "close"
    00364                 && (getVersionMajor() > 1
    00365                     || (getVersionMajor() >= 1 && getVersionMinor() >= 1)) );
    00366     }
    00367
    00375     inline void prepareBuffersForSend(WriteBuffers& write_buffers,
    00376                                       const bool keep_alive,
    00377                                       const bool using_chunks)
    00378     {
    00379         // update message headers
    00380         prepareHeadersForSend(keep_alive, using_chunks);
    00381         // add first message line
    00382         write_buffers.push_back(boost::asio::buffer(getFirstLine()));
    00383         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
    00384         // append HTTP headers
    00385         appendHeaders(write_buffers);
    00386     }
    00387
    00388
    00398     std::size_t send(TCPConnection& tcp_conn, boost::system::error_code& ec,
    00399         bool headers_only = false);
    00400
    00410     std::size_t receive(TCPConnection& tcp_conn, boost::system::error_code& ec,
    00411         bool headers_only = false);
    00412
    00422     std::size_t write(std::ostream& out, boost::system::error_code& ec,
    00423         bool headers_only = false);
    00424
    00434     std::size_t read(std::istream& in, boost::system::error_code& ec,
    00435         bool headers_only = false);
    00436
    00440     void concatenateChunks(void);
    00441
    00442
    00443 protected:
    00444
    00451     inline void prepareHeadersForSend(const bool keep_alive,
    00452                                       const bool using_chunks)
    00453     {
    00454         changeHeader(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
    00455         if (using_chunks) {
    00456             if (getChunksSupported())
    00457                 changeHeader(HEADER_TRANSFER_ENCODING, "chunked");
    00458         } else if (! m_do_not_send_content_length) {
    00459             changeHeader(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(getContentLength()));
    00460         }
    00461     }
    00462
    00468     inline void appendHeaders(WriteBuffers& write_buffers) {
    00469         // add HTTP headers
    00470         for (Headers::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
    00471             write_buffers.push_back(boost::asio::buffer(i->first));
    00472             write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
    00473             write_buffers.push_back(boost::asio::buffer(i->second));
    00474             write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
    00475         }
    00476         // add an extra CRLF to end HTTP headers
    00477         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
    00478     }
    00479
    00488     template <typename DictionaryType>
    00489     inline static const std::string& getValue(const DictionaryType& dict,
    00490                                               const std::string& key)
    00491     {
    00492         typename DictionaryType::const_iterator i = dict.find(key);
    00493         return ( (i==dict.end()) ? STRING_EMPTY : i->second );
    00494     }
    00495
    00505     template <typename DictionaryType>
    00506     inline static void changeValue(DictionaryType& dict,
    00507                                    const std::string& key, const std::string& value)
    00508
    00509     {
    00510         // retrieve all current values for key
    00511         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
    00512             result_pair = dict.equal_range(key);
    00513         if (result_pair.first == dict.end()) {
    00514             // no values exist -> add a new key
    00515             dict.insert(std::make_pair(key, value));
    00516         } else {
    00517             // set the first value found for the key to the new one
    00518             result_pair.first->second = value;
    00519             // remove any remaining values
    00520             typename DictionaryType::iterator i;
    00521             ++(result_pair.first);
    00522             while (result_pair.first != result_pair.second) {
    00523                 i = result_pair.first;
    00524                 ++(result_pair.first);
    00525                 dict.erase(i);
    00526             }
    00527         }
    00528     }
    00529
    00536     template <typename DictionaryType>
    00537     inline static void deleteValue(DictionaryType& dict,
    00538                                    const std::string& key)
    00539     {
    00540         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
    00541             result_pair = dict.equal_range(key);
    00542         if (result_pair.first != dict.end())
    00543             dict.erase(result_pair.first, result_pair.second);
    00544     }
    00545
    00548     inline void clearFirstLine(void) const {
    00549         if (! m_first_line.empty())
    00550             m_first_line.clear();
    00551     }
    00552
    00554     virtual void updateFirstLine(void) const = 0;
    00555
    00556
    00559     mutable std::string             m_first_line;
    00560
    00561
    00562 private:
    00563
    00565     static const boost::regex       REGEX_ICASE_CHUNKED;
    00566
    00568     bool                            m_is_valid;
    00569
    00571     bool                            m_is_chunked;
    00572
    00574     bool                            m_chunks_supported;
    00575
    00577     bool                            m_do_not_send_content_length;
    00578
    00580     boost::asio::ip::address        m_remote_ip;
    00581
    00583     boost::uint16_t                 m_version_major;
    00584
    00586     boost::uint16_t                 m_version_minor;
    00587
    00589     std::size_t                     m_content_length;
    00590
    00592     boost::scoped_array<char>       m_content_buf;
    00593
    00595     ChunkCache                      m_chunk_cache;
    00596
    00598     Headers                         m_headers;
    00599
    00601     CookieParams                    m_cookie_params;
    00602
    00604     DataStatus                      m_status;
    00605
    00607     bool                            m_has_missing_packets;
    00608
    00610     bool                            m_has_data_after_missing;
    00611 };
    00612
    00613
    00614 }   // end namespace net
    00615 }   // end namespace pion
    00616
    00617 #endif