#include "HttpMessage.h" #include #include "htime.h" #include "hlog.h" #include "http_parser.h" // for http_parser_url #ifndef WITHOUT_HTTP_CONTENT // NOTE: json ignore number/string, 123/"123" std::string HttpMessage::GetString(const char* key, const std::string& defvalue) { switch (content_type) { case APPLICATION_JSON: { auto value = json[key]; if (value.is_string()) { return value; } else if (value.is_number()) { return hv::to_string(value); } else if (value.is_null()) { return "null"; } else if (value.is_boolean()) { bool b = value; return b ? "true" : "false"; } else { 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<> HV_EXPORT int64_t HttpMessage::Get(const char* key, int64_t defvalue) { if (content_type == APPLICATION_JSON) { auto value = json[key]; if (value.is_number()) { return value; } else if (value.is_string()) { std::string str = value; return atoll(str.c_str()); } else if (value.is_null()) { return 0; } else if (value.is_boolean()) { bool b = value; return b ? 1 : 0; } else { return defvalue; } } else { std::string str = GetString(key); return str.empty() ? defvalue : atoll(str.c_str()); } } template<> HV_EXPORT double HttpMessage::Get(const char* key, double defvalue) { if (content_type == APPLICATION_JSON) { auto value = json[key]; if (value.is_number()) { return value; } else if (value.is_string()) { std::string str = value; return atof(str.c_str()); } else if (value.is_null()) { return 0.0f; } else { return defvalue; } } else { std::string str = GetString(key); return str.empty() ? defvalue : atof(str.c_str()); } } template<> HV_EXPORT bool HttpMessage::Get(const char* key, bool defvalue) { if (content_type == APPLICATION_JSON) { auto value = json[key]; if (value.is_boolean()) { return value; } else if (value.is_string()) { std::string str = value; return getboolean(str.c_str()); } else if (value.is_null()) { return false; } else if (value.is_number()) { return value != 0; } else { 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 } void HttpMessage::DumpBody(std::string& str) { DumpBody(); const char* content = (const char*)Content(); int content_length = ContentLength(); if (content && content_length) { str.append(content, content_length); } } int HttpMessage::ParseBody() { if (body.size() == 0) { return -1; } FillContentType(); #ifndef WITHOUT_HTTP_CONTENT switch(content_type) { case APPLICATION_JSON: { std::string errmsg; int ret = parse_json(body.c_str(), json, errmsg); if (ret != 0 && errmsg.size() != 0) { hloge("%s", errmsg.c_str()); } return ret; } 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="); string strBoundary(boundary); strBoundary = trim_pairs(strBoundary, "\"\"\'\'"); return parse_multipart(body, form, strBoundary.c_str()); } 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(str); } return str; } void HttpRequest::DumpUrl() { if (url.size() != 0 && strstr(url.c_str(), "://") != NULL) { // have been complete url return; } std::string str; // scheme:// str = scheme; 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 scheme = url.substr(parser.field_data[UF_SCHEMA].off, parser.field_data[UF_SCHEMA].len); // host if (parser.field_set & (1<