Просмотр исходного кода

修复HTTP服务Windows性能异常和中文路径相关Bug (#701)

* fix windows defaultStaticHandler abnormal performance
fix http chinese name file cache bug
support http index directory view chinese name
add high-performance wchar_to_utf8 and utf8_to_wchar

* FileCache Open Performance Optimization
宅の士 8 месяцев назад
Родитель
Сommit
70f0a8f322
4 измененных файлов с 63 добавлено и 30 удалено
  1. 5 4
      cpputil/hdir.cpp
  2. 21 0
      cpputil/hstring.cpp
  3. 6 0
      cpputil/hstring.h
  4. 31 26
      http/server/FileCache.cpp

+ 5 - 4
cpputil/hdir.cpp

@@ -3,6 +3,7 @@
 #include "hplatform.h"
 
 #ifdef OS_WIN
+#include "hstring.h" // import hv::utf8_to_wchar hv::wchar_to_utf8
 //FILETIME starts from 1601-01-01 UTC, epoch from 1970-01-01 UTC
 //FILETIME unit (100ns)
 #define FILETIME_EPOCH_DIFF     11644473600 // s
@@ -61,15 +62,15 @@ int listdir(const char* dir, std::list<hdir_t>& dirs) {
 #elif defined(OS_WIN)
     // FindFirstFile -> FindNextFile -> FindClose
     strcat(path, "*");
-    WIN32_FIND_DATAA data;
-    HANDLE h = FindFirstFileA(path, &data);
+    WIN32_FIND_DATAW data;
+    HANDLE h = FindFirstFileW(hv::utf8_to_wchar(path).c_str(), &data);
     if (h == NULL) {
         return -1;
     }
     hdir_t tmp;
     do {
         memset(&tmp, 0, sizeof(hdir_t));
-        strncpy(tmp.name, data.cFileName, sizeof(tmp.name));
+        strncpy(tmp.name, hv::wchar_to_utf8(data.cFileName).c_str(), sizeof(tmp.name));
         tmp.type = 'f';
         if (data.dwFileAttributes & _A_SUBDIR) {
             tmp.type = 'd';
@@ -80,7 +81,7 @@ int listdir(const char* dir, std::list<hdir_t>& dirs) {
         tmp.mtime = FileTime2Epoch(data.ftLastWriteTime);
         tmp.ctime = FileTime2Epoch(data.ftCreationTime);
         dirs.push_back(tmp);
-    } while (FindNextFileA(h, &data));
+    } while (FindNextFileW(h, &data));
     FindClose(h);
 #endif
     dirs.sort(less);

+ 21 - 0
cpputil/hstring.cpp

@@ -225,4 +225,25 @@ std::string NetAddr::to_string(const char* ip, int port) {
     return hv::asprintf(fmt, ip, port);
 }
 
+#ifdef OS_WIN
+std::string wchar_to_utf8(const std::wstring &wstr) {
+  std::string str(4 * wstr.size() + 1, '\0');
+  str.resize(WideCharToMultiByte(
+    CP_UTF8, 0, 
+    wstr.c_str(), wstr.size(), 
+    const_cast<char*>(str.data()), str.size(), 
+    NULL, NULL));
+  return str;
+}
+
+std::wstring utf8_to_wchar(const std::string &str) {
+  std::wstring wstr(2 * str.size() + 1, '\0');
+  wstr.resize(MultiByteToWideChar(
+    CP_UTF8, 0, 
+    str.c_str(), str.size(), 
+    const_cast<wchar_t*>(wstr.data()), wstr.size()));
+  return wstr;
+}
+#endif // OS_WIN
+
 } // end namespace hv

+ 6 - 0
cpputil/hstring.h

@@ -88,6 +88,12 @@ struct HV_EXPORT NetAddr {
     static std::string to_string(const char* ip, int port);
 };
 
+// windows wchar and utf8 conver
+#ifdef OS_WIN
+HV_EXPORT std::string wchar_to_utf8(const std::wstring &wstr);
+HV_EXPORT std::wstring utf8_to_wchar(const std::string &str);
+#endif // OS_WIN
+
 } // end namespace hv
 
 #endif // HV_STRING_H_

+ 31 - 26
http/server/FileCache.cpp

@@ -8,8 +8,8 @@
 #include "httpdef.h"    // import http_content_type_str_by_suffix
 #include "http_page.h"  // import make_index_of_page
 
-#ifdef _MSC_VER
-#include <codecvt>
+#ifdef OS_WIN
+#include "hstring.h" // import hv::utf8_to_wchar
 #endif
 
 #define ETAG_FMT    "\"%zx-%zx\""
@@ -19,27 +19,26 @@ FileCache::FileCache() {
     expired_time  = 60; // s
 }
 
-static int hv_open(char const* filepath, int flags) {
-#ifdef _MSC_VER
-    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
-    auto wfilepath = conv.from_bytes(filepath);
-    int fd = _wopen(wfilepath.c_str(), flags);
-#else
-    int fd = open(filepath, flags);
-#endif
-    return fd;
-}
-
 file_cache_ptr FileCache::Open(const char* filepath, OpenParam* param) {
     std::lock_guard<std::mutex> locker(mutex_);
     file_cache_ptr fc = Get(filepath);
+#ifdef OS_WIN
+    std::wstring wfilepath;
+#endif
     bool modified = false;
     if (fc) {
         time_t now = time(NULL);
         if (now - fc->stat_time > stat_interval) {
-            modified = fc->is_modified();
             fc->stat_time = now;
             fc->stat_cnt++;
+#ifdef OS_WIN
+            wfilepath = hv::utf8_to_wchar(filepath);
+            now = fc->st.st_mtime;
+            _wstat(wfilepath.c_str(), (struct _stat*)&fc->st);
+            modified = now != fc->st.st_mtime;
+#else
+            modified = fc->is_modified();
+#endif
         }
         if (param->need_read) {
             if (!modified && fc->is_complete()) {
@@ -48,31 +47,37 @@ file_cache_ptr FileCache::Open(const char* filepath, OpenParam* param) {
         }
     }
     if (fc == NULL || modified || param->need_read) {
+        struct stat st;
         int flags = O_RDONLY;
 #ifdef O_BINARY
         flags |= O_BINARY;
 #endif
-        int fd = hv_open(filepath, flags);
-        if (fd < 0) {
+        int fd = -1;
 #ifdef OS_WIN
+        if(wfilepath.empty()) wfilepath = hv::utf8_to_wchar(filepath);
+        if(_wstat(wfilepath.c_str(), (struct _stat*)&st) != 0) {
+            param->error = ERR_OPEN_FILE;
+            return NULL;
+        }
+        if(S_ISREG(st.st_mode)) {
+            fd = _wopen(wfilepath.c_str(), flags);
+        }else if (S_ISDIR(st.st_mode)) {
             // NOTE: open(dir) return -1 on windows
-            if (!hv_isdir(filepath)) {
-                param->error = ERR_OPEN_FILE;
-                return NULL;
-            }
+            fd = 0;
+        }
 #else
+        if(stat(filepath, &st) != 0) {
             param->error = ERR_OPEN_FILE;
             return NULL;
+        }
+        fd = open(filepath, flags);
 #endif
+        if (fd < 0) {
+            param->error = ERR_OPEN_FILE;
+            return NULL;
         }
         defer(if (fd > 0) { close(fd); })
         if (fc == NULL) {
-            struct stat st;
-            if (fd > 0) {
-                fstat(fd, &st);
-            } else {
-                stat(filepath, &st);
-            }
             if (S_ISREG(st.st_mode) ||
                 (S_ISDIR(st.st_mode) &&
                  filepath[strlen(filepath)-1] == '/')) {