diff --git a/README.md b/README.md index 2786bc0..c680108 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,21 @@ auto s = req.getStats(); 说明: - 若使用 HTTPS,需在编译时添加 `-DCPPHTTPLIB_OPENSSL_SUPPORT` 并链接 `-lssl -lcrypto`,且将 `opt.scheme` 设为 `"https"`、端口通常为 `443`。 - `base_path` 与各函数传入的 `path` 会自动合并,例如 `base_path="/api"` 且 `Get("/info")` 实际请求路径为 `/api/info`。 + +7) 快速用法(无需实例化) +```cpp +using ntq::NetRequest; + +// 直接传完整 URL 发起 GET +auto g = NetRequest::QuickGet("http://127.0.0.1:8080/api/info"); + +// 直接传完整 URL 发起 POST(JSON) +auto p1 = NetRequest::QuickPostJson( + "http://127.0.0.1:8080/api/set", + R"({"name":"orangepi","mode":"demo"})" +); + +// 直接传完整 URL 发起 POST(表单) +httplib::Params form = {{"user","abc"},{"pwd","123"}}; +auto p2 = NetRequest::QuickPostForm("http://127.0.0.1:8080/login", form); +``` diff --git a/include/NetRequest.hpp b/include/NetRequest.hpp index 7d24f4d..3cb473c 100644 --- a/include/NetRequest.hpp +++ b/include/NetRequest.hpp @@ -20,10 +20,96 @@ #include "httplib.h" #include #include -#include #include #include +// C++17/14 可选类型回退适配:统一使用 ntq::optional / ntq::nullopt +#if defined(__has_include) + #if __has_include() + #include + // 仅当启用了 C++17 或库声明了 optional 功能时,才使用 std::optional + #if defined(__cpp_lib_optional) || (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + namespace ntq { template using optional = std::optional; } + namespace ntq { constexpr auto nullopt = std::nullopt; using nullopt_t = decltype(std::nullopt); } + #elif __has_include() + #include + namespace ntq { template using optional = std::experimental::optional; } + namespace ntq { constexpr auto nullopt = std::experimental::nullopt; using nullopt_t = decltype(std::experimental::nullopt); } + #else + #include + namespace ntq { + struct nullopt_t { explicit constexpr nullopt_t(int) {} }; + static constexpr nullopt_t nullopt{0}; + template + class optional { + public: + optional() : has_(false) {} + optional(nullopt_t) : has_(false) {} + optional(const T &v) : has_(true), value_(v) {} + optional(T &&v) : has_(true), value_(std::move(v)) {} + optional(const optional &o) : has_(o.has_), value_(o.has_ ? o.value_ : T{}) {} + optional(optional &&o) noexcept : has_(o.has_), value_(std::move(o.value_)) {} + optional &operator=(nullopt_t) { has_ = false; return *this; } + optional &operator=(const T &v) { value_ = v; has_ = true; return *this; } + optional &operator=(T &&v) { value_ = std::move(v); has_ = true; return *this; } + explicit operator bool() const { return has_; } + bool has_value() const { return has_; } + T &value() { return value_; } + const T &value() const { return value_; } + T &operator*() { return value_; } + const T &operator*() const { return value_; } + private: + bool has_ = false; + T value_{}; + }; + } + #endif + #elif __has_include() + #include + namespace ntq { template using optional = std::experimental::optional; } + namespace ntq { constexpr auto nullopt = std::experimental::nullopt; using nullopt_t = decltype(std::experimental::nullopt); } + #else + #include + namespace ntq { + struct nullopt_t { explicit constexpr nullopt_t(int) {} }; + static constexpr nullopt_t nullopt{0}; + template + class optional { + public: + optional() : has_(false) {} + optional(nullopt_t) : has_(false) {} + optional(const T &v) : has_(true), value_(v) {} + optional(T &&v) : has_(true), value_(std::move(v)) {} + optional(const optional &o) : has_(o.has_), value_(o.has_ ? o.value_ : T{}) {} + optional(optional &&o) noexcept : has_(o.has_), value_(std::move(o.value_)) {} + optional &operator=(nullopt_t) { has_ = false; return *this; } + optional &operator=(const T &v) { value_ = v; has_ = true; return *this; } + optional &operator=(T &&v) { value_ = std::move(v); has_ = true; return *this; } + explicit operator bool() const { return has_; } + bool has_value() const { return has_; } + T &value() { return value_; } + const T &value() const { return value_; } + T &operator*() { return value_; } + const T &operator*() const { return value_; } + private: + bool has_ = false; + T value_{}; + }; + } + #endif +#else + // 无 __has_include:按语言级别判断 + #if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + #include + namespace ntq { template using optional = std::optional; } + namespace ntq { constexpr auto nullopt = std::nullopt; using nullopt_t = decltype(std::nullopt); } + #else + #include + namespace ntq { template using optional = std::experimental::optional; } + namespace ntq { constexpr auto nullopt = std::experimental::nullopt; using nullopt_t = decltype(std::experimental::nullopt); } + #endif +#endif + namespace ntq { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -137,7 +223,7 @@ namespace ntq * @param err 可选错误码输出 * @return 成功返回响应对象,失败返回 std::nullopt */ - std::optional Get(const std::string &path, + ntq::optional Get(const std::string &path, const httplib::Params &query = {}, const httplib::Headers &headers = {}, ErrorCode *err = nullptr); @@ -150,7 +236,7 @@ namespace ntq * @param err 可选错误码输出 * @return 成功返回响应对象,失败返回 std::nullopt */ - std::optional PostJson(const std::string &path, + ntq::optional PostJson(const std::string &path, const std::string &json, const httplib::Headers &headers = {}, ErrorCode *err = nullptr); @@ -163,7 +249,7 @@ namespace ntq * @param err 可选错误码输出 * @return 成功返回响应对象,失败返回 std::nullopt */ - std::optional PostForm(const std::string &path, + ntq::optional PostForm(const std::string &path, const httplib::Params &form, const httplib::Headers &headers = {}, ErrorCode *err = nullptr); @@ -172,7 +258,7 @@ namespace ntq * @brief 异步 GET 请求 * @return std::future,用于获取响应结果 */ - std::future> GetAsync(const std::string &path, + std::future> GetAsync(const std::string &path, const httplib::Params &query = {}, const httplib::Headers &headers = {}, ErrorCode *err = nullptr); @@ -181,7 +267,7 @@ namespace ntq * @brief 异步 POST JSON 请求 * @return std::future,用于获取响应结果 */ - std::future> PostJsonAsync(const std::string &path, + std::future> PostJsonAsync(const std::string &path, const std::string &json, const httplib::Headers &headers = {}, ErrorCode *err = nullptr); @@ -190,7 +276,7 @@ namespace ntq * @brief 异步 POST 表单请求 * @return std::future,用于获取响应结果 */ - std::future> PostFormAsync(const std::string &path, + std::future> PostFormAsync(const std::string &path, const httplib::Params &form, const httplib::Headers &headers = {}, ErrorCode *err = nullptr); @@ -217,6 +303,40 @@ namespace ntq */ Stats getStats() const; + /** + * @brief 便捷:直接用完整 URL 发起 GET(无需显式实例化) + * @param url 形如 http://host:port/path?x=1 或 https://host/path + * @param headers 额外头部 + * @param err 可选错误码输出 + */ + static ntq::optional QuickGet(const std::string &url, + const httplib::Headers &headers = {}, + ErrorCode *err = nullptr); + + /** + * @brief 便捷:直接用完整 URL 发起 POST JSON(无需显式实例化) + * @param url 形如 http://host:port/path?x=1 或 https://host/path + * @param json JSON 字符串(Content-Type: application/json) + * @param headers 额外头部 + * @param err 可选错误码输出 + */ + static ntq::optional QuickPostJson(const std::string &url, + const std::string &json, + const httplib::Headers &headers = {}, + ErrorCode *err = nullptr); + + /** + * @brief 便捷:直接用完整 URL 发起 POST 表单(无需显式实例化) + * @param url 形如 http://host:port/path?x=1 或 https://host/path + * @param form 表单参数 + * @param headers 额外头部 + * @param err 可选错误码输出 + */ + static ntq::optional QuickPostForm(const std::string &url, + const httplib::Params &form, + const httplib::Headers &headers = {}, + ErrorCode *err = nullptr); + private: struct Impl; Impl *impl_; diff --git a/include/encrypt.hpp b/include/encrypt.hpp new file mode 100644 index 0000000..42ea793 --- /dev/null +++ b/include/encrypt.hpp @@ -0,0 +1,14 @@ +#pragma once + +/* +主要是用于各种加密 +*/ + +#include "QCL_Include.hpp" + +using namespace std; + +namespace encrypt +{ + string MD5(const string &info); +} \ No newline at end of file diff --git a/src/NetRequest.cpp b/src/NetRequest.cpp index 605618a..f3aa541 100644 --- a/src/NetRequest.cpp +++ b/src/NetRequest.cpp @@ -198,7 +198,7 @@ namespace ntq impl_->cache.clear(); } - std::optional NetRequest::Get(const std::string &path, + ntq::optional NetRequest::Get(const std::string &path, const httplib::Params &query, const httplib::Headers &headers, ErrorCode *err) @@ -221,7 +221,7 @@ namespace ntq } } - std::optional result; + ntq::optional result; ErrorCode local_err = ErrorCode::None; const auto full_path = impl_->build_full_path(path); @@ -280,7 +280,7 @@ namespace ntq { impl_->stats.total_errors++; if (err) *err = local_err; - return std::nullopt; + return ntq::nullopt; } if (impl_->cache_enabled) @@ -293,7 +293,7 @@ namespace ntq return result; } - std::optional NetRequest::PostJson(const std::string &path, + ntq::optional NetRequest::PostJson(const std::string &path, const std::string &json, const httplib::Headers &headers, ErrorCode *err) @@ -302,7 +302,7 @@ namespace ntq impl_->stats.total_requests++; auto start = std::chrono::steady_clock::now(); - std::optional result; + ntq::optional result; ErrorCode local_err = ErrorCode::None; const auto full_path = impl_->build_full_path(path); @@ -349,13 +349,13 @@ namespace ntq { impl_->stats.total_errors++; if (err) *err = local_err; - return std::nullopt; + return ntq::nullopt; } if (err) *err = ErrorCode::None; return result; } - std::optional NetRequest::PostForm(const std::string &path, + ntq::optional NetRequest::PostForm(const std::string &path, const httplib::Params &form, const httplib::Headers &headers, ErrorCode *err) @@ -364,7 +364,7 @@ namespace ntq impl_->stats.total_requests++; auto start = std::chrono::steady_clock::now(); - std::optional result; + ntq::optional result; ErrorCode local_err = ErrorCode::None; const auto full_path = impl_->build_full_path(path); @@ -411,13 +411,13 @@ namespace ntq { impl_->stats.total_errors++; if (err) *err = local_err; - return std::nullopt; + return ntq::nullopt; } if (err) *err = ErrorCode::None; return result; } - std::future> NetRequest::GetAsync(const std::string &path, + std::future> NetRequest::GetAsync(const std::string &path, const httplib::Params &query, const httplib::Headers &headers, ErrorCode *err) @@ -430,7 +430,7 @@ namespace ntq }); } - std::future> NetRequest::PostJsonAsync(const std::string &path, + std::future> NetRequest::PostJsonAsync(const std::string &path, const std::string &json, const httplib::Headers &headers, ErrorCode *err) @@ -443,7 +443,7 @@ namespace ntq }); } - std::future> NetRequest::PostFormAsync(const std::string &path, + std::future> NetRequest::PostFormAsync(const std::string &path, const httplib::Params &form, const httplib::Headers &headers, ErrorCode *err) @@ -560,4 +560,76 @@ namespace ntq { return impl_->stats; } + + // ------------------------- Quick helpers ------------------------- + namespace { + struct ParsedURL { + std::string scheme; + std::string host; + int port = 0; + std::string path_and_query; + bool ok = false; + }; + + static ParsedURL parse_url(const std::string &url) + { + ParsedURL p; p.ok = false; + // very small parser: scheme://host[:port]/path[?query] + auto pos_scheme = url.find("://"); + if (pos_scheme == std::string::npos) return p; + p.scheme = url.substr(0, pos_scheme); + size_t pos_host = pos_scheme + 3; + + size_t pos_path = url.find('/', pos_host); + std::string hostport = pos_path == std::string::npos ? url.substr(pos_host) + : url.substr(pos_host, pos_path - pos_host); + auto pos_colon = hostport.find(':'); + if (pos_colon == std::string::npos) { + p.host = hostport; + p.port = (p.scheme == "https") ? 443 : 80; + } else { + p.host = hostport.substr(0, pos_colon); + std::string port_str = hostport.substr(pos_colon + 1); + p.port = port_str.empty() ? ((p.scheme == "https") ? 443 : 80) : std::atoi(port_str.c_str()); + } + p.path_and_query = (pos_path == std::string::npos) ? "/" : url.substr(pos_path); + p.ok = !p.host.empty(); + return p; + } + } + + ntq::optional NetRequest::QuickGet(const std::string &url, + const httplib::Headers &headers, + ErrorCode *err) + { + auto p = parse_url(url); + if (!p.ok) { if (err) *err = ErrorCode::InvalidURL; return std::nullopt; } + RequestOptions opt; opt.scheme = p.scheme; opt.host = p.host; opt.port = p.port; + NetRequest req(opt); + return req.Get(p.path_and_query, {}, headers, err); + } + + ntq::optional NetRequest::QuickPostJson(const std::string &url, + const std::string &json, + const httplib::Headers &headers, + ErrorCode *err) + { + auto p = parse_url(url); + if (!p.ok) { if (err) *err = ErrorCode::InvalidURL; return std::nullopt; } + RequestOptions opt; opt.scheme = p.scheme; opt.host = p.host; opt.port = p.port; + NetRequest req(opt); + return req.PostJson(p.path_and_query, json, headers, err); + } + + ntq::optional NetRequest::QuickPostForm(const std::string &url, + const httplib::Params &form, + const httplib::Headers &headers, + ErrorCode *err) + { + auto p = parse_url(url); + if (!p.ok) { if (err) *err = ErrorCode::InvalidURL; return std::nullopt; } + RequestOptions opt; opt.scheme = p.scheme; opt.host = p.host; opt.port = p.port; + NetRequest req(opt); + return req.PostForm(p.path_and_query, form, headers, err); + } } diff --git a/src/Netra.cpp b/src/Netra.cpp index bd49921..3160759 100644 --- a/src/Netra.cpp +++ b/src/Netra.cpp @@ -494,16 +494,29 @@ namespace QCL std::vector ReadFile::ReadLines() { - std::lock_guard lock(mtx_); - if (!file_.is_open() && !Open()) - return {}; + // std::lock_guard lock(mtx_); + // if (!file_.is_open() && !Open()) + // return {}; + // std::vector lines; + // std::string line; + // while (std::getline(file_, line)) + // { + // lines.push_back(line); + // } + // return lines; + + std::lock_guard lock(mtx_); + if (!file_.is_open()) { + file_.open(filename_, std::ios::in | std::ios::binary); + if (!file_.is_open()) return {}; + } + file_.clear(); + file_.seekg(0, std::ios::beg); + std::vector lines; std::string line; - while (std::getline(file_, line)) - { - lines.push_back(line); - } + while (std::getline(file_, line)) lines.push_back(line); return lines; } diff --git a/src/encrypt.cpp b/src/encrypt.cpp new file mode 100644 index 0000000..a8aea93 --- /dev/null +++ b/src/encrypt.cpp @@ -0,0 +1,91 @@ +#include "encrypt.hpp" +#include + +namespace encrypt +{ + string MD5(const string &info) + { + auto leftrotate = [](uint32_t x, uint32_t c) -> uint32_t { return (x << c) | (x >> (32 - c)); }; + + static const uint32_t s[64] = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 + }; + static const uint32_t K[64] = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + uint32_t a0 = 0x67452301; + uint32_t b0 = 0xefcdab89; + uint32_t c0 = 0x98badcfe; + uint32_t d0 = 0x10325476; + + std::vector msg(info.begin(), info.end()); + uint64_t bit_len = static_cast(msg.size()) * 8ULL; + msg.push_back(0x80); + while ((msg.size() % 64) != 56) msg.push_back(0x00); + for (int i = 0; i < 8; ++i) msg.push_back(static_cast((bit_len >> (8 * i)) & 0xff)); + + for (size_t offset = 0; offset < msg.size(); offset += 64) + { + uint32_t M[16]; + for (int i = 0; i < 16; ++i) + { + size_t j = offset + i * 4; + M[i] = static_cast(msg[j]) | + (static_cast(msg[j + 1]) << 8) | + (static_cast(msg[j + 2]) << 16) | + (static_cast(msg[j + 3]) << 24); + } + + uint32_t A = a0, B = b0, C = c0, D = d0; + for (uint32_t i = 0; i < 64; ++i) + { + uint32_t F, g; + if (i < 16) { F = (B & C) | ((~B) & D); g = i; } + else if (i < 32) { F = (D & B) | ((~D) & C); g = (5 * i + 1) % 16; } + else if (i < 48) { F = B ^ C ^ D; g = (3 * i + 5) % 16; } + else { F = C ^ (B | (~D)); g = (7 * i) % 16; } + + F = F + A + K[i] + M[g]; + A = D; + D = C; + C = B; + B = B + leftrotate(F, s[i]); + } + + a0 += A; b0 += B; c0 += C; d0 += D; + } + + uint8_t digest[16]; + auto u32_to_le = [](uint32_t v, uint8_t out[4]) { + out[0] = static_cast(v & 0xff); + out[1] = static_cast((v >> 8) & 0xff); + out[2] = static_cast((v >> 16) & 0xff); + out[3] = static_cast((v >> 24) & 0xff); + }; + u32_to_le(a0, digest + 0); + u32_to_le(b0, digest + 4); + u32_to_le(c0, digest + 8); + u32_to_le(d0, digest + 12); + + static const char *hex = "0123456789abcdef"; + std::string out; + out.resize(32); + for (int i = 0; i < 16; ++i) + { + out[i * 2] = hex[(digest[i] >> 4) & 0x0f]; + out[i * 2 + 1] = hex[digest[i] & 0x0f]; + } + return out; + } +} \ No newline at end of file