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