From 95fb89eb301f1b2b1d1b11aa518ac297c92387fe Mon Sep 17 00:00:00 2001 From: yhirose Date: Wed, 13 Nov 2024 19:32:56 -0500 Subject: [PATCH 1/3] Fix #1973 --- httplib.h | 11 ++++++++-- test/test.cc | 21 +++++++++++++++++++ ...6\227\245\346\234\254\350\252\236File.txt" | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 "test/www/\346\227\245\346\234\254\350\252\236Dir/\346\227\245\346\234\254\350\252\236File.txt" diff --git a/httplib.h b/httplib.h index c621f5b53d..f4a6d9cc60 100644 --- a/httplib.h +++ b/httplib.h @@ -2910,8 +2910,15 @@ inline bool mmap::open(const char *path) { #if defined(_WIN32) std::wstring wpath; - for (size_t i = 0; i < strlen(path); i++) { - wpath += path[i]; + { + auto len = static_cast(strlen(path)); + auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, path, len, nullptr, 0); + if (wlen <= 0) { return false; } + + wpath.resize(wlen); + auto pwpath = const_cast(reinterpret_cast(wpath.data())); + wlen = ::MultiByteToWideChar(CP_UTF8, 0, path, len, pwpath, wlen); + if (wlen != wpath.size()) { return false; } } #if _WIN32_WINNT >= _WIN32_WINNT_WIN8 diff --git a/test/test.cc b/test/test.cc index 154b60c5e2..dfa4e1a14e 100644 --- a/test/test.cc +++ b/test/test.cc @@ -5271,6 +5271,27 @@ TEST(MountTest, Redicect) { EXPECT_EQ(StatusCode::OK_200, res->status); } +TEST(MountTest, MultibytesPathName) { + Server svr; + + auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); }); + auto se = detail::scope_exit([&] { + svr.stop(); + listen_thread.join(); + ASSERT_FALSE(svr.is_running()); + }); + + svr.set_mount_point("/", "./www"); + svr.wait_until_ready(); + + Client cli("localhost", PORT); + + auto res = cli.Get("/日本語Dir/日本語File.txt"); + ASSERT_TRUE(res); + EXPECT_EQ(StatusCode::OK_200, res->status); + EXPECT_EQ("日本語コンテンツ", res->body); +} + TEST(KeepAliveTest, ReadTimeout) { Server svr; diff --git "a/test/www/\346\227\245\346\234\254\350\252\236Dir/\346\227\245\346\234\254\350\252\236File.txt" "b/test/www/\346\227\245\346\234\254\350\252\236Dir/\346\227\245\346\234\254\350\252\236File.txt" new file mode 100644 index 0000000000..3cc1ce7c92 --- /dev/null +++ "b/test/www/\346\227\245\346\234\254\350\252\236Dir/\346\227\245\346\234\254\350\252\236File.txt" @@ -0,0 +1 @@ +日本語コンテンツ \ No newline at end of file From 7604602ecaf48e699592f06de5cba0840f1eed62 Mon Sep 17 00:00:00 2001 From: yhirose Date: Wed, 13 Nov 2024 20:59:11 -0500 Subject: [PATCH 2/3] Fixed problems with 'Language for non-Unicode programs' setting on Windows --- httplib.h | 6 ++++-- test/test.cc | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/httplib.h b/httplib.h index f4a6d9cc60..62753e399f 100644 --- a/httplib.h +++ b/httplib.h @@ -2911,13 +2911,15 @@ inline bool mmap::open(const char *path) { #if defined(_WIN32) std::wstring wpath; { + auto cp = ::GetACP(); + auto len = static_cast(strlen(path)); - auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, path, len, nullptr, 0); + auto wlen = ::MultiByteToWideChar(cp, 0, path, len, nullptr, 0); if (wlen <= 0) { return false; } wpath.resize(wlen); auto pwpath = const_cast(reinterpret_cast(wpath.data())); - wlen = ::MultiByteToWideChar(CP_UTF8, 0, path, len, pwpath, wlen); + wlen = ::MultiByteToWideChar(cp, 0, path, len, pwpath, wlen); if (wlen != wpath.size()) { return false; } } diff --git a/test/test.cc b/test/test.cc index dfa4e1a14e..e278a96953 100644 --- a/test/test.cc +++ b/test/test.cc @@ -1,3 +1,4 @@ +// NOTE: This file should be saved as UTF-8 w/ BOM #include #include @@ -241,7 +242,7 @@ TEST(DecodeURLTest, PercentCharacter) { detail::decode_url( R"(descrip=Gastos%20%C3%A1%C3%A9%C3%AD%C3%B3%C3%BA%C3%B1%C3%91%206)", false), - R"(descrip=Gastos áéíóúñÑ 6)"); + u8"descrip=Gastos áéíóúñÑ 6"); } TEST(DecodeURLTest, PercentCharacterNUL) { @@ -267,9 +268,9 @@ TEST(EncodeQueryParamTest, ParseReservedCharactersTest) { } TEST(EncodeQueryParamTest, TestUTF8Characters) { - string chineseCharacters = "中国語"; - string russianCharacters = "дом"; - string brazilianCharacters = "óculos"; + string chineseCharacters = u8"中国語"; + string russianCharacters = u8"дом"; + string brazilianCharacters = u8"óculos"; EXPECT_EQ(detail::encode_query_param(chineseCharacters), "%E4%B8%AD%E5%9B%BD%E8%AA%9E"); @@ -5289,7 +5290,7 @@ TEST(MountTest, MultibytesPathName) { auto res = cli.Get("/日本語Dir/日本語File.txt"); ASSERT_TRUE(res); EXPECT_EQ(StatusCode::OK_200, res->status); - EXPECT_EQ("日本語コンテンツ", res->body); + EXPECT_EQ(u8"日本語コンテンツ", res->body); } TEST(KeepAliveTest, ReadTimeout) { From cc89f4d170cc2a77af79d0c7672963986aefce0e Mon Sep 17 00:00:00 2001 From: yhirose Date: Wed, 13 Nov 2024 22:09:31 -0500 Subject: [PATCH 3/3] Fix problems on English locale --- httplib.h | 40 +++++++++++++++++++++++++++------------- test/test.cc | 2 +- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/httplib.h b/httplib.h index 62753e399f..d75dcf0df5 100644 --- a/httplib.h +++ b/httplib.h @@ -2258,13 +2258,33 @@ make_basic_authentication_header(const std::string &username, namespace detail { +#if defined(_WIN32) +std::wstring u8string_to_wstring(const char *s) { + std::wstring ws; + auto len = static_cast(strlen(s)); + auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0); + if (wlen > 0) { + ws.resize(wlen); + wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, const_cast(reinterpret_cast(ws.data())), wlen); + if (wlen != ws.size()) { + ws.clear(); + } + } + return ws; +} +#endif + struct FileStat { FileStat(const std::string &path); bool is_file() const; bool is_dir() const; private: +#if defined(_WIN32) + struct _stat st_; +#else struct stat st_; +#endif int ret_ = -1; }; @@ -2639,7 +2659,12 @@ inline bool is_valid_path(const std::string &path) { } inline FileStat::FileStat(const std::string &path) { +#if defined(_WIN32) + auto wpath = u8string_to_wstring(path.c_str()); + ret_ = _wstat(wpath.c_str(), &st_); +#else ret_ = stat(path.c_str(), &st_); +#endif } inline bool FileStat::is_file() const { return ret_ >= 0 && S_ISREG(st_.st_mode); @@ -2909,19 +2934,8 @@ inline bool mmap::open(const char *path) { close(); #if defined(_WIN32) - std::wstring wpath; - { - auto cp = ::GetACP(); - - auto len = static_cast(strlen(path)); - auto wlen = ::MultiByteToWideChar(cp, 0, path, len, nullptr, 0); - if (wlen <= 0) { return false; } - - wpath.resize(wlen); - auto pwpath = const_cast(reinterpret_cast(wpath.data())); - wlen = ::MultiByteToWideChar(cp, 0, path, len, pwpath, wlen); - if (wlen != wpath.size()) { return false; } - } + auto wpath = u8string_to_wstring(path); + if (wpath.empty()) { return false; } #if _WIN32_WINNT >= _WIN32_WINNT_WIN8 hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, diff --git a/test/test.cc b/test/test.cc index e278a96953..556bf2497d 100644 --- a/test/test.cc +++ b/test/test.cc @@ -5287,7 +5287,7 @@ TEST(MountTest, MultibytesPathName) { Client cli("localhost", PORT); - auto res = cli.Get("/日本語Dir/日本語File.txt"); + auto res = cli.Get(u8"/日本語Dir/日本語File.txt"); ASSERT_TRUE(res); EXPECT_EQ(StatusCode::OK_200, res->status); EXPECT_EQ(u8"日本語コンテンツ", res->body);