00001
00002
00003
00004
00005
00006
00007
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 {
00027 namespace net {
00028
00029
00030
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,
00072 STATUS_TRUNCATED,
00073 STATUS_PARTIAL,
00074 STATUS_OK
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
00314 m_is_chunked = boost::regex_match(i->second, REGEX_ICASE_CHUNKED);
00315
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
00380 prepareHeadersForSend(keep_alive, using_chunks);
00381
00382 write_buffers.push_back(boost::asio::buffer(getFirstLine()));
00383 write_buffers.push_back(boost::asio::buffer(STRING_CRLF));
00384
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
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
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
00511 std::pair<typename DictionaryType::iterator, typename DictionaryType::iterator>
00512 result_pair = dict.equal_range(key);
00513 if (result_pair.first == dict.end()) {
00514
00515 dict.insert(std::make_pair(key, value));
00516 } else {
00517
00518 result_pair.first->second = value;
00519
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 }
00615 }
00616
00617 #endif