📄 httpcommon.cc
字号:
#include <time.h>#ifdef WIN32#include <winsock2.h>#include <ws2tcpip.h>#define _WINSOCKAPI_#include <windows.h>#define SECURITY_WIN32#include <security.h>#endif#include "base64.h"#include "common.h"#include "cryptstring.h"#include "httpcommon.h"#include "socketaddress.h"#include "stringdigest.h"#include "stringutils.h"namespace utils_base {#ifdef WIN32extern const ConstantLabel SECURITY_ERRORS[];//const ConstantLabel SECURITY_ERRORS[] = {///yzxu_comment// KLABEL(0),// KLABEL(1),// ...// LASTLABEL//};#endif//////////////////////////////////////////////////////////////////////// Enum - TODO: expose globally later?//////////////////////////////////////////////////////////////////////bool find_string(size_t& index, const std::string& needle, const char* const haystack[], size_t max_index) { for (index=0; index<max_index; ++index) { if (_stricmp(needle.c_str(), haystack[index]) == 0) { return true; } } return false;}template<class E>struct Enum { static const char** Names; static size_t Size; static inline const char* Name(E val) { return Names[val]; } static inline bool Parse(E& val, const std::string& name) { size_t index; if (!find_string(index, name, Names, Size)) return false; val = static_cast<E>(index); return true; } E val; inline operator E&() { return val; } inline Enum& operator=(E rhs) { val = rhs; return *this; } inline const char* name() const { return Name(val); } inline bool assign(const std::string& name) { return Parse(val, name); } inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; }};#define ENUM(e,n) \ template<> const char** Enum<e>::Names = n; \ template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0])//////////////////////////////////////////////////////////////////////// HttpCommon//////////////////////////////////////////////////////////////////////static const char* kHttpVersions[HVER_LAST+1] = { "1.0", "1.1"};ENUM(HttpVersion, kHttpVersions);static const char* kHttpVerbs[HV_LAST+1] = { "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD"};ENUM(HttpVerb, kHttpVerbs);static const char* kHttpHeaders[HH_LAST+1] = { "Age", "Cache-Control", "Connection", "Content-Length", "Content-Range", "Content-Type", "Cookie", "Date", "ETag", "Expires", "Host", "If-Modified-Since", "If-None-Match", "Keep-Alive", "Last-Modified", "Location", "Proxy-Authenticate", "Proxy-Authorization", "Proxy-Connection", "Range", "Set-Cookie", "TE", "Trailers", "Transfer-Encoding", "Upgrade", "User-Agent", "WWW-Authenticate",};ENUM(HttpHeader, kHttpHeaders);const char* ToString(HttpVersion version) { return Enum<HttpVersion>::Name(version);}bool FromString(HttpVersion& version, const std::string& str) { return Enum<HttpVersion>::Parse(version, str);}const char* ToString(HttpVerb verb) { return Enum<HttpVerb>::Name(verb);}bool FromString(HttpVerb& verb, const std::string& str) { return Enum<HttpVerb>::Parse(verb, str);}const char* ToString(HttpHeader header) { return Enum<HttpHeader>::Name(header);}bool FromString(HttpHeader& header, const std::string& str) { return Enum<HttpHeader>::Parse(header, str);}bool HttpCodeHasBody(uint32 code) { return !HttpCodeIsInformational(code) && (code != HC_NO_CONTENT) || (code != HC_NOT_MODIFIED);}bool HttpCodeIsCacheable(uint32 code) { switch (code) { case HC_OK: case HC_NON_AUTHORITATIVE: case HC_PARTIAL_CONTENT: case HC_MULTIPLE_CHOICES: case HC_MOVED_PERMANENTLY: case HC_GONE: return true; default: return false; }}bool HttpHeaderIsEndToEnd(HttpHeader header) { switch (header) { case HH_CONNECTION: case HH_KEEP_ALIVE: case HH_PROXY_AUTHENTICATE: case HH_PROXY_AUTHORIZATION: //case HH_PROXY_CONNECTION:?? case HH_TE: case HH_TRAILERS: case HH_TRANSFER_ENCODING: case HH_UPGRADE: return false; default: return true; }}bool HttpHeaderIsCollapsible(HttpHeader header) { switch (header) { case HH_SET_COOKIE: case HH_PROXY_AUTHENTICATE: case HH_WWW_AUTHENTICATE: return false; default: return true; }}bool HttpShouldKeepAlive(const HttpData& data) { std::string connection; if ((data.hasHeader(HH_PROXY_CONNECTION, &connection) || data.hasHeader(HH_CONNECTION, &connection))) { return (_stricmp(connection.c_str(), "Keep-Alive") == 0); } return (data.version >= HVER_1_1);}namespace {inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) { if (pos >= len) return true; if (isspace(static_cast<unsigned char>(data[pos]))) return true; // The reason for this complexity is that some attributes may contain trailing // equal signs (like base64 tokens in Negotiate auth headers) if ((pos+1 < len) && (data[pos] == '=') && !isspace(static_cast<unsigned char>(data[pos+1])) && (data[pos+1] != '=')) { return true; } return false;}} // anonymous namespacevoid HttpParseAttributes(const char * data, size_t len, HttpAttributeList& attributes) { size_t pos = 0; while (true) { // Skip leading whitespace while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) { ++pos; } // End of attributes? if (pos >= len) return; // Find end of attribute name size_t start = pos; while (!IsEndOfAttributeName(pos, len, data)) { ++pos; } HttpAttribute attribute; attribute.first.assign(data + start, data + pos); // Attribute has value? if ((pos < len) && (data[pos] == '=')) { ++pos; // Skip '=' // Check if quoted value if ((pos < len) && (data[pos] == '"')) { while (++pos < len) { if (data[pos] == '"') { ++pos; break; } if ((data[pos] == '\\') && (pos + 1 < len)) ++pos; attribute.second.append(1, data[pos]); } } else { while ((pos < len) && !isspace(static_cast<unsigned char>(data[pos])) && (data[pos] != ',')) { attribute.second.append(1, data[pos++]); } } } attributes.push_back(attribute); if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ',' }}bool HttpHasAttribute(const HttpAttributeList& attributes, const std::string& name, std::string* value) { for (HttpAttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { if (it->first == name) { if (value) { *value = it->second; } return true; } } return false;}bool HttpHasNthAttribute(HttpAttributeList& attributes, size_t index, std::string* name, std::string* value) { if (index >= attributes.size()) return false; if (name) *name = attributes[index].first; if (value) *value = attributes[index].second; return true;}bool HttpDateToSeconds(const std::string& date, unsigned long* seconds) { const char* const kTimeZones[] = { "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT", "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y" }; const int kTimeZoneOffsets[] = { 0, 0, -5, -4, -6, -5, -7, -6, -8, -7, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; ASSERT(NULL != seconds); struct tm tval; memset(&tval, 0, sizeof(tval)); char month[4], zone[6]; memset(month, 0, sizeof(month)); memset(zone, 0, sizeof(zone)); if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c", &tval.tm_mday, month, &tval.tm_year, &tval.tm_hour, &tval.tm_min, &tval.tm_sec, &zone)) { return false; } switch (toupper(month[2])) { case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break; case 'B': tval.tm_mon = 1; break; case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break; case 'Y': tval.tm_mon = 4; break; case 'L': tval.tm_mon = 6; break; case 'G': tval.tm_mon = 7; break; case 'P': tval.tm_mon = 8; break; case 'T': tval.tm_mon = 9; break; case 'V': tval.tm_mon = 10; break; case 'C': tval.tm_mon = 11; break; } tval.tm_year -= 1900; unsigned long gmt, non_gmt = (unsigned long)mktime(&tval); if ((zone[0] == '+') || (zone[0] == '-')) { if (!isdigit(zone[1]) || !isdigit(zone[2]) || !isdigit(zone[3]) || !isdigit(zone[4])) { return false; } int hours = (zone[1] - '0') * 10 + (zone[2] - '0'); int minutes = (zone[3] - '0') * 10 + (zone[4] - '0'); int offset = (hours * 60 + minutes) * 60; gmt = non_gmt + (zone[0] == '+') ? offset : -offset; } else { size_t zindex; if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) { return false; } gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60; }#ifdef OSX tm *tm_for_timezone = localtime((time_t *)&gmt); *seconds = gmt + tm_for_timezone->tm_gmtoff;#else *seconds = gmt - timezone;#endif return true;}//////////////////////////////////////////////////////////////////////// HttpData//////////////////////////////////////////////////////////////////////voidHttpData::clear(bool release_document) { if (release_document) { document.reset(); } m_headers.clear();}voidHttpData::changeHeader(const std::string& name, const std::string& value, HeaderCombine combine) { if (combine == HC_AUTO) { HttpHeader header; // Unrecognized headers are collapsible combine = !FromString(header, name) || HttpHeaderIsCollapsible(header) ? HC_YES : HC_NO; } else if (combine == HC_REPLACE) { m_headers.erase(name); combine = HC_NO; } // At this point, combine is one of (YES, NO, NEW) if (combine != HC_NO) { HeaderMap::iterator it = m_headers.find(name); if (it != m_headers.end()) { if (combine == HC_YES) { it->second.append(","); it->second.append(value); } return; } } m_headers.insert(HeaderMap::value_type(name, value));}voidHttpData::clearHeader(const std::string& name) { m_headers.erase(name);}boolHttpData::hasHeader(const std::string& name, std::string* value) const { HeaderMap::const_iterator it = m_headers.find(name); if (it == m_headers.end()) { return false; } else if (value) { *value = it->second; } return true;}voidHttpData::setContent(const std::string& content_type, StreamInterface* document) { ASSERT(document != NULL); this->document.reset(document); setHeader(HH_CONTENT_TYPE, content_type); size_t content_length = 0; if (this->document->GetSize(&content_length)) { char buffer[32]; sprintfn(buffer, sizeof(buffer), "%d", content_length); setHeader(HH_CONTENT_LENGTH, buffer); } else { setHeader(HH_TRANSFER_ENCODING, "chunked"); }}//// HttpRequestData//voidHttpRequestData::clear(bool release_document) { HttpData::clear(release_document); verb = HV_GET; path.clear();}size_tHttpRequestData::formatLeader(char* buffer, size_t size) { ASSERT(path.find(' ') == std::string::npos); return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(), path.data(), ToString(version));}HttpErrorHttpRequestData::parseLeader(const char* line, size_t len) { UNUSED(len); uint32 vmajor, vminor; int vend, dstart, dend; if ((sscanf(line, "%*s%n %n%*s%n HTTP/%lu.%lu", &vend, &dstart, &dend, &vmajor, &vminor) != 2) || (vmajor != 1)) { return HE_PROTOCOL; } if (vminor == 0) { version = HVER_1_0; } else if (vminor == 1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -