mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-06-12 17:47:14 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe01fa760b | ||
|
|
d61d63dd97 | ||
|
|
3fe13ecc91 | ||
|
|
064cc6810e | ||
|
|
464cc89b77 | ||
|
|
ca5a50d2c9 | ||
|
|
b251668522 | ||
|
|
9ee740fe8f | ||
|
|
851edaf77f | ||
|
|
1a2a6e2b01 | ||
|
|
ac7742bb32 | ||
|
|
82c11168c1 | ||
|
|
7c5197c86c | ||
|
|
8801e51138 |
221
README.md
221
README.md
@@ -17,24 +17,24 @@ Server Example
|
||||
|
||||
int main(void)
|
||||
{
|
||||
using namespace httplib;
|
||||
using namespace httplib;
|
||||
|
||||
Server svr;
|
||||
Server svr;
|
||||
|
||||
svr.Get("/hi", [](const Request& req, Response& res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
svr.Get("/hi", [](const Request& req, Response& res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
|
||||
auto numbers = req.matches[1];
|
||||
res.set_content(numbers, "text/plain");
|
||||
});
|
||||
svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) {
|
||||
auto numbers = req.matches[1];
|
||||
res.set_content(numbers, "text/plain");
|
||||
});
|
||||
|
||||
svr.Get("/stop", [&](const Request& req, Response& res) {
|
||||
svr.stop();
|
||||
});
|
||||
svr.Get("/stop", [&](const Request& req, Response& res) {
|
||||
svr.stop();
|
||||
});
|
||||
|
||||
svr.listen("localhost", 1234);
|
||||
svr.listen("localhost", 1234);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -50,17 +50,24 @@ svr.listen_after_bind();
|
||||
### Static File Server
|
||||
|
||||
```cpp
|
||||
auto ret = svr.set_base_dir("./www"); // This is same as `svr.set_base_dir("./www", "/")`;
|
||||
// Mount / to ./www directory
|
||||
auto ret = svr.set_mount_point("/", "./www");
|
||||
if (!ret) {
|
||||
// The specified base directory doesn't exist...
|
||||
}
|
||||
|
||||
// Mount /public to ./www directory
|
||||
ret = svr.set_base_dir("./www", "/public");
|
||||
ret = svr.set_mount_point("/public", "./www");
|
||||
|
||||
// Mount /public to ./www1 and ./www2 directories
|
||||
ret = svr.set_base_dir("./www1", "/public"); // 1st order to search
|
||||
ret = svr.set_base_dir("./www2", "/public"); // 2nd order to search
|
||||
ret = svr.set_mount_point("/public", "./www1"); // 1st order to search
|
||||
ret = svr.set_mount_point("/public", "./www2"); // 2nd order to search
|
||||
|
||||
// Remove mount /
|
||||
ret = svr.remove_mount_point("/");
|
||||
|
||||
// Remove mount /public
|
||||
ret = svr.remove_mount_point("/public");
|
||||
```
|
||||
|
||||
```cpp
|
||||
@@ -72,39 +79,41 @@ svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");
|
||||
|
||||
The followings are built-in mappings:
|
||||
|
||||
| Extension | MIME Type |
|
||||
| :--------- | :--------------------- |
|
||||
| .txt | text/plain |
|
||||
| .html .htm | text/html |
|
||||
| .css | text/css |
|
||||
| .jpeg .jpg | image/jpg |
|
||||
| .png | image/png |
|
||||
| .gif | image/gif |
|
||||
| .svg | image/svg+xml |
|
||||
| .ico | image/x-icon |
|
||||
| .json | application/json |
|
||||
| .pdf | application/pdf |
|
||||
| .js | application/javascript |
|
||||
| .wasm | application/wasm |
|
||||
| .xml | application/xml |
|
||||
| .xhtml | application/xhtml+xml |
|
||||
| Extension | MIME Type |
|
||||
| :-------- | :--------------------- |
|
||||
| txt | text/plain |
|
||||
| html, htm | text/html |
|
||||
| css | text/css |
|
||||
| jpeg, jpg | image/jpg |
|
||||
| png | image/png |
|
||||
| gif | image/gif |
|
||||
| svg | image/svg+xml |
|
||||
| ico | image/x-icon |
|
||||
| json | application/json |
|
||||
| pdf | application/pdf |
|
||||
| js | application/javascript |
|
||||
| wasm | application/wasm |
|
||||
| xml | application/xml |
|
||||
| xhtml | application/xhtml+xml |
|
||||
|
||||
NOTE: These the static file server methods are not thread safe.
|
||||
|
||||
### Logging
|
||||
|
||||
```cpp
|
||||
svr.set_logger([](const auto& req, const auto& res) {
|
||||
your_logger(req, res);
|
||||
your_logger(req, res);
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handler
|
||||
### Error handler
|
||||
|
||||
```cpp
|
||||
svr.set_error_handler([](const auto& req, auto& res) {
|
||||
auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, res.status);
|
||||
res.set_content(buf, "text/html");
|
||||
auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, res.status);
|
||||
res.set_content(buf, "text/html");
|
||||
});
|
||||
```
|
||||
|
||||
@@ -112,31 +121,12 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
|
||||
```cpp
|
||||
svr.Post("/multipart", [&](const auto& req, auto& res) {
|
||||
auto size = req.files.size();
|
||||
auto ret = req.has_file("name1");
|
||||
const auto& file = req.get_file_value("name1");
|
||||
// file.filename;
|
||||
// file.content_type;
|
||||
// file.content;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Send content with Content provider
|
||||
|
||||
```cpp
|
||||
const uint64_t DATA_CHUNK_SIZE = 4;
|
||||
|
||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
auto data = new std::string("abcdefg");
|
||||
|
||||
res.set_content_provider(
|
||||
data->size(), // Content length
|
||||
[data](uint64_t offset, uint64_t length, DataSink &sink) {
|
||||
const auto &d = *data;
|
||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
},
|
||||
[data] { delete data; });
|
||||
auto size = req.files.size();
|
||||
auto ret = req.has_file("name1");
|
||||
const auto& file = req.get_file_value("name1");
|
||||
// file.filename;
|
||||
// file.content_type;
|
||||
// file.content;
|
||||
});
|
||||
```
|
||||
|
||||
@@ -167,6 +157,24 @@ svr.Post("/content_receiver",
|
||||
});
|
||||
```
|
||||
|
||||
### Send content with Content provider
|
||||
|
||||
```cpp
|
||||
const uint64_t DATA_CHUNK_SIZE = 4;
|
||||
|
||||
svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
auto data = new std::string("abcdefg");
|
||||
|
||||
res.set_content_provider(
|
||||
data->size(), // Content length
|
||||
[data](uint64_t offset, uint64_t length, DataSink &sink) {
|
||||
const auto &d = *data;
|
||||
sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
},
|
||||
[data] { delete data; });
|
||||
});
|
||||
```
|
||||
|
||||
### Chunked transfer encoding
|
||||
|
||||
```cpp
|
||||
@@ -191,11 +199,7 @@ Please check [here](https://github.com/yhirose/cpp-httplib/blob/master/example/s
|
||||
|
||||
`ThreadPool` is used as a default task queue, and the default thread count is set to value from `std::thread::hardware_concurrency()`.
|
||||
|
||||
Set thread count to 8:
|
||||
|
||||
```cpp
|
||||
#define CPPHTTPLIB_THREAD_POOL_COUNT 8
|
||||
```
|
||||
You can change the thread count by setting `CPPHTTPLIB_THREAD_POOL_COUNT`.
|
||||
|
||||
### Override the default thread pool with yours
|
||||
|
||||
@@ -223,6 +227,24 @@ svr.new_task_queue = [] {
|
||||
};
|
||||
```
|
||||
|
||||
### 'Expect: 100-continue' handler
|
||||
|
||||
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
|
||||
|
||||
```cpp
|
||||
// Send a '417 Expectation Failed' response.
|
||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
return 417;
|
||||
});
|
||||
```
|
||||
|
||||
```cpp
|
||||
// Send a final status without reading the message body.
|
||||
svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
return res.status = 401;
|
||||
});
|
||||
```
|
||||
|
||||
Client Example
|
||||
--------------
|
||||
|
||||
@@ -234,36 +256,36 @@ Client Example
|
||||
|
||||
int main(void)
|
||||
{
|
||||
httplib::Client cli("localhost", 1234);
|
||||
httplib::Client cli("localhost", 1234);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
auto res = cli.Get("/hi");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET with HTTP headers
|
||||
|
||||
```c++
|
||||
httplib::Headers headers = {
|
||||
{ "Accept-Encoding", "gzip, deflate" }
|
||||
};
|
||||
auto res = cli.Get("/hi", headers);
|
||||
httplib::Headers headers = {
|
||||
{ "Accept-Encoding", "gzip, deflate" }
|
||||
};
|
||||
auto res = cli.Get("/hi", headers);
|
||||
```
|
||||
|
||||
### GET with Content Receiver
|
||||
|
||||
```c++
|
||||
std::string body;
|
||||
std::string body;
|
||||
|
||||
auto res = cli.Get("/large-data",
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
auto res = cli.Get("/large-data",
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
assert(res->body.empty());
|
||||
assert(res->body.empty());
|
||||
```
|
||||
|
||||
### POST
|
||||
@@ -296,15 +318,15 @@ auto res = cli.Post("/post", params);
|
||||
### POST with Multipart Form Data
|
||||
|
||||
```c++
|
||||
httplib::MultipartFormDataItems items = {
|
||||
{ "text1", "text default", "", "" },
|
||||
{ "text2", "aωb", "", "" },
|
||||
{ "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
|
||||
{ "file2", "{\n \"world\", true\n}\n", "world.json", "application/json" },
|
||||
{ "file3", "", "", "application/octet-stream" },
|
||||
};
|
||||
httplib::MultipartFormDataItems items = {
|
||||
{ "text1", "text default", "", "" },
|
||||
{ "text2", "aωb", "", "" },
|
||||
{ "file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain" },
|
||||
{ "file2", "{\n \"world\", true\n}\n", "world.json", "application/json" },
|
||||
{ "file3", "", "", "application/octet-stream" },
|
||||
};
|
||||
|
||||
auto res = cli.Post("/multipart", items);
|
||||
auto res = cli.Post("/multipart", items);
|
||||
```
|
||||
|
||||
### PUT
|
||||
@@ -338,12 +360,12 @@ httplib::Client client(url, port);
|
||||
|
||||
// prints: 0 / 000 bytes => 50% complete
|
||||
std::shared_ptr<httplib::Response> res =
|
||||
cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||
printf("%lld / %lld bytes => %d%% complete\n",
|
||||
len, total,
|
||||
(int)((len/total)*100));
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
}
|
||||
cli.Get("/", [](uint64_t len, uint64_t total) {
|
||||
printf("%lld / %lld bytes => %d%% complete\n",
|
||||
len, total,
|
||||
(int)((len/total)*100));
|
||||
return true; // return 'false' if you want to cancel the request.
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
@@ -532,3 +554,4 @@ The following folks made great contributions to polish this library to totally a
|
||||
* [DraTeots](https://github.com/DraTeots)
|
||||
* [BastienDurel](https://github.com/BastienDurel)
|
||||
* [vitalyster](https://github.com/vitalyster)
|
||||
* [trollixx](https://github.com/trollixx)
|
||||
|
||||
@@ -122,7 +122,7 @@ int main(int argc, const char **argv) {
|
||||
auto base_dir = "./";
|
||||
if (argc > 2) { base_dir = argv[2]; }
|
||||
|
||||
if (!svr.set_base_dir(base_dir)) {
|
||||
if (!svr.set_mount_point("/", base_dir)) {
|
||||
cout << "The specified base directory doesn't exist...";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
83
httplib.h
83
httplib.h
@@ -41,7 +41,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
|
||||
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max)()
|
||||
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max())
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_RECV_BUFSIZ
|
||||
@@ -49,12 +49,8 @@
|
||||
#endif
|
||||
|
||||
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
|
||||
// if hardware_concurrency() outputs 0 we still wants to use threads for this.
|
||||
// -1 because we have one thread already in the main function.
|
||||
#define CPPHTTPLIB_THREAD_POOL_COUNT \
|
||||
(std::thread::hardware_concurrency() \
|
||||
? std::thread::hardware_concurrency() - 1 \
|
||||
: 2)
|
||||
(std::max(1u, std::thread::hardware_concurrency() - 1))
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -448,6 +444,8 @@ public:
|
||||
using Handler = std::function<void(const Request &, Response &)>;
|
||||
using HandlerWithContentReader = std::function<void(
|
||||
const Request &, Response &, const ContentReader &content_reader)>;
|
||||
using Expect100ContinueHandler =
|
||||
std::function<int(const Request &, Response &)>;
|
||||
|
||||
Server();
|
||||
|
||||
@@ -465,7 +463,10 @@ public:
|
||||
Server &Delete(const char *pattern, Handler handler);
|
||||
Server &Options(const char *pattern, Handler handler);
|
||||
|
||||
bool set_base_dir(const char *dir, const char *mount_point = nullptr);
|
||||
[[deprecated]] bool set_base_dir(const char *dir,
|
||||
const char *mount_point = nullptr);
|
||||
bool set_mount_point(const char *mount_point, const char *dir);
|
||||
bool remove_mount_point(const char *mount_point);
|
||||
void set_file_extension_and_mimetype_mapping(const char *ext,
|
||||
const char *mime);
|
||||
void set_file_request_handler(Handler handler);
|
||||
@@ -473,6 +474,8 @@ public:
|
||||
void set_error_handler(Handler handler);
|
||||
void set_logger(Logger logger);
|
||||
|
||||
void set_expect_100_continue_handler(Expect100ContinueHandler handler);
|
||||
|
||||
void set_keep_alive_max_count(size_t count);
|
||||
void set_read_timeout(time_t sec, time_t usec);
|
||||
void set_payload_max_length(size_t length);
|
||||
@@ -550,6 +553,7 @@ private:
|
||||
Handlers options_handlers_;
|
||||
Handler error_handler_;
|
||||
Logger logger_;
|
||||
Expect100ContinueHandler expect_100_continue_handler_;
|
||||
};
|
||||
|
||||
class Client {
|
||||
@@ -1591,6 +1595,7 @@ find_content_type(const std::string &path,
|
||||
|
||||
inline const char *status_message(int status) {
|
||||
switch (status) {
|
||||
case 100: return "Continue";
|
||||
case 200: return "OK";
|
||||
case 202: return "Accepted";
|
||||
case 204: return "No Content";
|
||||
@@ -1607,6 +1612,7 @@ inline const char *status_message(int status) {
|
||||
case 414: return "Request-URI Too Long";
|
||||
case 415: return "Unsupported Media Type";
|
||||
case 416: return "Range Not Satisfiable";
|
||||
case 417: return "Expectation Failed";
|
||||
case 503: return "Service Unavailable";
|
||||
|
||||
default:
|
||||
@@ -2889,6 +2895,10 @@ inline Server &Server::Options(const char *pattern, Handler handler) {
|
||||
}
|
||||
|
||||
inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
|
||||
return set_mount_point(mount_point, dir);
|
||||
}
|
||||
|
||||
inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
|
||||
if (detail::is_dir(dir)) {
|
||||
std::string mnt = mount_point ? mount_point : "/";
|
||||
if (!mnt.empty() && mnt[0] == '/') {
|
||||
@@ -2899,6 +2909,16 @@ inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Server::remove_mount_point(const char *mount_point) {
|
||||
for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
|
||||
if (it->first == mount_point) {
|
||||
base_dirs_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void Server::set_file_extension_and_mimetype_mapping(const char *ext,
|
||||
const char *mime) {
|
||||
file_extension_and_mimetype_map_[ext] = mime;
|
||||
@@ -2914,6 +2934,11 @@ inline void Server::set_error_handler(Handler handler) {
|
||||
|
||||
inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
|
||||
|
||||
inline void
|
||||
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
|
||||
expect_100_continue_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
inline void Server::set_keep_alive_max_count(size_t count) {
|
||||
keep_alive_max_count_ = count;
|
||||
}
|
||||
@@ -2980,9 +3005,11 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
|
||||
|
||||
if (400 <= res.status && error_handler_) { error_handler_(req, res); }
|
||||
|
||||
detail::BufferStream bstrm;
|
||||
|
||||
// Response line
|
||||
if (!strm.write_format("HTTP/1.1 %d %s\r\n", res.status,
|
||||
detail::status_message(res.status))) {
|
||||
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
|
||||
detail::status_message(res.status))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2995,11 +3022,12 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
|
||||
res.set_header("Connection", "Keep-Alive");
|
||||
}
|
||||
|
||||
if (!res.has_header("Content-Type")) {
|
||||
if (!res.has_header("Content-Type") &&
|
||||
(!res.body.empty() || res.content_length > 0)) {
|
||||
res.set_header("Content-Type", "text/plain");
|
||||
}
|
||||
|
||||
if (!res.has_header("Accept-Ranges")) {
|
||||
if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
|
||||
res.set_header("Accept-Ranges", "bytes");
|
||||
}
|
||||
|
||||
@@ -3076,7 +3104,11 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
|
||||
res.set_header("Content-Length", length);
|
||||
}
|
||||
|
||||
if (!detail::write_headers(strm, res, Headers())) { return false; }
|
||||
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
strm.write(data.data(), data.size());
|
||||
|
||||
// Body
|
||||
if (req.method != "HEAD") {
|
||||
@@ -3212,7 +3244,8 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Server::handle_file_request(Request &req, Response &res, bool head) {
|
||||
inline bool Server::handle_file_request(Request &req, Response &res,
|
||||
bool head) {
|
||||
for (const auto &kv : base_dirs_) {
|
||||
const auto &mount_point = kv.first;
|
||||
const auto &base_dir = kv.second;
|
||||
@@ -3230,7 +3263,9 @@ inline bool Server::handle_file_request(Request &req, Response &res, bool head)
|
||||
detail::find_content_type(path, file_extension_and_mimetype_map_);
|
||||
if (type) { res.set_header("Content-Type", type); }
|
||||
res.status = 200;
|
||||
if (!head && file_request_handler_) { file_request_handler_(req, res); }
|
||||
if (!head && file_request_handler_) {
|
||||
file_request_handler_(req, res);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3332,7 +3367,10 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
|
||||
bool last_connection) {
|
||||
// File handler
|
||||
bool is_head_request = req.method == "HEAD";
|
||||
if ((req.method == "GET" || is_head_request) && handle_file_request(req, res, is_head_request)) { return true; }
|
||||
if ((req.method == "GET" || is_head_request) &&
|
||||
handle_file_request(req, res, is_head_request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (detail::expect_content(req)) {
|
||||
// Content reader handler
|
||||
@@ -3468,6 +3506,21 @@ Server::process_request(Stream &strm, bool last_connection,
|
||||
|
||||
if (setup_request) { setup_request(req); }
|
||||
|
||||
if (req.get_header_value("Expect") == "100-continue") {
|
||||
auto status = 100;
|
||||
if (expect_100_continue_handler_) {
|
||||
status = expect_100_continue_handler_(req, res);
|
||||
}
|
||||
switch (status) {
|
||||
case 100:
|
||||
case 417:
|
||||
strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
|
||||
detail::status_message(status));
|
||||
break;
|
||||
default: return write_response(strm, last_connection, req, res);
|
||||
}
|
||||
}
|
||||
|
||||
// Rounting
|
||||
if (routing(req, res, strm, last_connection)) {
|
||||
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
|
||||
|
||||
65
test/test.cc
65
test/test.cc
@@ -201,7 +201,7 @@ TEST(ParseHeaderValueTest, Range) {
|
||||
|
||||
TEST(BufferStreamTest, read) {
|
||||
detail::BufferStream strm1;
|
||||
Stream& strm = strm1;
|
||||
Stream &strm = strm1;
|
||||
|
||||
EXPECT_EQ(5, strm.write("hello"));
|
||||
|
||||
@@ -645,11 +645,22 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
|
||||
TEST(Server, BindAndListenSeparately) {
|
||||
Server svr;
|
||||
int port = svr.bind_to_any_port("localhost");
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
ASSERT_TRUE(port > 0);
|
||||
svr.stop();
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(SSLServer, BindAndListenSeparately) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, CLIENT_CA_CERT_DIR);
|
||||
int port = svr.bind_to_any_port("0.0.0.0");
|
||||
ASSERT_TRUE(svr.is_valid());
|
||||
ASSERT_TRUE(port > 0);
|
||||
svr.stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
class ServerTest : public ::testing::Test {
|
||||
protected:
|
||||
ServerTest()
|
||||
@@ -662,8 +673,8 @@ protected:
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
svr_.set_base_dir("./www");
|
||||
svr_.set_base_dir("./www2", "/mount");
|
||||
svr_.set_mount_point("/", "./www");
|
||||
svr_.set_mount_point("/mount", "./www2");
|
||||
svr_.set_file_extension_and_mimetype_mapping("abcde", "text/abcde");
|
||||
|
||||
svr_.Get("/hi",
|
||||
@@ -1245,7 +1256,7 @@ TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, InvalidBaseDirMount) {
|
||||
EXPECT_EQ(false, svr_.set_base_dir("./www3", "invalid_mount_point"));
|
||||
EXPECT_EQ(false, svr_.set_mount_point("invalid_mount_point", "./www3"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, EmptyRequest) {
|
||||
@@ -2069,6 +2080,50 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(MountTest, Unmount) {
|
||||
Server svr;
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", PORT);
|
||||
|
||||
svr.set_mount_point("/mount2", "./www2");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
|
||||
res = cli.Get("/mount2/dir/test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
|
||||
svr.set_mount_point("/", "./www");
|
||||
|
||||
res = cli.Get("/dir/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
|
||||
svr.remove_mount_point("/");
|
||||
res = cli.Get("/dir/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
|
||||
svr.remove_mount_point("/mount2");
|
||||
res = cli.Get("/mount2/dir/test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
class ServerTestWithAI_PASSIVE : public ::testing::Test {
|
||||
protected:
|
||||
ServerTestWithAI_PASSIVE()
|
||||
|
||||
Reference in New Issue
Block a user