From 44c62d838e90af68e98cdf6f68bfe531bca15a7e Mon Sep 17 00:00:00 2001 From: yhirose Date: Mon, 31 Jul 2023 07:43:50 -0400 Subject: [PATCH] Use memory mapped file for static file server (#1632) * Use memory mapped file for static file server * Fix build error --- httplib.h | 136 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/httplib.h b/httplib.h index 22858ff..256405f 100644 --- a/httplib.h +++ b/httplib.h @@ -90,10 +90,6 @@ #define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) #endif -#ifndef CPPHTTPLIB_SEND_BUFSIZ -#define CPPHTTPLIB_SEND_BUFSIZ size_t(4096u) -#endif - #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ #define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u) #endif @@ -197,6 +193,7 @@ using socket_t = SOCKET; #endif #include #include +#include #include #include #include @@ -2148,6 +2145,29 @@ private: std::string glowable_buffer_; }; +class mmap { +public: + mmap(const char *path); + ~mmap(); + + bool open(const char *path); + void close(); + + bool is_open() const; + size_t size() const; + const char *data() const; + +private: +#if defined(_WIN32) + HANDLE hFile_; + HANDLE hMapping_; +#else + int fd_; +#endif + size_t size_; + void *addr_; +}; + } // namespace detail // ---------------------------------------------------------------------------- @@ -2528,6 +2548,95 @@ inline void stream_line_reader::append(char c) { } } +inline mmap::mmap(const char *path) +#if defined(_WIN32) + : hFile_(NULL), hMapping_(NULL) +#else + : fd_(-1) +#endif + , + size_(0), addr_(nullptr) { + if (!open(path)) { std::runtime_error(""); } +} + +inline mmap::~mmap() { close(); } + +inline bool mmap::open(const char *path) { + close(); + +#if defined(_WIN32) + hFile_ = ::CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile_ == INVALID_HANDLE_VALUE) { return false; } + + size_ = ::GetFileSize(hFile_, NULL); + + hMapping_ = ::CreateFileMapping(hFile_, NULL, PAGE_READONLY, 0, 0, NULL); + + if (hMapping_ == NULL) { + close(); + return false; + } + + addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0); +#else + fd_ = ::open(path, O_RDONLY); + if (fd_ == -1) { return false; } + + struct stat sb; + if (fstat(fd_, &sb) == -1) { + close(); + return false; + } + size_ = static_cast(sb.st_size); + + addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0); +#endif + + if (addr_ == nullptr) { + close(); + return false; + } + + return true; +} + +inline bool mmap::is_open() const { return addr_ != nullptr; } + +inline size_t mmap::size() const { return size_; } + +inline const char *mmap::data() const { return (const char *)addr_; } + +inline void mmap::close() { +#if defined(_WIN32) + if (addr_) { + ::UnmapViewOfFile(addr_); + addr_ = nullptr; + } + + if (hMapping_) { + ::CloseHandle(hMapping_); + hMapping_ = NULL; + } + + if (hFile_ != INVALID_HANDLE_VALUE) { + ::CloseHandle(hFile_); + hFile_ = INVALID_HANDLE_VALUE; + } +#else + if (addr_ != nullptr) { + munmap(addr_, size_); + addr_ = nullptr; + } + + if (fd_ != -1) { + ::close(fd_); + fd_ = -1; + } +#endif + size_ = 0; +} inline int close_socket(socket_t sock) { #ifdef _WIN32 return closesocket(sock); @@ -5963,24 +6072,15 @@ inline bool Server::handle_file_request(const Request &req, Response &res, res.set_header(kv.first.c_str(), kv.second); } - auto fs = - std::make_shared(path, std::ios_base::binary); - - fs->seekg(0, std::ios_base::end); - auto size = static_cast(fs->tellg()); - fs->seekg(0); + auto mm = std::make_shared(path.c_str()); + if (!mm->is_open()) { return false; } res.set_content_provider( - size, + mm->size(), detail::find_content_type(path, file_extension_and_mimetype_map_, default_file_mimetype_), - [fs](size_t offset, size_t length, DataSink &sink) -> bool { - std::array buf{}; - length = std::min(length, CPPHTTPLIB_SEND_BUFSIZ); - - fs->seekg(static_cast(offset), std::ios_base::beg); - fs->read(buf.data(), static_cast(length)); - sink.write(buf.data(), length); + [mm](size_t offset, size_t length, DataSink &sink) -> bool { + sink.write(mm->data() + offset, length); return true; });