同步操作将从 libhv/libhv 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#include "HttpMessage.h"#include <string.h>#include "htime.h"#include "hlog.h"#include "hurl.h"#include "base64.h"using namespace hv;http_headers DefaultHeaders;http_body NoBody;HttpCookie NoCookie;char HttpMessage::s_date[32] = {0};HttpCookie::HttpCookie() {init();}void HttpCookie::init() {max_age = 0;secure = false;httponly = false;samesite = Default;priority = NotSet;}void HttpCookie::reset() {init();name.clear();value.clear();domain.clear();path.clear();expires.clear();kv.clear();}bool HttpCookie::parse(const std::string& str) {std::stringstream ss;ss << str;std::string line;std::string::size_type pos;std::string key;std::string val;reset();while (std::getline(ss, line, ';')) {pos = line.find_first_of('=');if (pos != std::string::npos) {key = trim(line.substr(0, pos));val = trim(line.substr(pos+1));const char* pkey = key.c_str();if (stricmp(pkey, "Domain") == 0) {domain = val;}else if (stricmp(pkey, "Path") == 0) {path = val;}else if (stricmp(pkey, "Expires") == 0) {expires = val;}else if (stricmp(pkey, "Max-Age") == 0) {max_age = atoi(val.c_str());}else if (stricmp(pkey, "SameSite") == 0) {samesite = stricmp(val.c_str(), "Strict") == 0 ? HttpCookie::SameSite::Strict :stricmp(val.c_str(), "Lax") == 0 ? HttpCookie::SameSite::Lax :stricmp(val.c_str(), "None") == 0 ? HttpCookie::SameSite::None :HttpCookie::SameSite::Default;}else if (stricmp(pkey, "Priority") == 0) {priority = stricmp(val.c_str(), "Low") == 0 ? HttpCookie::Priority::Low :stricmp(val.c_str(), "Medium") == 0 ? HttpCookie::Priority::Medium :stricmp(val.c_str(), "High") == 0 ? HttpCookie::Priority::High :HttpCookie::Priority::NotSet ;}else {if (name.empty()) {name = key;value = val;}kv[key] = val;}} else {key = trim(line);const char* pkey = key.c_str();if (stricmp(pkey, "Secure") == 0) {secure = true;}else if (stricmp(pkey, "HttpOnly") == 0) {httponly = true;}else {hlogw("Unrecognized key '%s'", key.c_str());}}}return !name.empty();}std::string HttpCookie::dump() const {assert(!name.empty() || !kv.empty());std::string res;if (!name.empty()) {res = name;res += "=";res += value;}for (auto& pair : kv) {if (pair.first == name) continue;if (!res.empty()) res += "; ";res += pair.first;res += "=";res += pair.second;}if (!domain.empty()) {res += "; Domain=";res += domain;}if (!path.empty()) {res += "; Path=";res += path;}if (max_age > 0) {res += "; Max-Age=";res += hv::to_string(max_age);} else if (!expires.empty()) {res += "; Expires=";res += expires;}if (samesite != HttpCookie::SameSite::Default) {res += "; SameSite=";res += samesite == HttpCookie::SameSite::Strict ? "Strict" :samesite == HttpCookie::SameSite::Lax ? "Lax" :"None" ;}if (priority != HttpCookie::Priority::NotSet) {res += "; Priority=";res += priority == HttpCookie::Priority::Low ? "Low" :priority == HttpCookie::Priority::Medium ? "Medium" :"High" ;}if (secure) {res += "; Secure";}if (httponly) {res += "; HttpOnly";}return res;}HttpMessage::HttpMessage() {type = HTTP_BOTH;Init();}HttpMessage::~HttpMessage() {}void HttpMessage::Init() {http_major = 1;http_minor = 1;content = NULL;content_length = 0;content_type = CONTENT_TYPE_NONE;}void HttpMessage::Reset() {Init();headers.clear();cookies.clear();body.clear();#ifndef WITHOUT_HTTP_CONTENTjson.clear();form.clear();kv.clear();#endif}#ifndef WITHOUT_HTTP_CONTENT// NOTE: json ignore number/string, 123/"123"std::string HttpMessage::GetString(const char* key, const std::string& defvalue) {switch (ContentType()) {case APPLICATION_JSON:{if (json.empty()) {ParseBody();}if (!json.is_object()) {return defvalue;}const auto& value = json[key];if (value.is_string()) {return value;}else if (value.is_number()) {return hv::to_string(value);}else if (value.is_boolean()) {bool b = value;return b ? "true" : "false";}else {return defvalue;}}break;case MULTIPART_FORM_DATA:{if (form.empty()) {ParseBody();}auto iter = form.find(key);if (iter != form.end()) {return iter->second.content;}}break;case APPLICATION_URLENCODED:{if (kv.empty()) {ParseBody();}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 (ContentType() == APPLICATION_JSON) {if (json.empty()) {ParseBody();}if (!json.is_object()) {return defvalue;}const 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_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 int HttpMessage::Get(const char* key, int defvalue) {return (int)Get<int64_t>(key, defvalue);}template<>HV_EXPORT double HttpMessage::Get(const char* key, double defvalue) {if (ContentType() == APPLICATION_JSON) {if (json.empty()) {ParseBody();}if (!json.is_object()) {return defvalue;}const 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 {return defvalue;}}else {std::string str = GetString(key);return str.empty() ? defvalue : atof(str.c_str());}}template<>HV_EXPORT float HttpMessage::Get(const char* key, float defvalue) {return (float)Get<double>(key, defvalue);}template<>HV_EXPORT bool HttpMessage::Get(const char* key, bool defvalue) {if (ContentType() == APPLICATION_JSON) {if (json.empty()) {ParseBody();}if (!json.is_object()) {return defvalue;}const auto& value = json[key];if (value.is_boolean()) {return value;}else if (value.is_string()) {std::string str = value;return hv_getboolean(str.c_str());}else if (value.is_number()) {return value != 0;}else {return defvalue;}}else {std::string str = GetString(key);return str.empty() ? defvalue : hv_getboolean(str.c_str());}}bool HttpMessage::GetBool(const char* key, bool defvalue) {return Get<bool>(key, defvalue);}int64_t HttpMessage::GetInt(const char* key, int64_t defvalue) {return Get<int64_t>(key, defvalue);}double HttpMessage::GetFloat(const char* key, double defvalue) {return Get<double>(key, defvalue);}#endifvoid 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_CONTENTif (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;}}#endifif (content_type != CONTENT_TYPE_NONE) {headers["Content-Type"] = http_content_type_str(content_type);}append:#ifndef WITHOUT_HTTP_CONTENTif (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;}}}#endifreturn;}void HttpMessage::FillContentLength() {auto iter = headers.find("Content-Length");if (iter != headers.end()) {content_length = atoll(iter->second.c_str());}if (content_length == 0) {DumpBody();content_length = body.size();}if (iter == headers.end() && !IsChunked() && content_type != TEXT_EVENT_STREAM) {if (content_length != 0 || type == HTTP_RESPONSE) {headers["Content-Length"] = hv::to_string(content_length);}}}bool HttpMessage::IsChunked() {auto iter = headers.find("Transfer-Encoding");return iter != headers.end() && stricmp(iter->second.c_str(), "chunked") == 0;}bool HttpMessage::IsKeepAlive() {bool keepalive = true;auto iter = headers.find("connection");if (iter != headers.end()) {const char* keepalive_value = iter->second.c_str();if (stricmp(keepalive_value, "keep-alive") == 0) {keepalive = true;}else if (stricmp(keepalive_value, "close") == 0) {keepalive = false;}else if (stricmp(keepalive_value, "upgrade") == 0) {keepalive = true;}}else if (http_major == 1 && http_minor == 0) {keepalive = false;}return keepalive;}bool HttpMessage::IsUpgrade() {auto iter = headers.find("upgrade");return iter != headers.end();}// headersvoid HttpMessage::SetHeader(const char* key, const std::string& value) {headers[key] = value;}std::string HttpMessage::GetHeader(const char* key, const std::string& defvalue) {auto iter = headers.find(key);return iter == headers.end() ? defvalue : iter->second;}// cookiesvoid HttpMessage::AddCookie(const HttpCookie& cookie) {cookies.push_back(cookie);}const HttpCookie& HttpMessage::GetCookie(const std::string& name) {for (auto iter = cookies.begin(); iter != cookies.end(); ++iter) {if (iter->name == name) {return *iter;}auto kv_iter = iter->kv.find(name);if (kv_iter != iter->kv.end()) {iter->name = name;iter->value = kv_iter->second;return *iter;}}return NoCookie;}// bodyvoid HttpMessage::SetBody(const std::string& body) {this->body = body;}const std::string& HttpMessage::Body() {return this->body;}void HttpMessage::DumpHeaders(std::string& str) {FillContentType();FillContentLength();// headersfor (auto& header: headers) {// http2 :method :path :scheme :authority :statusif (*str.c_str() != ':') {// %s: %s\r\nstr += header.first;str += ": ";// fix CVE-2023-26148// if the value has \r\n, translate to \\r\\nif (header.second.find("\r") != std::string::npos ||header.second.find("\n") != std::string::npos) {std::string newStr = "";for (size_t i = 0; i < header.second.size(); ++i) {if (header.second[i] == '\r') {newStr += "\\r";} else if (header.second[i] == '\n') {newStr += "\\n";} else {newStr += header.second[i];}}str += newStr;} else {str += header.second;}str += "\r\n";}}// cookiesconst char* cookie_field = "Cookie";if (type == HTTP_RESPONSE) {cookie_field = "Set-Cookie";}for (auto& cookie : cookies) {str += cookie_field;str += ": ";str += cookie.dump();str += "\r\n";}}void HttpMessage::DumpBody() {if (body.size() != 0) {return;}FillContentType();#ifndef WITHOUT_HTTP_CONTENTswitch(content_type) {case APPLICATION_JSON:body = dump_json(json, 2);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 dobreak;}#endif}void HttpMessage::DumpBody(std::string& str) {DumpBody();const char* content = (const char*)Content();size_t 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_CONTENTswitch(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 -1;}const char* boundary = strstr(iter->second.c_str(), "boundary=");if (boundary == NULL) {return -1;}boundary += strlen("boundary=");std::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 doreturn 0;}#endifreturn 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;}HttpRequest::HttpRequest() : HttpMessage() {type = HTTP_REQUEST;Init();}void HttpRequest::Init() {headers["User-Agent"] = DEFAULT_HTTP_USER_AGENT;headers["Accept"] = "*/*";method = HTTP_GET;scheme = "http";host = "127.0.0.1";port = DEFAULT_HTTP_PORT;path = "/";timeout = DEFAULT_HTTP_TIMEOUT;connect_timeout = DEFAULT_HTTP_CONNECT_TIMEOUT;retry_count = DEFAULT_HTTP_FAIL_RETRY_COUNT;retry_delay = DEFAULT_HTTP_FAIL_RETRY_DELAY;redirect = 1;proxy = 0;cancel = 0;}void HttpRequest::Reset() {HttpMessage::Reset();Init();url.clear();query_params.clear();}void HttpRequest::DumpUrl() {std::string str;if (url.size() != 0 &&*url.c_str() != '/' &&strstr(url.c_str(), "://") != NULL) {// have been complete urlgoto query;}// scheme://str = scheme;str += "://";// host:portif (url.size() != 0 && *url.c_str() != '/') {// url begin with hoststr += url;}else {if (port == 0 ||port == DEFAULT_HTTP_PORT ||port == DEFAULT_HTTPS_PORT) {str += Host();}else {str += hv::asprintf("%s:%d", host.c_str(), port);}}// /pathif (url.size() != 0 && *url.c_str() == '/') {// url begin with pathstr += url;}else if (path.size() > 1 && *path.c_str() == '/') {str += path;}else if (url.size() == 0) {str += '/';}url = str;query:// ?queryif (strchr(url.c_str(), '?') == NULL &&query_params.size() != 0) {url += '?';url += dump_query_params(query_params);}}void HttpRequest::ParseUrl() {DumpUrl();hurl_t parser;hv_parse_url(&parser, url.c_str());// schemestd::string scheme_ = url.substr(parser.fields[HV_URL_SCHEME].off, parser.fields[HV_URL_SCHEME].len);// hoststd::string host_(host);if (parser.fields[HV_URL_HOST].len > 0) {host_ = url.substr(parser.fields[HV_URL_HOST].off, parser.fields[HV_URL_HOST].len);}// portint port_ = parser.port ? parser.port : strcmp(scheme_.c_str(), "https") ? DEFAULT_HTTP_PORT : DEFAULT_HTTPS_PORT;if (!proxy) {scheme = scheme_;host = host_;port = port_;}FillHost(host_.c_str(), port_);// pathif (parser.fields[HV_URL_PATH].len > 0) {path = url.substr(parser.fields[HV_URL_PATH].off);}// queryif (parser.fields[HV_URL_QUERY].len > 0) {parse_query_params(url.c_str()+parser.fields[HV_URL_QUERY].off, query_params);}}std::string HttpRequest::Path() {const char* s = path.c_str();const char* e = s;while (*e && *e != '?' && *e != '#') ++e;return HUrl::unescape(std::string(s, e));}void HttpRequest::FillHost(const char* host, int port) {if (headers.find("Host") == headers.end()) {if (port == 0 ||port == DEFAULT_HTTP_PORT ||port == DEFAULT_HTTPS_PORT) {headers["Host"] = host;} else {headers["Host"] = asprintf("%s:%d", host, port);}}}void HttpRequest::SetHost(const char* host, int port) {this->host = host;this->port = port;FillHost(host, port);}void HttpRequest::SetProxy(const char* host, int port) {this->scheme = "http";this->host = host;this->port = port;proxy = 1;}void HttpRequest::SetAuth(const std::string& auth) {SetHeader("Authorization", auth);}void HttpRequest::SetBasicAuth(const std::string& username, const std::string& password) {std::string strAuth = hv::asprintf("%s:%s", username.c_str(), password.c_str());std::string base64Auth = hv::Base64Encode((const unsigned char*)strAuth.c_str(), strAuth.size());SetAuth(std::string("Basic ") + base64Auth);}void HttpRequest::SetBearerTokenAuth(const std::string& token) {SetAuth(std::string("Bearer ") + token);}std::string HttpRequest::Dump(bool is_dump_headers, bool is_dump_body) {ParseUrl();std::string str;str.reserve(MAX(512, path.size() + 128));// GET / HTTP/1.1\r\nstr = asprintf("%s %s HTTP/%d.%d\r\n",http_method_str(method),proxy ? url.c_str() : path.c_str(),(int)http_major, (int)http_minor);if (is_dump_headers) {DumpHeaders(str);}str += "\r\n";if (is_dump_body) {DumpBody(str);}return str;}void HttpRequest::SetRange(long from, long to) {SetHeader("Range", hv::asprintf("bytes=%ld-%ld", from, to));}bool HttpRequest::GetRange(long& from, long& to) {auto iter = headers.find("Range");if (iter != headers.end()) {sscanf(iter->second.c_str(), "bytes=%ld-%ld", &from, &to);return true;}from = to = 0;return false;}HttpResponse::HttpResponse() : HttpMessage() {type = HTTP_RESPONSE;Init();}void HttpResponse::Init() {status_code = HTTP_STATUS_OK;}void HttpResponse::Reset() {HttpMessage::Reset();Init();}std::string HttpResponse::Dump(bool is_dump_headers, bool is_dump_body) {char c_str[256] = {0};std::string str;str.reserve(512);// HTTP/1.1 200 OK\r\nsnprintf(c_str, sizeof(c_str), "HTTP/%d.%d %d %s\r\n",(int)http_major, (int)http_minor,(int)status_code, http_status_str(status_code));str = c_str;if (is_dump_headers) {if (*s_date) {headers["Date"] = s_date;} else {headers["Date"] = gmtime_fmt(time(NULL), c_str);}DumpHeaders(str);}str += "\r\n";if (is_dump_body) {DumpBody(str);}return str;}void HttpResponse::SetRange(long from, long to, long total) {SetHeader("Content-Range", hv::asprintf("bytes %ld-%ld/%ld", from, to, total));}bool HttpResponse::GetRange(long& from, long& to, long& total) {auto iter = headers.find("Content-Range");if (iter != headers.end()) {sscanf(iter->second.c_str(), "bytes %ld-%ld/%ld", &from, &to, &total);return true;}from = to = total = 0;return false;}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。