#include "HttpMessage.h" #include #include "htime.h" #include "http_parser.h" // for http_parser_url #ifndef WITHOUT_HTTP_CONTENT // NOTE: json ignore number/string, 123/"123" using nlohmann::detail::value_t; std::string HttpMessage::GetString(const char* key, const std::string& defvalue) { switch (content_type) { case APPLICATION_JSON: { auto iter = json.find(key); if (iter != json.end()) { switch (iter->type()) { case value_t::null: return "null"; case value_t::string: return *iter; case value_t::boolean: { bool b = *iter; return b ? "true" : "false"; } case value_t::number_integer: { std::int64_t n = *iter; return hv::to_string(n); } case value_t::number_unsigned: { std::uint64_t n = *iter; return hv::to_string(n); } case value_t::number_float: { double f = *iter; return hv::to_string(f); } default: return defvalue; } } } break; case MULTIPART_FORM_DATA: { auto iter = form.find(key); if (iter != form.end()) { return iter->second.content; } } break; case APPLICATION_URLENCODED: { auto iter = kv.find(key); if (iter != kv.end()) { return iter->second; } } break; default: break; } return defvalue; } template<> int64_t HttpMessage::Get(const char* key, int64_t defvalue) { if (content_type == APPLICATION_JSON) { auto iter = json.find(key); if (iter != json.end()) { switch (iter->type()) { return *iter; case value_t::boolean: { bool b = *iter; return b; } case value_t::number_integer: { std::int64_t n = *iter; return n; } case value_t::number_unsigned: { std::uint64_t n = *iter; return n; } case value_t::number_float: { double f = *iter; return f; } case value_t::string: { std::string str = *iter; return atoll(str.c_str()); } default: return defvalue; } } return defvalue; } else { std::string str = GetString(key); return str.empty() ? defvalue : atoll(str.c_str()); } } template<> double HttpMessage::Get(const char* key, double defvalue) { if (content_type == APPLICATION_JSON) { auto iter = json.find(key); if (iter != json.end()) { switch (iter->type()) { return *iter; case value_t::boolean: { bool b = *iter; return b; } case value_t::number_integer: { std::int64_t n = *iter; return n; } case value_t::number_unsigned: { std::uint64_t n = *iter; return n; } case value_t::number_float: { double f = *iter; return f; } case value_t::string: { std::string str = *iter; return atof(str.c_str()); } default: return defvalue; } } return defvalue; } else { std::string str = GetString(key); return str.empty() ? defvalue : atof(str.c_str()); } } template<> bool HttpMessage::Get(const char* key, bool defvalue) { if (content_type == APPLICATION_JSON) { auto iter = json.find(key); if (iter != json.end()) { switch (iter->type()) { return *iter; case value_t::boolean: { bool b = *iter; return b; } case value_t::number_integer: { std::int64_t n = *iter; return n; } case value_t::number_unsigned: { std::uint64_t n = *iter; return n; } case value_t::number_float: { double f = *iter; return f; } case value_t::string: { std::string str = *iter; return getboolean(str.c_str()); } default: return defvalue; } } return defvalue; } else { std::string str = GetString(key); return str.empty() ? defvalue : getboolean(str.c_str()); } } bool HttpMessage::GetBool(const char* key, bool defvalue) { return Get(key, defvalue); } int64_t HttpMessage::GetInt(const char* key, int64_t defvalue) { return Get(key, defvalue); } double HttpMessage::GetFloat(const char* key, double defvalue) { return Get(key, defvalue); } #endif void HttpMessage::FillContentType() { auto iter = headers.find("Content-Type"); if (iter != headers.end()) { content_type = http_content_type_enum(iter->second.c_str()); goto append; } #ifndef WITHOUT_HTTP_CONTENT if (content_type == CONTENT_TYPE_NONE) { if (json.size() != 0) { content_type = APPLICATION_JSON; } else if (form.size() != 0) { content_type = MULTIPART_FORM_DATA; } else if (kv.size() != 0) { content_type = X_WWW_FORM_URLENCODED; } else if (body.size() != 0) { content_type = TEXT_PLAIN; } } #endif if (content_type != CONTENT_TYPE_NONE) { headers["Content-Type"] = http_content_type_str(content_type); } append: #ifndef WITHOUT_HTTP_CONTENT if (content_type == MULTIPART_FORM_DATA) { auto iter = headers.find("Content-Type"); if (iter != headers.end()) { const char* boundary = strstr(iter->second.c_str(), "boundary="); if (boundary == NULL) { boundary = DEFAULT_MULTIPART_BOUNDARY; iter->second += "; boundary="; iter->second += boundary; } } } #endif return; } void HttpMessage::FillContentLength() { auto iter = headers.find("Content-Length"); if (iter != headers.end()) { content_length = atoi(iter->second.c_str()); } if (iter == headers.end() || content_length == 0) { if (content_length == 0) { content_length = body.size(); } if (content_length == 0) { DumpBody(); content_length = body.size(); } char sz[64]; snprintf(sz, sizeof(sz), "%d", content_length); headers["Content-Length"] = sz; } } void HttpMessage::DumpHeaders(std::string& str) { FillContentType(); FillContentLength(); for (auto& header: headers) { // http2 :method :path :scheme :authority :status if (*str.c_str() != ':') { // %s: %s\r\n str += header.first; str += ": "; str += header.second; str += "\r\n"; } } } void HttpMessage::DumpBody() { if (body.size() != 0) { return; } FillContentType(); #ifndef WITHOUT_HTTP_CONTENT switch(content_type) { case APPLICATION_JSON: body = dump_json(json); break; case MULTIPART_FORM_DATA: { auto iter = headers.find("Content-Type"); if (iter == headers.end()) { return; } const char* boundary = strstr(iter->second.c_str(), "boundary="); if (boundary == NULL) { return; } boundary += strlen("boundary="); body = dump_multipart(form, boundary); } break; case X_WWW_FORM_URLENCODED: body = dump_query_params(kv); break; default: // nothing to do break; } #endif } int HttpMessage::ParseBody() { if (body.size() == 0) { return -1; } FillContentType(); #ifndef WITHOUT_HTTP_CONTENT switch(content_type) { case APPLICATION_JSON: return parse_json(body.c_str(), json); case MULTIPART_FORM_DATA: { auto iter = headers.find("Content-Type"); if (iter == headers.end()) { return false; } const char* boundary = strstr(iter->second.c_str(), "boundary="); if (boundary == NULL) { return false; } boundary += strlen("boundary="); return parse_multipart(body, form, boundary); } case X_WWW_FORM_URLENCODED: return parse_query_params(body.c_str(), kv); default: // nothing to do return 0; } #endif return 0; } std::string HttpMessage::Dump(bool is_dump_headers, bool is_dump_body) { std::string str; if (is_dump_headers) { DumpHeaders(str); } str += "\r\n"; if (is_dump_body) { DumpBody(); if (ContentLength() != 0) { str.insert(str.size(), (const char*)Content(), ContentLength()); } } return str; } void HttpRequest::DumpUrl() { if (url.size() != 0 && strncmp(url.c_str(), "http", 4) == 0) { // have been complete url return; } std::string str; // scheme:// str += "http"; if (https) str += 's'; str += "://"; // host:port char c_str[256] = {0}; if (url.size() != 0 && *url.c_str() != '/') { // url begin with host str += url; } else { if (port == 0 || port == DEFAULT_HTTP_PORT || port == DEFAULT_HTTPS_PORT) { str += host; } else { snprintf(c_str, sizeof(c_str), "%s:%d", host.c_str(), port); str += c_str; } } // /path if (url.size() != 0 && *url.c_str() == '/') { // url begin with path str += url; } else if (path.size() > 1 && *path.c_str() == '/') { str += path; } else if (url.size() == 0) { str += '/'; } // ?query if (strchr(str.c_str(), '?') == NULL && query_params.size() != 0) { str += '?'; str += dump_query_params(query_params); } url = str; } void HttpRequest::ParseUrl() { DumpUrl(); http_parser_url parser; http_parser_url_init(&parser); http_parser_parse_url(url.c_str(), url.size(), 0, &parser); // scheme https = !strncmp(url.c_str(), "https", 5); // host if (parser.field_set & (1<