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 
00067     enum DataStatus
00068     {
00069         STATUS_NONE,        // no data received (i.e. all lost)
00070         STATUS_TRUNCATED,   // one or more missing packets at the end 
00071         STATUS_PARTIAL,     // one or more missing packets but NOT at the end 
00072         STATUS_OK           // no missing packets
00073     };
00074 
00076     HTTPMessage(void)
00077         : m_is_valid(false), m_is_chunked(false), m_chunks_supported(false),
00078         m_do_not_send_content_length(false),
00079         m_version_major(1), m_version_minor(1), m_content_length(0),
00080         m_status(STATUS_NONE), m_has_missing_packets(false), m_has_data_after_missing(false)
00081     {}
00082 
00084     HTTPMessage(const HTTPMessage& http_msg)
00085         : m_first_line(http_msg.m_first_line),
00086         m_is_valid(http_msg.m_is_valid),
00087         m_is_chunked(http_msg.m_is_chunked),
00088         m_chunks_supported(http_msg.m_chunks_supported),
00089         m_do_not_send_content_length(http_msg.m_do_not_send_content_length),
00090         m_remote_ip(http_msg.m_remote_ip),
00091         m_version_major(http_msg.m_version_major),
00092         m_version_minor(http_msg.m_version_minor),
00093         m_content_length(http_msg.m_content_length),
00094         m_chunk_cache(http_msg.m_chunk_cache),
00095         m_headers(http_msg.m_headers),
00096         m_status(http_msg.m_status),
00097         m_has_missing_packets(http_msg.m_has_missing_packets),
00098         m_has_data_after_missing(http_msg.m_has_data_after_missing)
00099     {
00100         if (http_msg.m_content_buf) {
00101             char *ptr = createContentBuffer();
00102             memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
00103         }
00104     }
00105 
00107     inline HTTPMessage& operator=(const HTTPMessage& http_msg) {
00108         m_first_line = http_msg.m_first_line;
00109         m_is_valid = http_msg.m_is_valid;
00110         m_is_chunked = http_msg.m_is_chunked;
00111         m_chunks_supported = http_msg.m_chunks_supported;
00112         m_do_not_send_content_length = http_msg.m_do_not_send_content_length;
00113         m_remote_ip = http_msg.m_remote_ip;
00114         m_version_major = http_msg.m_version_major;
00115         m_version_minor = http_msg.m_version_minor;
00116         m_content_length = http_msg.m_content_length;
00117         m_chunk_cache = http_msg.m_chunk_cache;
00118         m_headers = http_msg.m_headers;
00119         m_status = http_msg.m_status;
00120         m_has_missing_packets = http_msg.m_has_missing_packets;
00121         m_has_data_after_missing = http_msg.m_has_data_after_missing;
00122         if (http_msg.m_content_buf) {
00123             char *ptr = createContentBuffer();
00124             memcpy(ptr, http_msg.m_content_buf.get(), m_content_length);
00125         }
00126         return *this;
00127     }
00128 
00130     virtual ~HTTPMessage() {}
00131 
00133     virtual void clear(void) {
00134         clearFirstLine();
00135         m_is_valid = m_is_chunked = m_chunks_supported
00136             = m_do_not_send_content_length = false;
00137         m_remote_ip = boost::asio::ip::address_v4(0);
00138         m_version_major = m_version_minor = 1;
00139         m_content_length = 0;
00140         m_content_buf.reset();
00141         m_chunk_cache.clear();
00142         m_headers.clear();
00143         m_cookie_params.clear();
00144         m_status = STATUS_NONE;
00145         m_has_missing_packets = false;
00146         m_has_data_after_missing = false;
00147     }
00148 
00150     virtual bool isContentLengthImplied(void) const = 0;
00151 
00153     inline bool isValid(void) const { return m_is_valid; }
00154 
00156     inline bool getChunksSupported(void) const { return m_chunks_supported; }
00157 
00159     inline boost::asio::ip::address& getRemoteIp(void) {
00160         return m_remote_ip;
00161     }
00162 
00164     inline boost::uint16_t getVersionMajor(void) const { return m_version_major; }
00165 
00167     inline boost::uint16_t getVersionMinor(void) const { return m_version_minor; }
00168 
00170     inline std::string getVersionString(void) const {
00171         std::string http_version(STRING_HTTP_VERSION);
00172         http_version += boost::lexical_cast<std::string>(getVersionMajor());
00173         http_version += '.';
00174         http_version += boost::lexical_cast<std::string>(getVersionMinor());
00175         return http_version;
00176     }
00177 
00179     inline std::size_t getContentLength(void) const { return m_content_length; }
00180 
00182     inline bool isChunked(void) const { return m_is_chunked; }
00183 
00185     inline char *getContent(void) { return m_content_buf.get(); }
00186 
00188     inline const char *getContent(void) const { return m_content_buf.get(); }
00189 
00191     inline ChunkCache& getChunkCache(void) { return m_chunk_cache; }
00192 
00194     inline const std::string& getHeader(const std::string& key) const {
00195         return getValue(m_headers, key);
00196     }
00197 
00199     inline Headers& getHeaders(void) {
00200         return m_headers;
00201     }
00202 
00204     inline bool hasHeader(const std::string& key) const {
00205         return(m_headers.find(key) != m_headers.end());
00206     }
00207 
00210     inline const std::string& getCookie(const std::string& key) const {
00211         return getValue(m_cookie_params, key);
00212     }
00213     
00215     inline CookieParams& getCookieParams(void) {
00216         return m_cookie_params;
00217     }
00218 
00221     inline bool hasCookie(const std::string& key) const {
00222         return(m_cookie_params.find(key) != m_cookie_params.end());
00223     }
00224     
00227     inline void addCookie(const std::string& key, const std::string& value) {
00228         m_cookie_params.insert(std::make_pair(key, value));
00229     }
00230 
00233     inline void changeCookie(const std::string& key, const std::string& value) {
00234         changeValue(m_cookie_params, key, value);
00235     }
00236 
00239     inline void deleteCookie(const std::string& key) {
00240         deleteValue(m_cookie_params, key);
00241     }
00242     
00244     inline const std::string& getFirstLine(void) const {
00245         if (m_first_line.empty())
00246             updateFirstLine();
00247         return m_first_line;
00248     }
00249 
00251     inline bool hasMissingPackets() const { return m_has_missing_packets; }
00252     
00254     inline void setMissingPackets(bool newVal) { m_has_missing_packets = newVal; }
00255 
00257     inline bool hasDataAfterMissingPackets() const { return m_has_data_after_missing; }
00258 
00259     inline void setDataAfterMissingPacket(bool newVal) { m_has_data_after_missing = newVal; }
00260 
00262     inline void setIsValid(bool b = true) { m_is_valid = b; }
00263 
00265     inline void setChunksSupported(bool b) { m_chunks_supported = b; }
00266 
00268     inline void setRemoteIp(const boost::asio::ip::address& ip) { m_remote_ip = ip; }
00269 
00271     inline void setVersionMajor(const boost::uint16_t n) {
00272         m_version_major = n;
00273         clearFirstLine();
00274     }
00275 
00277     inline void setVersionMinor(const boost::uint16_t n) {
00278         m_version_minor = n;
00279         clearFirstLine();
00280     }
00281 
00283     inline void setContentLength(const std::size_t n) { m_content_length = n; }
00284 
00286     inline void setDoNotSendContentLength(void) { m_do_not_send_content_length = true; }
00287 
00289     inline DataStatus getStatus() const { return m_status; }
00290 
00292     inline void setStatus(DataStatus newVal) { m_status = newVal; }
00293 
00295     inline void updateContentLengthUsingHeader(void) {
00296         Headers::const_iterator i = m_headers.find(HEADER_CONTENT_LENGTH);
00297         if (i == m_headers.end()) {
00298             m_content_length = 0;
00299         } else {
00300             std::string trimmed_length(i->second);
00301             boost::algorithm::trim(trimmed_length);
00302             m_content_length = boost::lexical_cast<std::size_t>(trimmed_length);
00303         }
00304     }
00305 
00307     inline void updateTransferCodingUsingHeader(void) {
00308         m_is_chunked = false;
00309         Headers::const_iterator i = m_headers.find(HEADER_TRANSFER_ENCODING);
00310         if (i != m_headers.end()) {
00311             // From RFC 2616, sec 3.6: All transfer-coding values are case-insensitive.
00312             m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
00313             // ignoring other possible values for now
00314         }
00315     }
00316 
00319     inline char *createContentBuffer(void) {
00320         m_content_buf.reset(new char[m_content_length + 1]);
00321         m_content_buf[m_content_length] = '\0';
00322         return m_content_buf.get();
00323     }
00324 
00326     inline void setContentType(const std::string& type) {
00327         changeValue(m_headers, HEADER_CONTENT_TYPE, type);
00328     }
00329 
00331     inline void addHeader(const std::string& key, const std::string& value) {
00332         m_headers.insert(std::make_pair(key, value));
00333     }
00334 
00336     inline void changeHeader(const std::string& key, const std::string& value) {
00337         changeValue(m_headers, key, value);
00338     }
00339 
00341     inline void deleteHeader(const std::string& key) {
00342         deleteValue(m_headers, key);
00343     }
00344 
00346     inline bool checkKeepAlive(void) const {
00347         return (getHeader(HEADER_CONNECTION) != "close"
00348                 && (getVersionMajor() > 1
00349                     || (getVersionMajor() >= 1 && getVersionMinor() >= 1)) );
00350     }
00351 
00359     inline void prepareBuffersForSend(WriteBuffers& write_buffers,
00360                                       const bool keep_alive,
00361                                       const bool using_chunks)
00362     {
00363         // update message headers
00364         prepareHeadersForSend(keep_alive, using_chunks);
00365         // add first message line
00366         write_buffers.push_back(boost::asio::buffer(getFirstLine()));
00367         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00368         // append HTTP headers
00369         appendHeaders(write_buffers);
00370     }
00371 
00372 
00379     std::size_t send(TCPConnection& tcp_conn, boost::system::error_code& ec);
00380 
00387     std::size_t receive(TCPConnection& tcp_conn, boost::system::error_code& ec);
00388 
00392     void concatenateChunks(void);
00393 
00394 
00395 protected:
00396 
00403     inline void prepareHeadersForSend(const bool keep_alive,
00404                                       const bool using_chunks)
00405     {
00406         changeHeader(HEADER_CONNECTION, (keep_alive ? "Keep-Alive" : "close") );
00407         if (using_chunks) {
00408             if (getChunksSupported())
00409                 changeHeader(HEADER_TRANSFER_ENCODING, "chunked");
00410         } else if (! m_do_not_send_content_length) {
00411             changeHeader(HEADER_CONTENT_LENGTH, boost::lexical_cast<std::string>(getContentLength()));
00412         }
00413     }
00414 
00420     inline void appendHeaders(WriteBuffers& write_buffers) {
00421         // add HTTP headers
00422         for (Headers::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i) {
00423             write_buffers.push_back(boost::asio::buffer(i->first));
00424             write_buffers.push_back(boost::asio::buffer(HEADER_NAME_VALUE_DELIMITER));
00425             write_buffers.push_back(boost::asio::buffer(i->second));
00426             write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00427         }
00428         // add an extra CRLF to end HTTP headers
00429         write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00430     }
00431 
00440     template <typename DictionaryType>
00441     inline static const std::string& getValue(const DictionaryType& dict,
00442                                               const std::string& key)
00443     {
00444         typename DictionaryType::const_iterator i = dict.find(key);
00445         return ( (i==dict.end()) ? STRING_EMPTY : i->second );
00446     }
00447 
00457     template <typename DictionaryType>
00458     inline static void changeValue(DictionaryType& dict,
00459                                    const std::string& key, const std::string& value)
00460 
00461     {
00462         // retrieve all current values for key
00463         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00464             result_pair = dict.equal_range(key);
00465         if (result_pair.first == dict.end()) {
00466             // no values exist -> add a new key
00467             dict.insert(std::make_pair(key, value));
00468         } else {
00469             // set the first value found for the key to the new one
00470             result_pair.first->second = value;
00471             // remove any remaining values
00472             typename DictionaryType::iterator i;
00473             ++(result_pair.first);
00474             while (result_pair.first != result_pair.second) {
00475                 i = result_pair.first;
00476                 ++(result_pair.first);
00477                 dict.erase(i);
00478             }
00479         }
00480     }
00481 
00488     template <typename DictionaryType>
00489     inline static void deleteValue(DictionaryType& dict,
00490                                    const std::string& key)
00491     {
00492         std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00493             result_pair = dict.equal_range(key);
00494         if (result_pair.first != dict.end())
00495             dict.erase(result_pair.first, result_pair.second);
00496     }
00497 
00500     inline void clearFirstLine(void) const {
00501         if (! m_first_line.empty())
00502             m_first_line.clear();
00503     }
00504 
00506     virtual void updateFirstLine(void) const = 0;
00507 
00508 
00511     mutable std::string             m_first_line;
00512 
00513 
00514 private:
00515 
00517     static const boost::regex       REGEX_ICASE_CHUNKED;
00518 
00520     bool                            m_is_valid;
00521 
00523     bool                            m_is_chunked;
00524 
00526     bool                            m_chunks_supported;
00527 
00529     bool                            m_do_not_send_content_length;
00530 
00532     boost::asio::ip::address        m_remote_ip;
00533 
00535     boost::uint16_t                 m_version_major;
00536 
00538     boost::uint16_t                 m_version_minor;
00539 
00541     std::size_t                     m_content_length;
00542 
00544     boost::scoped_array<char>       m_content_buf;
00545 
00547     ChunkCache                      m_chunk_cache;
00548 
00550     Headers                         m_headers;
00551 
00553     CookieParams                    m_cookie_params;
00554 
00556     DataStatus                      m_status;
00557 
00559     bool                            m_has_missing_packets;
00560 
00562     bool                            m_has_data_after_missing;
00563 };
00564 
00565 
00566 }   // end namespace net
00567 }   // end namespace pion
00568 
00569 #endif

Generated on Fri Apr 30 14:48:53 2010 for pion-net by  doxygen 1.4.7