mirror of
https://github.com/lifegpc/c-utils.git
synced 2026-07-08 01:30:39 +08:00
Update
This commit is contained in:
134
http_client.cpp
134
http_client.cpp
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "inttypes.h"
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
static bool inited = false;
|
static bool inited = false;
|
||||||
@@ -19,6 +20,14 @@ static WSADATA wsaData = { 0 };
|
|||||||
static bool ssl_inited = false;
|
static bool ssl_inited = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_PRINTF_S
|
||||||
|
#define printf printf_s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_SSCANF_S
|
||||||
|
#define sscanf sscanf_s
|
||||||
|
#endif
|
||||||
|
|
||||||
AIException::AIException(int code) {
|
AIException::AIException(int code) {
|
||||||
this->code = code;
|
this->code = code;
|
||||||
}
|
}
|
||||||
@@ -108,6 +117,7 @@ Socket::Socket(std::string host, std::string protocol) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Socket::~Socket() {
|
Socket::~Socket() {
|
||||||
|
if (this->moved) return;
|
||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
if (this->ssl_ctx) {
|
if (this->ssl_ctx) {
|
||||||
SSL_CTX_free(this->ssl_ctx);
|
SSL_CTX_free(this->ssl_ctx);
|
||||||
@@ -272,7 +282,7 @@ Request::Request(std::string host, std::string path, std::string method, HeaderM
|
|||||||
this->options = options;
|
this->options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::send() {
|
Response Request::send() {
|
||||||
std::string data;
|
std::string data;
|
||||||
data += this->method + " " + this->path + " HTTP/1.1\r\n";
|
data += this->method + " " + this->path + " HTTP/1.1\r\n";
|
||||||
auto hasBody = !this->body || !this->body->isFinished();
|
auto hasBody = !this->body || !this->body->isFinished();
|
||||||
@@ -289,7 +299,6 @@ void Request::send() {
|
|||||||
data += header.first + ": " + header.second + "\r\n";
|
data += header.first + ": " + header.second + "\r\n";
|
||||||
}
|
}
|
||||||
data += "\r\n";
|
data += "\r\n";
|
||||||
printf("%s", data.c_str());
|
|
||||||
#if HAVE_OPENSSL
|
#if HAVE_OPENSSL
|
||||||
Socket socket(this->host, this->options.https ? "https" : "http");
|
Socket socket(this->host, this->options.https ? "https" : "http");
|
||||||
#else
|
#else
|
||||||
@@ -302,12 +311,15 @@ void Request::send() {
|
|||||||
char buf[1024];
|
char buf[1024];
|
||||||
size_t len = this->body->pullData(buf, 1024);
|
size_t len = this->body->pullData(buf, 1024);
|
||||||
socket.send(buf, len, 0);
|
socket.send(buf, len, 0);
|
||||||
printf("%s", std::string(buf, len).c_str());
|
|
||||||
}
|
}
|
||||||
socket.send("\r\n");
|
socket.send("\r\n");
|
||||||
}
|
}
|
||||||
auto s = socket.recv(10240);
|
return Response(socket);
|
||||||
printf("%s\n", s.c_str());
|
}
|
||||||
|
|
||||||
|
void Request::setBody(HttpBody* body) {
|
||||||
|
if (this->body != nullptr) delete this->body;
|
||||||
|
this->body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClient::HttpClient(std::string host) {
|
HttpClient::HttpClient(std::string host) {
|
||||||
@@ -421,3 +433,115 @@ Request::~Request() {
|
|||||||
delete this->body;
|
delete this->body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Response::Response(Socket socket): socket(socket) {
|
||||||
|
parseStatus();
|
||||||
|
parseHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Response::pullData() {
|
||||||
|
if (!this->buff.empty()) return false;
|
||||||
|
this->buff = this->socket.recv(1024);
|
||||||
|
return this->buff.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::parseStatus() {
|
||||||
|
if (this->code) return;
|
||||||
|
auto line = this->readLine();
|
||||||
|
auto parts = str_util::str_splitv(line, " ", 3);
|
||||||
|
if (parts.size() < 3) {
|
||||||
|
throw std::runtime_error("Invalid HTTP status line");
|
||||||
|
}
|
||||||
|
if (cstr_stricmp(parts[0].c_str(), "http/1.1")) {
|
||||||
|
throw std::runtime_error("Unspported HTTP version");
|
||||||
|
}
|
||||||
|
if (sscanf(parts[1].c_str(), "%" SCNu8, &this->code) != 1) {
|
||||||
|
throw std::runtime_error("Invalid HTTP status code");
|
||||||
|
}
|
||||||
|
this->reason = parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::parseHeader() {
|
||||||
|
if (!this->code) parseStatus();
|
||||||
|
if (this->headerParsed) return;
|
||||||
|
this->headerParsed = true;
|
||||||
|
auto line = this->readLine();
|
||||||
|
while (!line.empty()) {
|
||||||
|
auto kv = str_util::str_splitv(line, ": ", 2);
|
||||||
|
if (kv.size() < 2) {
|
||||||
|
throw std::runtime_error("Invalid HTTP header");
|
||||||
|
}
|
||||||
|
this->headers[kv[0]] = kv[1];
|
||||||
|
if (!cstr_stricmp(kv[0].c_str(), "transfer-encoding")) {
|
||||||
|
auto list = str_util::str_splitv(kv[1], ",");
|
||||||
|
for (auto& item : list) {
|
||||||
|
auto it = str_util::str_trim(item);
|
||||||
|
if (!cstr_stricmp(it.c_str(), "chunked")) {
|
||||||
|
this->chunked = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Unspported transfer-encoding");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = this->readLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Response::readLine() {
|
||||||
|
std::string line;
|
||||||
|
while (true) {
|
||||||
|
if (this->pullData()) break;
|
||||||
|
auto pos = this->buff.find("\r\n");
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
line += this->buff;
|
||||||
|
this->buff.clear();
|
||||||
|
} else {
|
||||||
|
line += this->buff.substr(0, pos);
|
||||||
|
this->buff = this->buff.substr(pos + 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Response::read() {
|
||||||
|
if (!this->code) this->parseStatus();
|
||||||
|
if (!this->headerParsed) this->parseHeader();
|
||||||
|
if (this->chunked) {
|
||||||
|
std::string data;
|
||||||
|
size_t size = -1;
|
||||||
|
auto line = this->readLine();
|
||||||
|
if (sscanf(line.c_str(), "%zx", &size) != 1) {
|
||||||
|
throw std::runtime_error("Invalid chunk size");
|
||||||
|
}
|
||||||
|
size_t osize = size;
|
||||||
|
while (size > 0) {
|
||||||
|
if (this->pullData()) break;
|
||||||
|
auto len = std::min(this->buff.length(), size);
|
||||||
|
data += this->buff.substr(0, len);
|
||||||
|
this->buff = this->buff.substr(len);
|
||||||
|
size -= len;
|
||||||
|
}
|
||||||
|
if (osize != data.length()) {
|
||||||
|
throw std::runtime_error("Chunk size != data length");
|
||||||
|
}
|
||||||
|
this->readLine();
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
if (this->pullData()) return "";
|
||||||
|
auto data = this->buff;
|
||||||
|
this->buff.clear();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Response::readAll() {
|
||||||
|
std::string data;
|
||||||
|
auto d = this->read();
|
||||||
|
while (!d.empty()) {
|
||||||
|
data += d;
|
||||||
|
d = this->read();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define _UTIL_HTTP_CLIENT_H
|
#define _UTIL_HTTP_CLIENT_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -95,6 +96,10 @@ private:
|
|||||||
class Socket {
|
class Socket {
|
||||||
public:
|
public:
|
||||||
Socket(std::string host, std::string protocol);
|
Socket(std::string host, std::string protocol);
|
||||||
|
Socket(Socket& socket) {
|
||||||
|
*this = socket;
|
||||||
|
socket.moved = true;
|
||||||
|
}
|
||||||
~Socket();
|
~Socket();
|
||||||
void connect();
|
void connect();
|
||||||
size_t send(const char* data, size_t len, int flags = 0);
|
size_t send(const char* data, size_t len, int flags = 0);
|
||||||
@@ -116,6 +121,7 @@ private:
|
|||||||
BIO* web = nullptr;
|
BIO* web = nullptr;
|
||||||
SSL* ssl = nullptr;
|
SSL* ssl = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
bool moved = false;
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string protocol;
|
std::string protocol;
|
||||||
int socket = -1;
|
int socket = -1;
|
||||||
@@ -123,30 +129,41 @@ private:
|
|||||||
addrinfo* addr = nullptr;
|
addrinfo* addr = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct Response Response;
|
||||||
|
|
||||||
class Request {
|
class Request {
|
||||||
public:
|
public:
|
||||||
Request(std::string host, std::string path, std::string method, HeaderMap headers, HttpClientOptions options);
|
Request(std::string host, std::string path, std::string method, HeaderMap headers, HttpClientOptions options);
|
||||||
~Request();
|
~Request();
|
||||||
void send();
|
Response send();
|
||||||
HeaderMap headers;
|
HeaderMap headers;
|
||||||
HttpClientOptions options;
|
HttpClientOptions options;
|
||||||
void setBody(HttpBody* body) {
|
void setBody(HttpBody* body);
|
||||||
if (this->body != nullptr) delete this->body;
|
|
||||||
this->body = body;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
HttpBody* body = nullptr;
|
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string method;
|
std::string method;
|
||||||
|
private:
|
||||||
|
HttpBody* body = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Response {
|
class Response {
|
||||||
public:
|
public:
|
||||||
Response(Request req, Socket socket);
|
Response() = delete;
|
||||||
|
explicit Response(Socket socket);
|
||||||
|
HeaderMap headers;
|
||||||
|
uint8_t code = 0;
|
||||||
|
std::string reason;
|
||||||
|
std::string read();
|
||||||
|
std::string readAll();
|
||||||
private:
|
private:
|
||||||
Request req;
|
std::string readLine();
|
||||||
|
void parseHeader();
|
||||||
|
void parseStatus();
|
||||||
|
bool pullData();
|
||||||
|
bool headerParsed = false;
|
||||||
|
bool chunked = false;
|
||||||
Socket socket;
|
Socket socket;
|
||||||
|
std::string buff;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HttpClient {
|
class HttpClient {
|
||||||
|
|||||||
10
str_util.cpp
10
str_util.cpp
@@ -99,3 +99,13 @@ std::string str_util::remove_quote(std::string input) {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string str_util::str_trim(std::string input) {
|
||||||
|
while (!input.empty() && (input.front() == ' ' || input.front() == '\t' || input.front() == '\r' || input.front() == '\n')) {
|
||||||
|
input.erase(input.begin());
|
||||||
|
}
|
||||||
|
while (!input.empty() && (input.back() == ' ' || input.back() == '\t' || input.back() == '\r' || input.back() == '\n')) {
|
||||||
|
input.erase(input.end() - 1);
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ namespace str_util {
|
|||||||
* @return true if input ends with pattern
|
* @return true if input ends with pattern
|
||||||
*/
|
*/
|
||||||
bool str_endswith(std::string input, std::string pattern);
|
bool str_endswith(std::string input, std::string pattern);
|
||||||
|
/**
|
||||||
|
* @brief Trim a string
|
||||||
|
* @param input Input string
|
||||||
|
* @return Result
|
||||||
|
*/
|
||||||
|
std::string str_trim(std::string input);
|
||||||
/**
|
/**
|
||||||
* @brief Remove quote from a string
|
* @brief Remove quote from a string
|
||||||
* @param input Input string
|
* @param input Input string
|
||||||
|
|||||||
Reference in New Issue
Block a user