203 lines
6.7 KiB
C++
203 lines
6.7 KiB
C++
|
#include "NetworkUtility.h"
|
||
|
#include "BoostLog.h"
|
||
|
#include <boost/beast/core.hpp>
|
||
|
#include <boost/beast/http.hpp>
|
||
|
#include <boost/beast/ssl.hpp>
|
||
|
#include <boost/beast/version.hpp>
|
||
|
#include <optional>
|
||
|
|
||
|
static std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream>>
|
||
|
makeSslStream(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port,
|
||
|
boost::system::error_code &error);
|
||
|
|
||
|
std::string Https::get(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port,
|
||
|
const std::string_view &url, boost::system::error_code &error, Http::Version version) {
|
||
|
namespace beast = boost::beast;
|
||
|
namespace http = boost::beast::http;
|
||
|
|
||
|
auto stream = makeSslStream(ioContext, host, port, error);
|
||
|
if (!stream) return std::string();
|
||
|
|
||
|
http::request<http::string_body> req{http::verb::get, url, static_cast<unsigned>(version)};
|
||
|
req.set(http::field::host, host);
|
||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||
|
http::write(*stream, req, error);
|
||
|
if (error) {
|
||
|
// LOG(error) << error.message();
|
||
|
return std::string();
|
||
|
}
|
||
|
beast::flat_buffer buffer;
|
||
|
http::response_parser<http::dynamic_body> parser;
|
||
|
parser.body_limit(std::numeric_limits<std::uint64_t>::max());
|
||
|
http::read(*stream, buffer, parser, error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::string();
|
||
|
}
|
||
|
beast::error_code ec;
|
||
|
stream->shutdown(ec);
|
||
|
return boost::beast::buffers_to_string(parser.get().body().data());
|
||
|
}
|
||
|
|
||
|
std::string Https::post(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port,
|
||
|
const std::string_view &url, const std::string_view &body, boost::system::error_code &error,
|
||
|
Http::Version version) {
|
||
|
|
||
|
namespace beast = boost::beast;
|
||
|
namespace http = boost::beast::http;
|
||
|
|
||
|
auto stream = makeSslStream(ioContext, host, port, error);
|
||
|
if (!stream) return std::string();
|
||
|
|
||
|
http::request<http::string_body> req{http::verb::post, url.data(), static_cast<unsigned>(version)};
|
||
|
req.set(http::field::host, host);
|
||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||
|
req.body() = body;
|
||
|
req.prepare_payload();
|
||
|
http::write(*stream, req, error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::string();
|
||
|
}
|
||
|
beast::flat_buffer buffer;
|
||
|
http::response<boost::beast::http::dynamic_body> response;
|
||
|
http::read(*stream, buffer, response, error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::string();
|
||
|
}
|
||
|
|
||
|
beast::error_code ec;
|
||
|
stream->shutdown(ec);
|
||
|
return boost::beast::buffers_to_string(response.body().data());
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
namespace beast = boost::beast;
|
||
|
namespace http = boost::beast::http;
|
||
|
|
||
|
auto stream = makeSslStream(ioContext, host, port, error);
|
||
|
if (!stream) return std::string();
|
||
|
|
||
|
http::request<http::string_body> req{http::verb::put, url.data(), static_cast<unsigned>(version)};
|
||
|
req.set(http::field::host, host);
|
||
|
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||
|
req.body() = body;
|
||
|
req.prepare_payload();
|
||
|
http::write(*stream, req, error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::string();
|
||
|
}
|
||
|
beast::flat_buffer buffer;
|
||
|
http::response<boost::beast::http::dynamic_body> response;
|
||
|
http::read(*stream, buffer, response, error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::string();
|
||
|
}
|
||
|
|
||
|
beast::error_code ec;
|
||
|
stream->shutdown(ec);
|
||
|
return boost::beast::buffers_to_string(response.body().data());
|
||
|
}
|
||
|
|
||
|
static std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream>>
|
||
|
makeSslStream(boost::asio::io_context &ioContext, const std::string_view &host, const std::string_view &port,
|
||
|
boost::system::error_code &error) {
|
||
|
namespace beast = boost::beast;
|
||
|
namespace http = boost::beast::http;
|
||
|
|
||
|
namespace asio = boost::asio; // from <boost/asio.hpp>
|
||
|
using tcp = asio::ip::tcp;
|
||
|
|
||
|
namespace ssl = asio::ssl;
|
||
|
|
||
|
ssl::context sslContext(ssl::context::sslv23_client);
|
||
|
sslContext.set_default_verify_paths(error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
|
||
|
boost::asio::ip::tcp::resolver resolver(ioContext);
|
||
|
|
||
|
beast::ssl_stream<beast::tcp_stream> stream(ioContext, sslContext);
|
||
|
|
||
|
auto const results = resolver.resolve(host, port, error);
|
||
|
if (error) {
|
||
|
LOG(error) << error.message();
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
beast::get_lowest_layer(stream).connect(results);
|
||
|
stream.handshake(ssl::stream_base::client);
|
||
|
return std::make_optional(std::move(stream));
|
||
|
}
|
||
|
|
||
|
std::vector<Http::ParseItem> Http::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;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|