pion-net  4.0.9
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