Kylin/HttpProxy/NetworkUtility.cpp
2024-05-03 21:39:01 +08:00

250 lines
8.0 KiB
C++

#include "NetworkUtility.h"
#include "BoostLog.h"
#include "root_certificates.hpp"
#include <boost/beast/core.hpp>
#include <boost/beast/http/dynamic_body.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/ssl/ssl_stream.hpp>
#include <boost/beast/version.hpp>
std::string Https::get(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string &url, boost::system::error_code &error, Http::Version version) {
Http::Client client(ioContext, Http::SSL, version);
return client.get(host, port, url, error);
}
std::string Https::post(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string &url, const std::string &body, boost::system::error_code &error,
Http::Version version) {
Http::Client client(ioContext, Http::SSL, version);
return client.post(host, port, url, body, error);
}
std::string Https::put(boost::asio::io_context &ioContext, const std::string &host, const std::string &port,
const std::string &url, const std::string &body, boost::system::error_code &error,
Http::Version version) {
Http::Client client(ioContext, Http::SSL, version);
return client.put(host, port, url, body, error);
}
uint64_t Network::htonll(uint64_t val) {
return (static_cast<uint64_t>(htonl(static_cast<uint32_t>(val))) << 32) + htonl(val >> 32);
}
uint64_t Network::ntohll(uint64_t val) {
return (static_cast<uint64_t>(ntohl(static_cast<uint32_t>(val))) << 32) + ntohl(val >> 32);
}
bool Network::isConnected(const std::string_view &host, size_t size) {
std::ostringstream oss;
#ifdef __linux__
oss << "ping " << host << " -q -W2 -c" << size;
#else
oss << "ping -n " << size << " " << host;
#endif
auto ret = std::system(oss.str().data());
return ret == 0;
}
#ifdef WIN32
#include <winsock.h>
#endif
Network::Network(int argc, char *argv[]) {
#ifdef WIN32
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
::WSAStartup(wVersionRequested, &wsaData);
LOG(info) << "WinSock initialized complete.";
#endif
}
Network::~Network() {
#ifdef WIN32
::WSACleanup();
#endif
}
namespace Http {
std::vector<ParseItem> parseFormData(const std::string_view &buffer) {
std::vector<ParseItem> ret;
auto pos = buffer.find("\r\n");
std::string_view m_spliter = buffer.substr(0, pos);
size_t m_end = buffer.rfind(m_spliter);
pos = 0;
while (pos != m_end) {
pos = buffer.find(m_spliter, pos);
if (pos == m_end) break;
ParseItem item;
auto dataSpliterPos = buffer.find("\r\n\r\n", pos);
item.begin = dataSpliterPos + 4;
item.end = buffer.find(m_spliter, pos + 1) - 2;
auto meta = buffer.substr(pos, dataSpliterPos - pos);
auto beginPos = meta.find("filename=\"") + 10;
auto endPos = meta.find("\"", beginPos);
item.filename = meta.substr(beginPos, endPos - beginPos);
pos++;
ret.push_back(item);
}
return ret;
}
class ClientPrivate {
public:
std::unique_ptr<boost::beast::ssl_stream<boost::beast::tcp_stream>> sslStream;
std::unique_ptr<boost::beast::tcp_stream> tcpStream;
};
Client::Client(boost::asio::io_context &ioContext, Type type, Version version)
: m_d(new ClientPrivate()), m_ioContext(ioContext) {
m_request.version(static_cast<unsigned>(version));
using namespace boost::asio;
using namespace boost::beast;
m_request.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
if (type == SSL) {
m_sslContext = std::make_shared<ssl::context>(ssl::context::sslv23_client);
}
}
Client::~Client() {
boost::system::error_code error;
shutdown(error);
if (error) {
LOG(error) << error.message();
}
if (m_d != nullptr) delete m_d;
}
void Client::loadRootCertificates(boost::system::error_code &error) {
if (m_sslContext) {
load_root_certificates(*m_sslContext, error);
if (!error) {
m_certificateLocation = CertificateLocation::Default;
}
}
}
std::string Client::get(const std::string &host, const std::string &port, const std::string &url,
boost::system::error_code &error) {
using namespace boost::beast;
m_request.method(http::verb::get);
m_request.target(url);
m_request.set(http::field::host, host);
return execute(host, port, m_request, error);
}
std::string Client::post(const std::string &host, const std::string &port, const std::string &url,
const std::string &body, boost::system::error_code &error) {
using namespace boost::beast;
m_request.method(http::verb::post);
m_request.target(url);
m_request.set(http::field::host, host);
m_request.body() = body;
m_request.prepare_payload();
return execute(host, port, m_request, error);
}
std::string Client::put(const std::string &host, const std::string &port, const std::string &url,
const std::string &body, boost::system::error_code &error) {
using namespace boost::beast;
m_request.method(http::verb::put);
m_request.target(url);
m_request.set(http::field::host, host);
m_request.body() = body;
m_request.prepare_payload();
return execute(host, port, m_request, error);
}
void Client::addRequestField(boost::beast::http::field name, const std::string &value) {
m_request.set(name, value);
}
std::string Client::execute(const std::string &host, const std::string &port, const Request &request,
boost::system::error_code &error) {
using namespace boost::beast;
if ((m_host != host) || (m_port != port) || (!m_d->sslStream && !m_d->tcpStream)) {
if (m_d->sslStream || m_d->tcpStream) {
boost::system::error_code errorCode;
shutdown(errorCode);
if (errorCode) {
LOG(error) << errorCode.message();
}
}
connect(host, port, error);
if (error) {
return std::string();
}
}
if (m_d->sslStream) {
http::write(*m_d->sslStream, request, error);
} else if (m_d->tcpStream) {
http::write(*m_d->tcpStream, request, error);
}
http::response<http::dynamic_body> response;
if (!error) {
flat_buffer buffer;
if (m_d->sslStream) {
http::read(*m_d->sslStream, buffer, response, error);
} else if (m_d->tcpStream) {
http::read(*m_d->tcpStream, buffer, response, error);
}
}
return boost::beast::buffers_to_string(response.body().data());
}
void Client::connect(const std::string &host, const std::string &port, boost::system::error_code &error) {
using namespace boost::asio::ip;
using namespace boost::beast;
tcp::resolver resolver(m_ioContext);
auto const results = resolver.resolve(host, port, error);
if (error) {
return;
}
if (m_sslContext) {
if (m_certificateLocation == CertificateLocation::None) {
m_sslContext->set_default_verify_paths(error);
if (!error) m_certificateLocation = CertificateLocation::SystemDependent;
}
m_d->sslStream = std::make_unique<ssl_stream<tcp_stream>>(m_ioContext, *m_sslContext);
get_lowest_layer(*m_d->sslStream).connect(results, error);
if (error) {
return;
}
m_d->sslStream->handshake(ssl::stream_base::client, error);
} else {
m_d->tcpStream = std::make_unique<tcp_stream>(m_ioContext);
m_d->tcpStream->connect(results, error);
}
if (error) {
return;
}
m_host = host;
m_port = port;
}
void Client::shutdown(boost::system::error_code &error) {
using namespace boost::asio::ip;
if (m_d->sslStream) {
m_d->sslStream->shutdown(error);
m_d->sslStream.reset();
} else if (m_d->tcpStream) {
m_d->tcpStream->socket().shutdown(tcp::socket::shutdown_both, error);
m_d->tcpStream.reset();
}
}
} // namespace Http