Use memory mapped file for static file server (#1632)

* Use memory mapped file for static file server

* Fix build error
This commit is contained in:
yhirose 2023-07-31 07:43:50 -04:00 committed by GitHub
parent 6bb580cda8
commit 44c62d838e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

136
httplib.h
View file

@ -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 <csignal>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
@ -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<size_t>(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<std::ifstream>(path, std::ios_base::binary);
fs->seekg(0, std::ios_base::end);
auto size = static_cast<size_t>(fs->tellg());
fs->seekg(0);
auto mm = std::make_shared<detail::mmap>(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<char, CPPHTTPLIB_SEND_BUFSIZ> buf{};
length = std::min(length, CPPHTTPLIB_SEND_BUFSIZ);
fs->seekg(static_cast<std::streamsize>(offset), std::ios_base::beg);
fs->read(buf.data(), static_cast<std::streamsize>(length));
sink.write(buf.data(), length);
[mm](size_t offset, size_t length, DataSink &sink) -> bool {
sink.write(mm->data() + offset, length);
return true;
});