diff --git a/include/cinatra/coro_http_client.hpp b/include/cinatra/coro_http_client.hpp index 3ffdb4b4..8e0795a9 100644 --- a/include/cinatra/coro_http_client.hpp +++ b/include/cinatra/coro_http_client.hpp @@ -1186,6 +1186,9 @@ class coro_http_client : public std::enable_shared_from_this { std::pair handle_uri(resp_data &data, const S &uri) { uri_t u; if (!u.parse_from(uri.data())) { + CINATRA_LOG_WARNING + << uri + << ", the url is not right, maybe need to encode the url firstly"; data.net_err = std::make_error_code(std::errc::protocol_error); data.status = 404; return {false, {}}; diff --git a/include/cinatra/coro_http_connection.hpp b/include/cinatra/coro_http_connection.hpp index 494e45fc..c4d2dd16 100644 --- a/include/cinatra/coro_http_connection.hpp +++ b/include/cinatra/coro_http_connection.hpp @@ -172,6 +172,12 @@ class coro_http_connection parser_.method().data(), parser_.method().length() + 1 + parser_.url().length()}; + std::string decode_key; + if (parser_.url().find('%') != std::string_view::npos) { + decode_key = code_utils::url_decode(key); + key = decode_key; + } + if (!body_.empty()) { request_.set_body(body_); } diff --git a/include/cinatra/coro_http_server.hpp b/include/cinatra/coro_http_server.hpp index 03934e1e..13894086 100644 --- a/include/cinatra/coro_http_server.hpp +++ b/include/cinatra/coro_http_server.hpp @@ -228,12 +228,12 @@ class coro_http_server { } std::filesystem::path router_path = - std::filesystem::path(static_dir_router_path_); + std::filesystem::u8path(static_dir_router_path_); std::string uri; for (auto &file : files_) { auto relative_path = - std::filesystem::path(file.substr(static_dir_.length())).string(); + std::filesystem::u8path(file.substr(static_dir_.length())).string(); if (size_t pos = relative_path.find('\\') != std::string::npos) { replace_all(relative_path, "\\", "/"); } diff --git a/include/cinatra/url_encode_decode.hpp b/include/cinatra/url_encode_decode.hpp index 039c628e..75efacef 100644 --- a/include/cinatra/url_encode_decode.hpp +++ b/include/cinatra/url_encode_decode.hpp @@ -29,24 +29,40 @@ inline static std::string url_encode(const std::string &value) noexcept { return result; } -inline static std::string url_decode(const std::string &value) noexcept { +inline static std::string url_decode(std::string_view str) noexcept { std::string result; - result.reserve(value.size() / 3 + - (value.size() % 3)); // Minimum size of result - - for (std::size_t i = 0; i < value.size(); ++i) { - auto &chr = value[i]; - if (chr == '%' && i + 2 < value.size()) { - auto hex = value.substr(i + 1, 2); - auto decoded_chr = - static_cast(std::strtol(hex.c_str(), nullptr, 16)); - result += decoded_chr; - i += 2; + result.reserve(str.size()); + + for (size_t i = 0; i < str.size(); ++i) { + char ch = str[i]; + if (ch == '%') { + constexpr char hex[] = "0123456789ABCDEF"; + + if (++i == str.size()) { + result.push_back('?'); + break; + } + + int hi = (int)(std::find(hex, hex + 16, toupper(str[i])) - hex); + + if (++i == str.size()) { + result.push_back('?'); + break; + } + + int lo = (int)(std::find(hex, hex + 16, toupper(str[i])) - hex); + + if ((hi >= 16) || (lo >= 16)) { + result.push_back('?'); + break; + } + + result.push_back((char)((hi << 4) + lo)); } - else if (chr == '+') - result += ' '; + else if (ch == '+') + result.push_back(' '); else - result += chr; + result.push_back(ch); } return result; diff --git a/tests/test_coro_http_server.cpp b/tests/test_coro_http_server.cpp index b2060d69..726c56ce 100644 --- a/tests/test_coro_http_server.cpp +++ b/tests/test_coro_http_server.cpp @@ -130,12 +130,15 @@ TEST_CASE("coro_server example, will block") { CHECK(server.port() > 0); } -bool create_file(std::string_view filename, size_t file_size = 1024) { - std::ofstream out(filename.data(), std::ios::binary); +template +bool create_file(View filename, size_t file_size = 1024) { + std::cout << "begin to open file: " << filename << "\n"; + std::ofstream out(filename, std::ios::binary); if (!out.is_open()) { + std::cout << "open file: " << filename << " failed\n"; return false; } - + std::cout << "open file: " << filename << " ok\n"; std::string str(file_size, 'A'); out.write(str.data(), str.size()); return true; @@ -175,11 +178,43 @@ TEST_CASE("test multiple download") { TEST_CASE("test range download") { create_file("range_test.txt", 64); +#ifdef ASIO_WINDOWS +#else + create_file("中文测试.txt", 64); + create_file(fs::u8path("utf8中文.txt").string(), 64); +#endif std::cout << fs::current_path() << "\n"; coro_http_server server(1, 9001); server.set_static_res_dir("", ""); server.set_file_resp_format_type(file_resp_format_type::range); server.async_start(); + std::this_thread::sleep_for(300ms); + +#ifdef ASIO_WINDOWS +#else + { + // test Chinese file name + coro_http_client client{}; + std::string local_filename = "temp.txt"; + + std::string base_uri = "http://127.0.0.1:9001/"; + std::string path = code_utils::url_encode("中文测试.txt"); + auto result = client.download(base_uri + path, local_filename); + CHECK(result.status == 200); + CHECK(fs::file_size(local_filename) == 64); + } + + { + coro_http_client client{}; + std::string local_filename = "temp1.txt"; + std::string base_uri = "http://127.0.0.1:9001/"; + std::string path = + code_utils::url_encode(fs::u8path("utf8中文.txt").string()); + auto result = client.download(base_uri + path, local_filename); + CHECK(result.status == 200); + CHECK(fs::file_size(local_filename) == 64); + } +#endif coro_http_client client{}; std::string filename = "test1.txt";