Added support for paths containing unicode characters on Windows (#921)
Co-authored-by: Christophe Greisberger <christophe@greisberger.fr> Co-authored-by: an-tao <antao2002@gmail.com> Co-authored-by: marty1885 <marty188586@gmail.com>
This commit is contained in:
parent
f87a6ca1b8
commit
d888816997
|
@ -91,9 +91,10 @@ include(CheckIncludeFileCXX)
|
|||
check_include_file_cxx(any HAS_ANY)
|
||||
check_include_file_cxx(string_view HAS_STRING_VIEW)
|
||||
check_include_file_cxx(coroutine HAS_COROUTINE)
|
||||
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
|
||||
check_include_file_cxx(filesystem HAS_FILESYSTEM)
|
||||
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE AND HAS_FILESYSTEM)
|
||||
set(DROGON_CXX_STANDARD 20)
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW)
|
||||
elseif (HAS_ANY AND HAS_STRING_VIEW AND HAS_FILESYSTEM)
|
||||
set(DROGON_CXX_STANDARD 17)
|
||||
else ()
|
||||
set(DROGON_CXX_STANDARD 14)
|
||||
|
@ -128,14 +129,33 @@ else (NOT WIN32)
|
|||
endif (NOT WIN32)
|
||||
|
||||
if (DROGON_CXX_STANDARD EQUAL 14)
|
||||
# With C++14, use boost to support any and string_view
|
||||
# With C++14, use Boost to support any and string_view
|
||||
message(STATUS "use c++14")
|
||||
find_package(Boost 1.61.0 REQUIRED)
|
||||
message(STATUS "boost include dir:" ${Boost_INCLUDE_DIR})
|
||||
find_package(Boost 1.61.0 REQUIRED COMPONENTS filesystem)
|
||||
message(STATUS "Using Boost filesytem, string_view and any")
|
||||
message(STATUS "Boost include dir: " ${Boost_INCLUDE_DIR})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem)
|
||||
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
|
||||
elseif (DROGON_CXX_STANDARD EQUAL 17)
|
||||
# With C++17, use Boost if std::filesystem::path is missing
|
||||
message(STATUS "use c++17")
|
||||
# Check for partial implementation of c++17 (Windows/OSX only?)
|
||||
try_compile(check_filesystem_path ${CMAKE_BINARY_DIR}/cmaketest
|
||||
${PROJECT_SOURCE_DIR}/cmake/tests/check_has_std_filesystem_path.cc
|
||||
CXX_STANDARD 17)
|
||||
if (check_filesystem_path)
|
||||
message(STATUS "Using c++17 filesytem::path")
|
||||
else()
|
||||
find_package(Boost 1.49.0 COMPONENTS filesystem system REQUIRED)
|
||||
message(STATUS "Using Boost filesytem::path")
|
||||
message(STATUS "Boost include dir: " ${Boost_INCLUDE_DIR})
|
||||
include_directories(${BOOST_INCLUDE_DIRS})
|
||||
message(STATUS "Boost libraries: " ${Boost_LIBRARIES})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem Boost::system)
|
||||
list(APPEND INCLUDE_DIRS_FOR_DYNAMIC_VIEW ${Boost_INCLUDE_DIR})
|
||||
endif()
|
||||
else ()
|
||||
message(STATUS "use c++20")
|
||||
endif ()
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include <filesystem>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::filesystem::path aPath("../");
|
||||
return 0;
|
||||
}
|
|
@ -32,10 +32,12 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
|
|||
# ##############################################################################
|
||||
|
||||
if (CMAKE_CXX_STANDARD LESS 17)
|
||||
# With C++14, use boost to support any and string_view
|
||||
# With C++14, use boost to support any, string_view and filesystem
|
||||
message(STATUS "use c++14")
|
||||
find_package(Boost 1.61.0 REQUIRED)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
|
||||
find_package(Boost 1.61.0 REQUIRED COMPONENTS filesystem)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC Boost::filesystem)
|
||||
elseif (CMAKE_CXX_STANDARD LESS 20)
|
||||
message(STATUS "use c++17")
|
||||
else ()
|
||||
|
|
|
@ -154,6 +154,39 @@ DROGON_EXPORT std::string formattedString(const char *format, ...);
|
|||
*/
|
||||
DROGON_EXPORT int createPath(const std::string &path);
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* @brief Convert a UTF-8 path with arbitrary directory separator to a standard
|
||||
* Windows UCS2 path.
|
||||
* @note Although windows accept both slash and backslash as directory
|
||||
* separator, it is better to stick to its standard.
|
||||
*
|
||||
* @param strUtf8Path Ascii path considered as being UTF-8
|
||||
*
|
||||
* @return std::wstring path, with windows standard backslash directory
|
||||
* separator.
|
||||
*/
|
||||
DROGON_EXPORT std::wstring toNativePath(const std::string &strPath);
|
||||
/**
|
||||
* @brief Convert a UCS2 to an UTF-8 path.
|
||||
*
|
||||
* @param strPath Wide char unicode path
|
||||
*
|
||||
* @return std::string path in UTF-8 unicode, with standard '/' directory
|
||||
* separator.
|
||||
*/
|
||||
DROGON_EXPORT std::string fromNativePath(std::wstring strPath);
|
||||
#else // _WIN32
|
||||
inline const std::string &toNativePath(const std::string &strPath)
|
||||
{
|
||||
return strPath;
|
||||
}
|
||||
inline const std::string &fromNativePath(const std::string &strPath)
|
||||
{
|
||||
return strPath;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
/// Replace all occurances of from to to inplace
|
||||
/**
|
||||
* @param from string to replace
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <trantor/utils/Logger.h>
|
||||
#ifdef _WIN32
|
||||
#include <mman.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -29,7 +30,8 @@ CacheFile::CacheFile(const std::string &path, bool autoDelete)
|
|||
#ifndef _MSC_VER
|
||||
file_ = fopen(path_.data(), "wb+");
|
||||
#else
|
||||
if (fopen_s(&file_, path_.data(), "wb+") != 0)
|
||||
auto wPath{drogon::utils::toNativePath(path)};
|
||||
if (_wfopen_s(&file_, wPath.c_str(), L"wb+") != 0)
|
||||
{
|
||||
file_ = nullptr;
|
||||
}
|
||||
|
@ -46,7 +48,8 @@ CacheFile::~CacheFile()
|
|||
{
|
||||
fclose(file_);
|
||||
#ifdef _WIN32
|
||||
_unlink(path_.data());
|
||||
auto wPath{drogon::utils::toNativePath(path_)};
|
||||
_wunlink(wPath.c_str());
|
||||
#else
|
||||
unlink(path_.data());
|
||||
#endif
|
||||
|
|
|
@ -22,9 +22,14 @@
|
|||
#include <trantor/utils/Logger.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#define os_access access
|
||||
#else
|
||||
#include <io.h>
|
||||
#define os_access _waccess
|
||||
#define R_OK 04
|
||||
#define W_OK 02
|
||||
#endif
|
||||
#include <drogon/utils/Utilities.h>
|
||||
|
||||
using namespace drogon;
|
||||
static bool bytesSize(std::string &sizeStr, size_t &size)
|
||||
|
@ -94,27 +99,20 @@ static bool bytesSize(std::string &sizeStr, size_t &size)
|
|||
}
|
||||
ConfigLoader::ConfigLoader(const std::string &configFile)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (_access(configFile.c_str(), 0) != 0)
|
||||
#else
|
||||
if (access(configFile.c_str(), 0) != 0)
|
||||
#endif
|
||||
if (os_access(drogon::utils::toNativePath(configFile).c_str(), 0) != 0)
|
||||
{
|
||||
std::cerr << "Config file " << configFile << " not found!" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (_access(configFile.c_str(), 04) != 0)
|
||||
#else
|
||||
if (access(configFile.c_str(), R_OK) != 0)
|
||||
#endif
|
||||
if (os_access(drogon::utils::toNativePath(configFile).c_str(), R_OK) != 0)
|
||||
{
|
||||
std::cerr << "No permission to read config file " << configFile
|
||||
<< std::endl;
|
||||
exit(1);
|
||||
}
|
||||
configFile_ = configFile;
|
||||
std::ifstream infile(configFile.c_str(), std::ifstream::in);
|
||||
std::ifstream infile(drogon::utils::toNativePath(configFile).c_str(),
|
||||
std::ifstream::in);
|
||||
if (infile)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <drogon/HttpTypes.h>
|
||||
#include <drogon/Session.h>
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include "filesystem.h"
|
||||
#include <trantor/utils/AsyncFileLogger.h>
|
||||
#include <json/json.h>
|
||||
|
||||
|
@ -58,8 +59,12 @@
|
|||
#include <sys/file.h>
|
||||
#include <uuid.h>
|
||||
#include <unistd.h>
|
||||
#define os_access access
|
||||
#else
|
||||
#include <io.h>
|
||||
#define os_access _waccess
|
||||
#define R_OK 04
|
||||
#define W_OK 02
|
||||
#endif
|
||||
|
||||
using namespace drogon;
|
||||
|
@ -393,20 +398,14 @@ HttpAppFramework &HttpAppFrameworkImpl::setLogPath(
|
|||
{
|
||||
if (logPath.empty())
|
||||
return *this;
|
||||
#ifdef _WIN32
|
||||
if (_access(logPath.c_str(), 0) != 0)
|
||||
#else
|
||||
if (access(logPath.c_str(), 0) != 0)
|
||||
#endif
|
||||
// std::filesystem does not provide a method to check access permissions, so
|
||||
// keep existing code
|
||||
if (os_access(utils::toNativePath(logPath).c_str(), 0) != 0)
|
||||
{
|
||||
std::cerr << "Log path does not exist!\n";
|
||||
exit(1);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (_access(logPath.c_str(), 06) != 0)
|
||||
#else
|
||||
if (access(logPath.c_str(), R_OK | W_OK) != 0)
|
||||
#endif
|
||||
if (os_access(utils::toNativePath(logPath).c_str(), R_OK | W_OK) != 0)
|
||||
{
|
||||
std::cerr << "Unable to access log path!\n";
|
||||
exit(1);
|
||||
|
@ -493,11 +492,9 @@ void HttpAppFrameworkImpl::run()
|
|||
// set logger
|
||||
if (!logPath_.empty())
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (_access(logPath_.c_str(), 06) != 0)
|
||||
#else
|
||||
if (access(logPath_.c_str(), R_OK | W_OK) != 0)
|
||||
#endif
|
||||
// std::filesystem does not provide a method to check access
|
||||
// permissions, so keep existing code
|
||||
if (os_access(utils::toNativePath(logPath_).c_str(), R_OK | W_OK) != 0)
|
||||
{
|
||||
LOG_ERROR << "log file path not exist";
|
||||
abort();
|
||||
|
@ -666,22 +663,14 @@ HttpAppFramework &HttpAppFrameworkImpl::setUploadPath(
|
|||
const std::string &uploadPath)
|
||||
{
|
||||
assert(!uploadPath.empty());
|
||||
if (uploadPath[0] == '/' ||
|
||||
(uploadPath.length() >= 2 && uploadPath[0] == '.' &&
|
||||
uploadPath[1] == '/') ||
|
||||
(uploadPath.length() >= 3 && uploadPath[0] == '.' &&
|
||||
uploadPath[1] == '.' && uploadPath[2] == '/') ||
|
||||
uploadPath == "." || uploadPath == "..")
|
||||
|
||||
filesystem::path fsUploadPath(utils::toNativePath(uploadPath));
|
||||
if (!fsUploadPath.is_absolute())
|
||||
{
|
||||
uploadPath_ = uploadPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rootPath_[rootPath_.length() - 1] == '/')
|
||||
uploadPath_ = rootPath_ + uploadPath;
|
||||
else
|
||||
uploadPath_ = rootPath_ + "/" + uploadPath;
|
||||
filesystem::path fsRoot(utils::toNativePath(rootPath_));
|
||||
fsUploadPath = fsRoot / fsUploadPath;
|
||||
}
|
||||
uploadPath_ = utils::fromNativePath(fsUploadPath.native());
|
||||
return *this;
|
||||
}
|
||||
void HttpAppFrameworkImpl::findSessionForRequest(const HttpRequestImplPtr &req)
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
#include <drogon/MultiPart.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
// Switch between native c++17 or boost for c++14
|
||||
#ifdef HAS_STD_FILESYSTEM_PATH
|
||||
#include <system_error>
|
||||
namespace stl = std;
|
||||
#else // HAS_STD_FILESYSTEM_PATH
|
||||
#include <boost/system/error_code.hpp>
|
||||
namespace stl = boost::system;
|
||||
#endif // HAS_STD_FILESYSTEM_PATH
|
||||
|
||||
using namespace drogon;
|
||||
// Verify if last char of path is a slash, otherwise, add the slash
|
||||
static inline void ensureSlashPostfix(std::string &path)
|
||||
{
|
||||
if (path[path.length() - 1] != '/')
|
||||
path += '/';
|
||||
}
|
||||
|
||||
int HttpFileImpl::save() const
|
||||
{
|
||||
|
@ -36,59 +38,43 @@ int HttpFileImpl::save(const std::string &path) const
|
|||
assert(!path.empty());
|
||||
if (fileName_.empty())
|
||||
return -1;
|
||||
std::string fileName;
|
||||
if (path[0] == '/' ||
|
||||
(path.length() >= 2 && path[0] == '.' && path[1] == '/') ||
|
||||
(path.length() >= 3 && path[0] == '.' && path[1] == '.' &&
|
||||
path[2] == '/') ||
|
||||
path == "." || path == "..")
|
||||
filesystem::path fsPath(utils::toNativePath(path));
|
||||
if (!fsPath.is_absolute() &&
|
||||
(!fsPath.has_parent_path() ||
|
||||
(fsPath.begin()->string() != "." && fsPath.begin()->string() != "..")))
|
||||
{
|
||||
// Absolute or relative path
|
||||
fileName = path;
|
||||
filesystem::path fsUploadPath(utils::toNativePath(
|
||||
HttpAppFrameworkImpl::instance().getUploadPath()));
|
||||
fsPath = fsUploadPath / fsPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName = HttpAppFrameworkImpl::instance().getUploadPath();
|
||||
ensureSlashPostfix(fileName);
|
||||
fileName += path;
|
||||
}
|
||||
if (utils::createPath(fileName) < 0)
|
||||
return -1;
|
||||
ensureSlashPostfix(fileName);
|
||||
fileName += fileName_;
|
||||
return saveTo(fileName);
|
||||
filesystem::path fsFileName(utils::toNativePath(fileName_));
|
||||
return saveTo(fsPath / fsFileName);
|
||||
}
|
||||
int HttpFileImpl::saveAs(const std::string &fileName) const
|
||||
{
|
||||
assert(!fileName.empty());
|
||||
std::string pathAndFileName;
|
||||
if (fileName[0] == '/' ||
|
||||
(fileName.length() >= 2 && fileName[0] == '.' && fileName[1] == '/') ||
|
||||
(fileName.length() >= 3 && fileName[0] == '.' && fileName[1] == '.' &&
|
||||
fileName[2] == '/'))
|
||||
filesystem::path fsFileName(utils::toNativePath(fileName));
|
||||
if (!fsFileName.is_absolute() && (!fsFileName.has_parent_path() ||
|
||||
(fsFileName.begin()->string() != "." &&
|
||||
fsFileName.begin()->string() != "..")))
|
||||
{
|
||||
// Absolute or relative path
|
||||
pathAndFileName = fileName;
|
||||
filesystem::path fsUploadPath(utils::toNativePath(
|
||||
HttpAppFrameworkImpl::instance().getUploadPath()));
|
||||
fsFileName = fsUploadPath / fsFileName;
|
||||
}
|
||||
else
|
||||
if (fsFileName.has_parent_path())
|
||||
{
|
||||
pathAndFileName = HttpAppFrameworkImpl::instance().getUploadPath();
|
||||
ensureSlashPostfix(pathAndFileName);
|
||||
pathAndFileName += fileName;
|
||||
}
|
||||
auto pathPos = pathAndFileName.rfind('/');
|
||||
if (pathPos != std::string::npos)
|
||||
{
|
||||
std::string path = pathAndFileName.substr(0, pathPos);
|
||||
if (utils::createPath(path) < 0)
|
||||
stl::error_code err;
|
||||
filesystem::create_directories(fsFileName.parent_path(), err);
|
||||
if (err)
|
||||
return -1;
|
||||
}
|
||||
return saveTo(pathAndFileName);
|
||||
return saveTo(fsFileName);
|
||||
}
|
||||
int HttpFileImpl::saveTo(const std::string &pathAndFileName) const
|
||||
int HttpFileImpl::saveTo(const filesystem::path &pathAndFileName) const
|
||||
{
|
||||
LOG_TRACE << "save uploaded file:" << pathAndFileName;
|
||||
std::ofstream file(pathAndFileName, std::ios::binary);
|
||||
std::ofstream file(pathAndFileName.native(), std::ios::binary);
|
||||
if (file.is_open())
|
||||
{
|
||||
file.write(fileContent_.data(), fileContent_.size());
|
||||
|
@ -101,6 +87,7 @@ int HttpFileImpl::saveTo(const std::string &pathAndFileName) const
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string HttpFileImpl::getMd5() const
|
||||
{
|
||||
return utils::getMd5(fileContent_.data(), fileContent_.size());
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
#pragma once
|
||||
#include "HttpUtils.h"
|
||||
#include <drogon/utils/string_view.h>
|
||||
#include "filesystem.h"
|
||||
#include <drogon/HttpRequest.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
class HttpFileImpl
|
||||
|
@ -110,7 +112,8 @@ class HttpFileImpl
|
|||
|
||||
/// Return the md5 string of the file
|
||||
std::string getMd5() const;
|
||||
int saveTo(const std::string &pathAndFileName) const;
|
||||
// int saveTo(const std::string &pathAndFileName) const;
|
||||
int saveTo(const filesystem::path &pathAndFileName) const;
|
||||
void setRequest(const HttpRequestPtr &req)
|
||||
{
|
||||
requestPtr_ = req;
|
||||
|
|
|
@ -287,7 +287,8 @@ void HttpRequestImpl::appendToBuffer(trantor::MsgBuffer *output) const
|
|||
content.append("\"; filename=\"");
|
||||
content.append(file.fileName());
|
||||
content.append("\"\r\n\r\n");
|
||||
std::ifstream infile(file.path(), std::ifstream::binary);
|
||||
std::ifstream infile(utils::toNativePath(file.path()),
|
||||
std::ifstream::binary);
|
||||
if (!infile)
|
||||
{
|
||||
LOG_ERROR << file.path() << " not found";
|
||||
|
|
|
@ -17,14 +17,19 @@
|
|||
#include "HttpUtils.h"
|
||||
#include <drogon/HttpViewData.h>
|
||||
#include <drogon/IOThreadStorage.h>
|
||||
#include "filesystem.h"
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#ifdef _WIN32
|
||||
#define stat _stati64
|
||||
// Switch between native c++17 or boost for c++14
|
||||
#ifdef HAS_STD_FILESYSTEM_PATH
|
||||
namespace stl = std;
|
||||
#else
|
||||
namespace stl = boost::system;
|
||||
#endif
|
||||
|
||||
using namespace trantor;
|
||||
using namespace drogon;
|
||||
|
||||
|
@ -235,7 +240,7 @@ HttpResponsePtr HttpResponse::newFileResponse(
|
|||
const std::string &attachmentFileName,
|
||||
ContentType type)
|
||||
{
|
||||
std::ifstream infile(fullPath, std::ifstream::binary);
|
||||
std::ifstream infile(utils::toNativePath(fullPath), std::ifstream::binary);
|
||||
LOG_TRACE << "send http file:" << fullPath;
|
||||
if (!infile)
|
||||
{
|
||||
|
@ -345,19 +350,19 @@ void HttpResponseImpl::makeHeaderString(trantor::MsgBuffer &buffer)
|
|||
}
|
||||
else
|
||||
{
|
||||
struct stat filestat
|
||||
stl::error_code err;
|
||||
filesystem::path fsSendfile(utils::toNativePath(sendfileName_));
|
||||
auto fileSize = filesystem::file_size(fsSendfile, err);
|
||||
if (err)
|
||||
{
|
||||
};
|
||||
if (stat(sendfileName_.data(), &filestat) < 0)
|
||||
{
|
||||
LOG_SYSERR << sendfileName_ << " stat error";
|
||||
LOG_SYSERR << fsSendfile << " stat error " << err.value()
|
||||
<< ": " << err.message();
|
||||
return;
|
||||
}
|
||||
len = snprintf(
|
||||
buffer.beginWrite(),
|
||||
len = snprintf(buffer.beginWrite(),
|
||||
buffer.writableBytes(),
|
||||
contentLengthFormatString<decltype(filestat.st_size)>(),
|
||||
filestat.st_size);
|
||||
contentLengthFormatString<decltype(fileSize)>(),
|
||||
fileSize);
|
||||
}
|
||||
buffer.hasWritten(len);
|
||||
if (headers_.find("connection") == headers_.end())
|
||||
|
|
|
@ -23,11 +23,20 @@
|
|||
#ifndef _WIN32
|
||||
#include <sys/file.h>
|
||||
#else
|
||||
#define stat _stati64
|
||||
#define stat _wstati64
|
||||
#define S_ISREG(m) (((m)&0170000) == (0100000))
|
||||
#define S_ISDIR(m) (((m)&0170000) == (0040000))
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
// Switch between native c++17 or boost for c++14
|
||||
#include "filesystem.h"
|
||||
#ifdef HAS_STD_FILESYSTEM_PATH
|
||||
#include <system_error>
|
||||
namespace stl = std;
|
||||
#else
|
||||
#include <boost/system/error_code.hpp>
|
||||
namespace stl = boost::system;
|
||||
#endif
|
||||
|
||||
using namespace drogon;
|
||||
|
||||
|
@ -137,13 +146,14 @@ void StaticFileRouter::route(
|
|||
std::string filePath =
|
||||
location.realLocation_ +
|
||||
std::string{restOfThePath.data(), restOfThePath.length()};
|
||||
struct stat fileStat;
|
||||
if (stat(filePath.c_str(), &fileStat) != 0)
|
||||
filesystem::path fsFilePath(utils::toNativePath(filePath));
|
||||
stl::error_code err;
|
||||
if (!filesystem::exists(fsFilePath, err))
|
||||
{
|
||||
defaultHandler_(req, std::move(callback));
|
||||
return;
|
||||
}
|
||||
if (S_ISDIR(fileStat.st_mode))
|
||||
if (filesystem::is_directory(fsFilePath, err))
|
||||
{
|
||||
// Check if path is eligible for an implicit index.html
|
||||
if (implicitPageEnable_)
|
||||
|
@ -215,10 +225,11 @@ void StaticFileRouter::route(
|
|||
|
||||
std::string directoryPath =
|
||||
HttpAppFrameworkImpl::instance().getDocumentRoot() + path;
|
||||
struct stat fileStat;
|
||||
if (stat(directoryPath.c_str(), &fileStat) == 0)
|
||||
filesystem::path fsDirectoryPath(utils::toNativePath(directoryPath));
|
||||
stl::error_code err;
|
||||
if (filesystem::exists(fsDirectoryPath, err))
|
||||
{
|
||||
if (S_ISDIR(fileStat.st_mode))
|
||||
if (filesystem::is_directory(fsDirectoryPath, err))
|
||||
{
|
||||
// Check if path is eligible for an implicit index.html
|
||||
if (implicitPageEnable_)
|
||||
|
@ -299,9 +310,15 @@ void StaticFileRouter::sendStaticFileResponse(
|
|||
}
|
||||
else
|
||||
{
|
||||
struct stat fileStat;
|
||||
LOG_TRACE << "enabled LastModify";
|
||||
if (stat(filePath.c_str(), &fileStat) == 0 &&
|
||||
// std::filesystem::file_time_type::clock::to_time_t still not
|
||||
// implemented by M$, even in c++20, so keep calls to stat()
|
||||
#ifdef _WIN32
|
||||
struct _stati64 fileStat;
|
||||
#else // _WIN32
|
||||
struct stat fileStat;
|
||||
#endif // _WIN32
|
||||
if (stat(utils::toNativePath(filePath).c_str(), &fileStat) == 0 &&
|
||||
S_ISREG(fileStat.st_mode))
|
||||
{
|
||||
fileExists = true;
|
||||
|
@ -361,9 +378,10 @@ void StaticFileRouter::sendStaticFileResponse(
|
|||
}
|
||||
if (!fileExists)
|
||||
{
|
||||
struct stat fileStat;
|
||||
if (stat(filePath.c_str(), &fileStat) != 0 ||
|
||||
!S_ISREG(fileStat.st_mode))
|
||||
filesystem::path fsFilePath(utils::toNativePath(filePath));
|
||||
stl::error_code err;
|
||||
if (!filesystem::exists(fsFilePath, err) ||
|
||||
!filesystem::is_regular_file(fsFilePath, err))
|
||||
{
|
||||
defaultHandler_(req, std::move(callback));
|
||||
return;
|
||||
|
@ -383,9 +401,10 @@ void StaticFileRouter::sendStaticFileResponse(
|
|||
{
|
||||
// Find compressed file first.
|
||||
auto brFileName = filePath + ".br";
|
||||
struct stat filestat;
|
||||
if (stat(brFileName.c_str(), &filestat) == 0 &&
|
||||
S_ISREG(filestat.st_mode))
|
||||
filesystem::path fsBrFile(utils::toNativePath(brFileName));
|
||||
stl::error_code err;
|
||||
if (filesystem::exists(fsBrFile, err) &&
|
||||
filesystem::is_regular_file(fsBrFile, err))
|
||||
{
|
||||
resp =
|
||||
HttpResponse::newFileResponse(brFileName,
|
||||
|
@ -399,9 +418,10 @@ void StaticFileRouter::sendStaticFileResponse(
|
|||
{
|
||||
// Find compressed file first.
|
||||
auto gzipFileName = filePath + ".gz";
|
||||
struct stat filestat;
|
||||
if (stat(gzipFileName.c_str(), &filestat) == 0 &&
|
||||
S_ISREG(filestat.st_mode))
|
||||
filesystem::path fsGzipFile(utils::toNativePath(gzipFileName));
|
||||
stl::error_code err;
|
||||
if (filesystem::exists(fsGzipFile, err) &&
|
||||
filesystem::is_regular_file(fsGzipFile, err))
|
||||
{
|
||||
resp =
|
||||
HttpResponse::newFileResponse(gzipFileName,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
|
||||
#include <drogon/utils/Utilities.h>
|
||||
#include "filesystem.h"
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <drogon/config.h>
|
||||
#ifdef OpenSSL_FOUND
|
||||
|
@ -51,6 +52,15 @@
|
|||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// Switch between native c++17 or boost for c++14
|
||||
#ifdef HAS_STD_FILESYSTEM_PATH
|
||||
#include <system_error>
|
||||
namespace stl = std;
|
||||
#else
|
||||
#include <boost/system/error_code.hpp>
|
||||
namespace stl = boost::system;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
char *strptime(const char *s, const char *f, struct tm *tm)
|
||||
{
|
||||
|
@ -1043,61 +1053,70 @@ std::string formattedString(const char *format, ...)
|
|||
return strBuffer;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string utf8_encode(const std::wstring &wstr)
|
||||
{
|
||||
if (wstr.empty())
|
||||
return {};
|
||||
int nSizeNeeded = ::WideCharToMultiByte(
|
||||
CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
|
||||
std::string strTo(nSizeNeeded, 0);
|
||||
::WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
&wstr[0],
|
||||
(int)wstr.size(),
|
||||
&strTo[0],
|
||||
nSizeNeeded,
|
||||
NULL,
|
||||
NULL);
|
||||
return strTo;
|
||||
}
|
||||
std::wstring utf8_decode(const std::string &str)
|
||||
{
|
||||
if (str.empty())
|
||||
return {};
|
||||
int nSizeNeeded =
|
||||
::MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
|
||||
std::wstring wstrTo(nSizeNeeded, 0);
|
||||
::MultiByteToWideChar(
|
||||
CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], nSizeNeeded);
|
||||
return wstrTo;
|
||||
}
|
||||
|
||||
std::wstring toNativePath(const std::string &strUtf8Path)
|
||||
{
|
||||
// Consider path to be utf-8, to allow using paths with unicode characters
|
||||
auto wPath{utf8_decode(strUtf8Path)};
|
||||
// Not needed: normalize path (just replaces '/' with '\')
|
||||
filesystem::path fsPath(wPath);
|
||||
// Not needed: normalize path (just replaces '/' with '\')
|
||||
fsPath.make_preferred();
|
||||
return fsPath.native();
|
||||
}
|
||||
|
||||
std::string fromNativePath(std::wstring wstrPath)
|
||||
{
|
||||
std::replace(wstrPath.begin(), wstrPath.end(), L'\\', L'/');
|
||||
auto strPath{utf8_encode(wstrPath)};
|
||||
return strPath;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
int createPath(const std::string &path)
|
||||
{
|
||||
auto tmpPath = path;
|
||||
std::stack<std::string> pathStack;
|
||||
#ifdef _WIN32
|
||||
while (_access(tmpPath.c_str(), 06) != 0)
|
||||
#else
|
||||
while (access(tmpPath.c_str(), F_OK) != 0)
|
||||
#endif
|
||||
if (path.empty())
|
||||
return 0;
|
||||
auto osPath{toNativePath(path)};
|
||||
if (osPath.back() != filesystem::path::preferred_separator)
|
||||
osPath.push_back(filesystem::path::preferred_separator);
|
||||
filesystem::path fsPath(osPath);
|
||||
stl::error_code err;
|
||||
filesystem::create_directories(fsPath, err);
|
||||
if (err)
|
||||
{
|
||||
if (tmpPath == "./" || tmpPath == "/")
|
||||
LOG_ERROR << "Error " << err.value() << " creating path " << osPath
|
||||
<< ": " << err.message();
|
||||
return -1;
|
||||
while (tmpPath[tmpPath.length() - 1] == '/')
|
||||
tmpPath.resize(tmpPath.length() - 1);
|
||||
auto pos = tmpPath.rfind('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
pathStack.push(tmpPath.substr(pos));
|
||||
tmpPath = tmpPath.substr(0, pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pathStack.push(tmpPath);
|
||||
tmpPath.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (pathStack.size() > 0)
|
||||
{
|
||||
if (tmpPath.empty())
|
||||
{
|
||||
tmpPath = pathStack.top();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmpPath[tmpPath.length() - 1] == '/')
|
||||
{
|
||||
tmpPath.append(pathStack.top());
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpPath.append("/").append(pathStack.top());
|
||||
}
|
||||
}
|
||||
pathStack.pop();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (_mkdir(tmpPath.c_str()) == -1)
|
||||
#else
|
||||
if (mkdir(tmpPath.c_str(), 0755) == -1)
|
||||
#endif
|
||||
{
|
||||
LOG_ERROR << "Can't create path:" << path;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* @author An Tao
|
||||
*
|
||||
* Copyright 2018, An Tao. All rights reserved.
|
||||
* https://github.com/an-tao/drogon
|
||||
* Use of this source code is governed by a MIT license
|
||||
* that can be found in the License file.
|
||||
*
|
||||
* Drogon
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Check of std::filesystem::path availability:
|
||||
* - OS X: depends on the target OSX version (>= 10.15 Catalina)
|
||||
* - Windows: Visual Studio >= 2019 (c++20)
|
||||
* - Others: should already have it in c++17
|
||||
*/
|
||||
#if (defined(__APPLE__) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500L) || \
|
||||
(defined(_WIN32) && defined(_MSC_VER) && _MSC_VER >= 1920) || \
|
||||
(!defined(__APPLE__) && !defined(_WIN32) && __cplusplus >= 201703L)
|
||||
#define HAS_STD_FILESYSTEM_PATH
|
||||
#endif
|
||||
|
||||
#ifdef HAS_STD_FILESYSTEM_PATH
|
||||
#include <filesystem>
|
||||
#else
|
||||
#include <boost/filesystem.hpp>
|
||||
#endif
|
||||
|
||||
namespace drogon
|
||||
{
|
||||
#ifdef HAS_STD_FILESYSTEM_PATH
|
||||
namespace filesystem = std::filesystem;
|
||||
#else
|
||||
namespace filesystem = boost::filesystem;
|
||||
#endif
|
||||
} // namespace drogon
|
||||
|
||||
namespace trantor
|
||||
{
|
||||
inline LogStream &operator<<(LogStream &ls, const drogon::filesystem::path &v)
|
||||
{
|
||||
#if defined(_WIN32) && defined(__cpp_char8_t)
|
||||
// Convert UCS-2 to UTF-8, not ASCII - not needed on other OSes
|
||||
auto u8path{v.u8string()};
|
||||
ls.append((const char *)u8path.data(), u8path.length());
|
||||
#else
|
||||
// No need to convert
|
||||
ls.append(v.string().data(), v.string().length());
|
||||
#endif
|
||||
return ls;
|
||||
}
|
||||
} // namespace trantor
|
2
trantor
2
trantor
|
@ -1 +1 @@
|
|||
Subproject commit 15556700e5ce305c7d3fd5a970a58ef6b8297e0c
|
||||
Subproject commit d7f700ac7ca0b6c656755142378bc71cd34ecfed
|
Loading…
Reference in New Issue