From c753f200cd80d756992f3524aac763d00b2438c9 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 10 Jan 2024 21:45:37 +0800 Subject: [PATCH] Use new way to detect eof --- http_client.cpp | 117 +++++++++++++++++++++++++++++++++++++++--------- http_client.h | 4 ++ 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/http_client.cpp b/http_client.cpp index 05e532b..3f30581 100644 --- a/http_client.cpp +++ b/http_client.cpp @@ -6,6 +6,7 @@ #include "err.h" #include "str_util.h" +#include "urlparse.h" #include #include @@ -40,6 +41,33 @@ const char* AIException::what() { #endif } +std::string getDefaultAcceptEncoding() { + std::string ae = ""; +#if HAVE_ZLIB + ae += "deflate, gzip"; +#endif + return ae; +} + +void make_sure_http_client_inited() { +#if _WIN32 + if (!inited) { + WSAStartup(MAKEWORD(2, 2), &wsaData); + inited = true; + } +#endif +#if HAVE_OPENSSL + if (!ssl_inited) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_library_init(); +#else + SSL_load_error_strings(); +#endif + ssl_inited = true; + } +#endif +} + std::string decodeURIComponent(std::string str) { std::string re; for (size_t i = 0; i < str.length(); i++) { @@ -329,22 +357,7 @@ HttpClient::HttpClient(std::string host) { } else { this->port = this->https ? "https" : "http"; } -#if _WIN32 - if (!inited) { - WSAStartup(MAKEWORD(2, 2), &wsaData); - inited = true; - } -#endif -#if HAVE_OPENSSL - if (!ssl_inited) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_library_init(); -#else - SSL_load_error_strings(); -#endif - ssl_inited = true; - } -#endif + make_sure_http_client_inited(); if (is_number_port) { this->headers["Host"] = this->host + ":" + this->port; } else { @@ -352,11 +365,7 @@ HttpClient::HttpClient(std::string host) { } this->headers["User-Agent"] = "simple-http-client"; this->headers["Accept"] = "*/*"; - std::string ae = ""; -#if HAVE_ZLIB - ae += "deflate, gzip"; -#endif - this->headers["Accept-Encoding"] = ae; + this->headers["Accept-Encoding"] = getDefaultAcceptEncoding(); } Request HttpClient::request(std::string path, std::string method) { @@ -459,6 +468,9 @@ Response::Response(Socket socket): socket(socket) { bool Response::pullData() { if (!this->buff.empty()) return false; this->buff = this->socket.recv(1024); + if (this->buff.empty()) { + this->eof = true; + } return this->buff.empty(); } @@ -554,6 +566,7 @@ std::string Response::read() { std::string data; size_t size = -1; auto line = this->readLine(); + if (this->eof) return ""; if (sscanf(line.c_str(), "%zx", &size) != 1) { throw std::runtime_error("Invalid chunk size"); } @@ -569,6 +582,9 @@ std::string Response::read() { throw std::runtime_error("Chunk size != data length"); } this->readLine(); + if (!osize) { + this->eof = true; + } #if HAVE_ZLIB if (this->gzip || this->deflate) { return this->inflate(data); @@ -591,7 +607,7 @@ std::string Response::read() { std::string Response::readAll() { std::string data; auto d = this->read(); - while (!d.empty()) { + while (!this->eof) { data += d; d = this->read(); } @@ -629,3 +645,60 @@ std::string Response::inflate(std::string data) { return re; } #endif + +Request::Request(std::string url, std::string method, HttpClientOptions options, HeaderMap headers) { + auto u = urlparse(url.c_str(), nullptr, 1); + if (!u) { + throw std::runtime_error("Invalid URL"); + } + this->path = u->path; + std::string params = u->params, query = u->query, scheme = u->scheme, netloc = u->netloc; + free_url_parse_result(u); + if (!params.empty()) { + this->path += ";" + params; + } + if (!query.empty()) { + this->path += "?" + query; + } + bool is_number_port = false; + if (!scheme.empty()) { + if (!cstr_stricmp(scheme.c_str(), "http")) { + this->https = false; + } else if (!cstr_stricmp(scheme.c_str(), "https")) { + this->https = true; + } else { + throw std::runtime_error("Unspported protocol"); + } + } + auto pos = netloc.find(":"); + if (pos != std::string::npos) { + this->port = netloc.substr(pos + 1); + this->host = netloc.substr(0, pos); + is_number_port = true; + } else { + this->host = netloc; + this->port = this->https ? "https" : "http"; + } + this->method = method; + this->options = options; + if (is_number_port) { + this->headers["Host"] = this->host + ":" + this->port; + } else { + this->headers["Host"] = this->host; + } + this->headers["User-Agent"] = "simple-http-client"; + this->headers["Accept"] = "*/*"; + this->headers["Accept-Encoding"] = getDefaultAcceptEncoding(); + for (auto& header : headers) { + this->headers[header.first] = header.second; + } + make_sure_http_client_inited(); +} + +HttpBody* Request::getBody() { + return this->body; +} + +bool Response::isEof() { + return this->eof; +} diff --git a/http_client.h b/http_client.h index d7c4710..c2e43f5 100644 --- a/http_client.h +++ b/http_client.h @@ -137,10 +137,12 @@ typedef struct Response Response; class Request { public: Request(std::string host, std::string port, bool https, std::string path, std::string method, HeaderMap headers, HttpClientOptions options); + Request(std::string url, std::string method, HttpClientOptions options = {}, HeaderMap headers = {}); ~Request(); Response send(); HeaderMap headers; HttpClientOptions options; + HttpBody* getBody(); void setBody(HttpBody* body); std::string host; std::string port; @@ -161,6 +163,7 @@ public: std::string reason; std::string read(); std::string readAll(); + bool isEof(); private: std::string readLine(); void parseHeader(); @@ -168,6 +171,7 @@ private: bool pullData(); bool headerParsed = false; bool chunked = false; + bool eof = false; #if HAVE_ZLIB bool gzip = false; bool deflate = false;