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 <vector>
00014 #include <boost/cstdint.hpp>
00015 #include <boost/asio.hpp>
00016 #include <boost/scoped_array.hpp>
00017 #include <boost/lexical_cast.hpp>
00018 #include <boost/algorithm/string/trim.hpp>
00019 #include <boost/regex.hpp>
00020 #include <pion/PionConfig.hpp>
00021 #include <pion/net/HTTPTypes.hpp>
00022 
00023 
00024 namespace pion {    // begin namespace pion
00025 namespace net {     // begin namespace net (Pion Network Library)
00026 
00027 
00028 // forward declaration for class used by send() and receive()
00029 class TCPConnection;
00030 
00031 
00035 class PION_NET_API HTTPMessage
00036     : public HTTPTypes
00037 {
00038 public:
00039 
00041     typedef std::vector<boost::asio::const_buffer>  WriteBuffers;
00042 
00044     typedef std::vector<char>   ChunkCache;
00045 
00047     struct ReceiveError
00048         : public boost::system::error_category
00049     {
00050         virtual ~ReceiveError() {}
00051         virtual inline const char *name() const { return "ReceiveError"; }
00052         virtual inline std::string message(int ev) const {
00053             std::string result;
00054             switch(ev) {
00055                 case 1:
00056                     result = "HTTP message parsing error";
00057                     break;
00058                 default:
00059                     result = "Unknown receive error";
00060                     break;
00061             }
00062             return result;
00063         }
00064     };
00065 
00066 
00068     HTTPMessage(void)
00069         : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
00070         m_do_not_send_content_length(false),
00071         m_version_major(1), m_version_minor(1), m_content_length(0)
00072     {}
00073 
00075     HTTPMessage(const HTTPMessage& http_msg)
00076         : m_first_line(http_msg.m_first_line),
00077         m_is_valid(http_msg.m_is_valid),
00078         m_is_chunked(http_msg.m_is_chunked),
00079         m_chunks_supported(http_msg.m_chunks_supported),
00080         m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
00081         m_remote_ip(http_msg.m_remote_ip),
00082         m_version_major(http_msg.m_version_major),
00083         m_version_minor(http_msg.m_version_minor),
00084         m_content_length(http_msg.m_content_length),
00085         m_chunk_cache(http_msg.m_chunk_cache),
00086         m_headers(http_msg.m_headers)
00087     {
00088         if (http_msg.m_content_buf) {
00089             char *ptr = createContentBuffer();
00090             memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
00091         }
00092     }
00093 
00095     inline HTTPMessage& operator=(const HTTPMessage& http_msg) {
00096         m_first_line = http_msg.m_first_line;
00097         m_is_valid = http_msg.m_is_valid;
00098         m_is_chunked = http_msg.m_is_chunked;
00099         m_chunks_supported = http_msg.m_chunks_supported;
00100         m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
00101         m_remote_ip = http_msg.m_remote_ip;
00102         m_version_major = http_msg.m_version_major;
00103         m_version_minor = http_msg.m_version_minor;
00104         m_content_length = http_msg.m_content_length;
00105         m_chunk_cache = http_msg.m_chunk_cache;
00106         m_headers = http_msg.m_headers;
00107         if (http_msg.m_content_buf) {
00108             char *ptr = createContentBuffer();
00109             memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
00110         }
00111         return *this;
00112     }
00113 
00115     virtual ~HTTPMessage() {}
00116 
00118     virtual void clear(void) {
00119         clearFirstLine();
00120         m_is_valid = m_is_chunked = m_chunks_supported
00121             = m_do_not_send_content_length = false;
00122         m_remote_ip = boost::asio::ip::address_v4(0);
00123         m_version_major = m_version_minor = 1;
00124         m_content_length = 0;
00125         m_content_buf.reset();
00126         m_chunk_cache.clear();
00127         m_headers.clear();
00128     }
00129 
00131     virtual bool isContentLengthImplied(void) const = 0;
00132 
00134     inline bool isValid(void) const { return m_is_valid; }
00135 
00137     inline bool getChunksSupported(void) const { return m_chunks_supported; }
00138 
00140     inline boost::asio::ip::address& getRemoteIp(void) {
00141         return m_remote_ip;
00142     }
00143 
00145     inline boost::uint16_t getVersionMajor(void) const { return m_version_major; }
00146 
00148     inline boost::uint16_t getVersionMinor(void) const { return m_version_minor; }
00149 
00151     inline std::string getVersionString(void) const {
00152         std::string http_version(STRING_HTTP_VERSION);
00153         http_version += boost::lexical_cast<std::string>(getVersionMajor());
00154         http_version += '.';
00155         http_version += boost::lexical_cast<std::string>(getVersionMinor());
00156         return http_version;
00157     }
00158 
00160     inline std::size_t getContentLength(void) const { return m_content_length; }
00161 
00163     inline bool isChunked(void) const { return m_is_chunked; }
00164 
00166     inline char *getContent(void) { return m_content_buf.get(); }
00167 
00169     inline const char *getContent(void) const { return m_content_buf.get(); }
00170 
00172     inline ChunkCache& getChunkCache(void) { return m_chunk_cache; }
00173 
00175     inline const std::string& getHeader(const std::string& key) const {
00176         return getValue(m_headers, key);
00177     }
00178 
00180     inline Headers& getHeaders(void) {
00181         return m_headers;
00182     }
00183 
00185     inline bool hasHeader(const std::string& key) const {
00186         return(m_headers.find(key) != m_headers.end());
00187     }
00188 
00190     inline const std::string& getFirstLine(void) const {
00191         if (m_first_line.empty())
00192             updateFirstLine();
00193         return m_first_line;
00194     }
00195 
00196 
00198     inline void setIsValid(bool b = true) { m_is_valid = b; }
00199 
00201     inline void setChunksSupported(bool b) { m_chunks_supported = b; }
00202 
00204     inline void setRemoteIp(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
00205 
00207     inline void setVersionMajor(const boost::uint16_t n) {
00208         m_version_major = n;
00209         clearFirstLine();
00210     }
00211 
00213     inline void setVersionMinor(const boost::uint16_t n) {
00214         m_version_minor = n;
00215         clearFirstLine();
00216     }
00217 
00219     inline void setContentLength(const std::size_t n) { m_content_length = n; }
00220 
00222     inline void setDoNotSendContentLength(void) { m_do_not_send_content_length = true; }
00223 
00225     inline void updateContentLengthUsingHeader(void) {
00226         Headers::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
00227         if (i == m_headers.end()) {
00228             m_content_length = 0;
00229         } else {
00230             std::string trimmed_length(i->second);
00231             boost::algorithm::trim(trimmed_length);
00232             m_content_length = boost::lexical_cast<std::size_t>(trimmed_length);
00233         }
00234     }
00235 
00237     inline void updateTransferCodingUsingHeader(void) {
00238         m_is_chunked = false;
00239         Headers::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
00240         if (i != m_headers.end()) {
00241             // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
00242             m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
00243             // ignoring other possible values for now
00244         }
00245     }
00246 
00249     inline char *createContentBuffer(void) {
00250         m_content_buf.reset(new char[m_content_length + 1]);
00251         m_content_buf[m_content_length] = '\0';
00252         return m_content_buf.get();
00253     }
00254 
00256     inline void setContentType(const std::string& type) {
00257         changeValue(m_headers, HEADER_CONTENT_TYPE, type);
00258     }
00259 
00261     inline void addHeader(const std::string& key, const std::string& value) {
00262         m_headers.insert(std::make_pair(key, value));
00263     }
00264 
00266     inline void changeHeader(const std::string& key, const std::string& value) {
00267         changeValue(m_headers, key, value);
00268     }
00269 
00271     inline void deleteHeader(const std::string& key) {
00272         deleteValue(m_headers, key);
00273     }
00274 
00276     inline bool checkKeepAlive(void) const {
00277         return (getHeader(HEADER_CONNECTION) != "close"
00278                 && (getVersionMajor() > 1
00279                     || (getVersionMajor() >= 1 && getVersionMinor() >= 1)) );
00280     }
00281 
00289     inline void prepareBuffersForSend(WriteBuffers& write_buffers,
00290                                       const bool keep_alive,
00291                                       const bool using_chunks)
00292     {
00293         // update message headers
00294         prepareHeadersForSend(keep_alive, using_chunks);
00295         // add first message line
00296         write_buffers.push_back(boost::asio::buffer(getFirstLine()));
00297         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00298         // append HTTP headers
00299         appendHeaders(write_buffers);
00300     }
00301 
00302 
00309     std::size_t send(TCPConnection& tcp_conn, boost::system::error_code& ec);
00310 
00317     std::size_t receive(TCPConnection& tcp_conn, boost::system::error_code& ec);
00318 
00322     void concatenateChunks(void);
00323 
00324 
00325 protected:
00326 
00333     inline void prepareHeadersForSend(const bool keep_alive,
00334                                       const bool using_chunks)
00335     {
00336         changeHeader(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
00337         if (using_chunks) {
00338             if (getChunksSupported())
00339                 changeHeader(HEADER_TRANSFER_ENCODING, "chunked");
00340         } else if (! m_do_not_send_content_length) {
00341             changeHeader(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(getContentLength()));
00342         }
00343     }
00344 
00350     inline void appendHeaders(WriteBuffers& write_buffers) {
00351         // add HTTP headers
00352         for (Headers::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
00353             write_buffers.push_back(boost::asio::buffer(i->first));
00354             write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
00355             write_buffers.push_back(boost::asio::buffer(i->second));
00356             write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00357         }
00358         // add an extra CRLF to end HTTP headers
00359         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00360     }
00361 
00370     template <typename DictionaryType>
00371     inline static const std::string& getValue(const DictionaryType& dict,
00372                                               const std::string& key)
00373     {
00374         typename DictionaryType::const_iterator i = dict.find(key);
00375         return ( (i==dict.end()) ? STRING_EMPTY : i->second );
00376     }
00377 
00387     template <typename DictionaryType>
00388     inline static void changeValue(DictionaryType& dict,
00389                                    const std::string& key, const std::string& value)
00390 
00391     {
00392         // retrieve all current values for key
00393         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00394             result_pair = dict.equal_range(key);
00395         if (result_pair.first == dict.end()) {
00396             // no values exist -> add a new key
00397             dict.insert(std::make_pair(key, value));
00398         } else {
00399             // set the first value found for the key to the new one
00400             result_pair.first->second = value;
00401             // remove any remaining values
00402             typename DictionaryType::iterator i;
00403             ++(result_pair.first);
00404             while (result_pair.first != result_pair.second) {
00405                 i = result_pair.first;
00406                 ++(result_pair.first);
00407                 dict.erase(i);
00408             }
00409         }
00410     }
00411 
00418     template <typename DictionaryType>
00419     inline static void deleteValue(DictionaryType& dict,
00420                                    const std::string& key)
00421     {
00422         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00423             result_pair = dict.equal_range(key);
00424         if (result_pair.first != dict.end())
00425             dict.erase(result_pair.first, result_pair.second);
00426     }
00427 
00430     inline void clearFirstLine(void) const {
00431         if (! m_first_line.empty())
00432             m_first_line.clear();
00433     }
00434 
00436     virtual void updateFirstLine(void) const = 0;
00437 
00438 
00441     mutable std::string             m_first_line;
00442 
00443 
00444 private:
00445 
00447     static const boost::regex       REGEX_ICASE_CHUNKED;
00448 
00450     bool                            m_is_valid;
00451 
00453     bool                            m_is_chunked;
00454 
00456     bool                            m_chunks_supported;
00457 
00459     bool                            m_do_not_send_content_length;
00460 
00462     boost::asio::ip::address        m_remote_ip;
00463 
00465     boost::uint16_t                 m_version_major;
00466 
00468     boost::uint16_t                 m_version_minor;
00469 
00471     std::size_t                     m_content_length;
00472 
00474     boost::scoped_array<char>       m_content_buf;
00475 
00477     ChunkCache                      m_chunk_cache;
00478 
00480     Headers                         m_headers;
00481 };
00482 
00483 
00484 }   // end namespace net
00485 }   // end namespace pion
00486 
00487 #endif

Generated on Fri Dec 4 08:54:29 2009 for pion-net by  doxygen 1.4.7