From 64c387aba9c3ab1ebcc7a186f3ed1d00938d76bd Mon Sep 17 00:00:00 2001 From: luocai Date: Fri, 21 Jul 2023 11:53:19 +0800 Subject: [PATCH] add library 'HttpProxy'. --- CMakeLists.txt | 1 + HttpProxy/CMakeLists.txt | 11 ++ HttpProxy/NetworkUtility.cpp | 202 +++++++++++++++++++++++++++++++++++ HttpProxy/NetworkUtility.h | 68 ++++++++++++ Universal/CMakeLists.txt | 2 + Universal/DateTime.cpp | 180 +++++++++++++++++++++++++++++++ Universal/DateTime.h | 73 +++++++++++++ 7 files changed, 537 insertions(+) create mode 100644 HttpProxy/CMakeLists.txt create mode 100644 HttpProxy/NetworkUtility.cpp create mode 100644 HttpProxy/NetworkUtility.h create mode 100644 Universal/DateTime.cpp create mode 100644 Universal/DateTime.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ea110c..fd1baa0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,4 +2,5 @@ cmake_minimum_required(VERSION 3.15) project(Kylin) +add_subdirectory(HttpProxy) add_subdirectory(Universal) \ No newline at end of file diff --git a/HttpProxy/CMakeLists.txt b/HttpProxy/CMakeLists.txt new file mode 100644 index 0000000..fa5e1f9 --- /dev/null +++ b/HttpProxy/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(HttpProxy + NetworkUtility.h NetworkUtility.cpp +) + +target_include_directories(HttpProxy + INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(HttpProxy + PUBLIC Universal +) \ No newline at end of file diff --git a/HttpProxy/NetworkUtility.cpp b/HttpProxy/NetworkUtility.cpp new file mode 100644 index 0000000..5e2aecd --- /dev/null +++ b/HttpProxy/NetworkUtility.cpp @@ -0,0 +1,202 @@ +#include "NetworkUtility.h" +#include "BoostLog.h" +#include +#include +#include +#include +#include + +static std::optional> +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 req{http::verb::get, url, static_cast(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 parser; + parser.body_limit(std::numeric_limits::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 req{http::verb::post, url.data(), static_cast(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 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 req{http::verb::put, url.data(), static_cast(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 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> +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 + 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 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::parseFormData(const std::string_view &buffer) { + std::vector 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(htonl(static_cast(val))) << 32) + htonl(val >> 32); +} + +uint64_t Network::ntohll(uint64_t val) { + return (static_cast(ntohl(static_cast(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 +#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 +} diff --git a/HttpProxy/NetworkUtility.h b/HttpProxy/NetworkUtility.h new file mode 100644 index 0000000..7bed5f1 --- /dev/null +++ b/HttpProxy/NetworkUtility.h @@ -0,0 +1,68 @@ +#ifndef __NETWORKUTILITY_H__ +#define __NETWORKUTILITY_H__ + +#include "Singleton.h" +#include +#include +#include +#include + +class Network { + friend class Amass::Singleton; + +public: + static bool isConnected(const std::string_view &host, size_t size); + static uint64_t htonll(uint64_t val); + static uint64_t ntohll(uint64_t val); + +protected: + Network(int argc, char *argv[]); + ~Network(); +}; + +class Http { +public: + enum Version : unsigned { + Version_1_1 = 11, + }; + struct ParseItem { + std::string filename; + // std::string::const_iterator begin; + // std::string::const_iterator end; + size_t begin; + size_t end; + }; + + /** + * @brief + * + * @param buffer + * @return std::vector + * @example auto items = parseFormData(request.body()); + * for (auto &item : items) { + * std::string filename("E:/"); + * filename += item.filename; + * std::ofstream ofs(filename.c_str(), std::ios::binary); + * ofs.write(buffer.data() + item.begin, item.end - item.begin); + * ofs.close(); + * } + */ + static std::vector parseFormData(const std::string_view &buffer); +}; + +class Https { +public: + static std::string 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 = Http::Version_1_1); + + static std::string 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 = Http::Version_1_1); + + static std::string 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::Version_1_1); +}; + +#endif // __NETWORKUTILITY_H__ \ No newline at end of file diff --git a/Universal/CMakeLists.txt b/Universal/CMakeLists.txt index dc75abf..396d6e6 100644 --- a/Universal/CMakeLists.txt +++ b/Universal/CMakeLists.txt @@ -2,6 +2,8 @@ find_package(Boost REQUIRED COMPONENTS log log_setup) add_library(Universal BoostLog.h BoostLog.inl BoostLog.cpp + DateTime.h DateTime.cpp + FunctionTraits.h IoContext.h IoContext.cpp MessageManager.h MessageManager.inl MessageManager.cpp Singleton.h diff --git a/Universal/DateTime.cpp b/Universal/DateTime.cpp new file mode 100644 index 0000000..37810b6 --- /dev/null +++ b/Universal/DateTime.cpp @@ -0,0 +1,180 @@ +#include "DateTime.h" +#include "BoostLog.h" +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif + +DateTime::DateTime(const std::chrono::time_point &time) { + auto t = std::chrono::system_clock::to_time_t(time); + // auto tc = std::gmtime(&t); + auto tc = std::localtime(&t); + + m_year = tc->tm_year + 1900; + m_month = tc->tm_mon + 1; + m_day = tc->tm_mday; + m_hour = tc->tm_hour; + m_minute = tc->tm_min; + m_second = tc->tm_sec; + m_week = static_cast(tc->tm_wday); +} + +DateTime::DateTime(size_t year, size_t month, size_t day, size_t hour, size_t minute, size_t second) + : m_year(year), m_month(month), m_day(day), m_hour(hour), m_minute(minute), m_second(second) { +} + +int64_t DateTime::currentMSecsSinceEpoch() { +#ifdef __linux__ + timespec time; + + clock_gettime(CLOCK_REALTIME, &time); + return time.tv_sec * 1000 + time.tv_nsec * 1e-6; +#else + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +#endif +} + +int64_t DateTime::currentSecsSinceEpoch() { + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); +} + +size_t DateTime::year() const { + return m_year; +} + +size_t DateTime::month() const { + return m_month; +} + +size_t DateTime::day() const { + return m_day; +} + +DateTime::Week DateTime::week() const { + return m_week; +} + +std::chrono::time_point DateTime::operator()() { + return makeDateTime(m_year, m_month, m_day, m_hour, m_minute, m_second); +} + +size_t DateTime::hour() const { + return m_hour; +} + +void DateTime::setHour(size_t hour) { + if (m_hour == hour || hour > 23) return; + m_hour = hour; +} + +size_t DateTime::minute() const { + return m_minute; +} + +void DateTime::setMinute(size_t minute) { + if (m_minute == minute || minute > 59) return; + m_minute = minute; +} + +size_t DateTime::second() const { + return m_second; +} + +void DateTime::setSecond(size_t second) { + if (m_second == second || second > 60) return; + m_second = second; +} + +DateTime DateTime::currentDateTime() { + return DateTime(std::chrono::system_clock::now()); +} + +std::string DateTime::toString(const std::string_view &format) const { + auto time = makeTime(m_year, m_month, m_day, m_hour, m_minute, m_second); + std::ostringstream oss; + oss << std::put_time(std::localtime(&time), format.data()); + return oss.str(); +} + +std::string DateTime::toString(const std::chrono::time_point &time, const std::string_view &format) { + + auto now_c = std::chrono::system_clock::to_time_t(time); + std::ostringstream oss; + oss << std::put_time(std::localtime(&now_c), format.data()); + return oss.str(); +} + +DateTime DateTime::tomorrow() { + DateTime ret(*this); + size_t currentMonthDays = days(ret.m_year, ret.m_month); + ret.m_day++; + if (ret.m_day <= currentMonthDays) return ret; + ret.m_day = 1; + ret.m_month++; + if (m_month <= 12) return ret; + ret.m_month = 1; + ret.m_year++; + return ret; +} + +bool DateTime::isLeapYear(size_t year) { + return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); +} + +size_t DateTime::days(size_t year, size_t month) { + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + return 31; + case 4: + case 6: + case 9: + case 11: + return 30; + case 2: + return isLeapYear(year) ? 29 : 28; + default: + return -1; + } +} + +std::chrono::time_point DateTime::makeDateTime(size_t year, size_t month, size_t day, size_t hour, size_t minute, + size_t second) { + return std::chrono::system_clock::from_time_t(makeTime(year, month, day, hour, minute, second)); +} + +std::time_t DateTime::makeTime(size_t year, size_t month, size_t day, size_t hour, size_t minute, size_t second) { + time_t rawtime; + time(&rawtime); + auto timepoint = localtime(&rawtime); + timepoint->tm_year = static_cast(year - 1900); + timepoint->tm_mon = static_cast(month - 1); + timepoint->tm_mday = static_cast(day); + timepoint->tm_hour = static_cast(hour); + timepoint->tm_min = static_cast(minute); + timepoint->tm_sec = static_cast(second); + + return std::mktime(timepoint); +} + +std::tuple DateTime::parseTime(const std::string &text) { + auto timeStr = boost::algorithm::trim_copy(text); + std::vector result; + boost::algorithm::split(result, timeStr, boost::algorithm::is_any_of(":")); + if (result.size() > 3 || result.size() < 2) return {(uint8_t)-1, (uint8_t)-1, (uint8_t)-1}; + + auto hour = std::stoi(result.at(0)); + auto minute = std::stoi(result.at(1)); + auto second = result.size() > 2 ? std::stoi(result.at(2)) : 0; + return std::make_tuple(hour, minute, second); +} diff --git a/Universal/DateTime.h b/Universal/DateTime.h new file mode 100644 index 0000000..04b2f0f --- /dev/null +++ b/Universal/DateTime.h @@ -0,0 +1,73 @@ +#ifndef DATETIME_H +#define DATETIME_H + +#include +#include +#include +#include + +class DateTime { +public: + enum Week { + Sunday = 0, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + }; + DateTime(const std::chrono::time_point &time); + DateTime(size_t year, size_t month, size_t day, size_t hour, size_t minute, size_t second); + size_t year() const; + size_t month() const; + size_t day() const; + + size_t hour() const; + void setHour(size_t hour); + + size_t minute() const; + void setMinute(size_t minute); + + size_t second() const; + void setSecond(size_t second); + + Week week() const; + std::chrono::time_point operator()(); + std::string toString(const std::string_view &format = "%F %T") const; + + DateTime tomorrow(); + + // Returns the number of milliseconds since 1970-01-01T00:00:00 Universal Coordinated Time. This number is like the + // POSIX time_t variable, but expressed in milliseconds instead. + static int64_t currentMSecsSinceEpoch(); + static int64_t currentSecsSinceEpoch(); + static DateTime currentDateTime(); + static std::string toString(const std::chrono::time_point &time, + const std::string_view &format = "%F %T"); + static std::chrono::time_point makeDateTime(size_t year, size_t month, size_t day, + size_t hour, size_t minute, size_t second); + static bool isLeapYear(size_t year); + static size_t days(size_t year, size_t month); + + /** + * @brief parse time like "12:34:56"、"23:56" + * + * @return std::tuple + */ + static std::tuple parseTime(const std::string &text); + +protected: + static std::time_t makeTime(size_t year, size_t month, size_t day, size_t hour, size_t minute, size_t second); + +private: + size_t m_year{1990}; + size_t m_month{1}; + size_t m_day{0}; + size_t m_hour{0}; + size_t m_minute{0}; + size_t m_second{0}; + Week m_week; +}; + +#endif // DATETIME_H