mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 85b4abbf16 | |||
| e42a358da8 | |||
| f008fe4539 | |||
| ddf41d29ef | |||
| 3f88a46c4a | |||
| 242706ea34 | |||
| a9f5f8683f | |||
| 60c2213893 | |||
| eb2d28bca2 | |||
| 86f637a246 | |||
| 2c07ec4600 | |||
| 871d8d67b0 | |||
| 7299713195 | |||
| 96afa7e108 | |||
| 55f57af0b9 | |||
| 6b35cd0116 | |||
| 99f2229e48 | |||
| b9641048fc | |||
| e9c6c6e609 |
@@ -15,8 +15,15 @@ Simple examples
|
||||
#### Server
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// HTTP
|
||||
httplib::Server svr;
|
||||
|
||||
// HTTPS
|
||||
httplib::SSLServer svr;
|
||||
|
||||
svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
@@ -27,12 +34,18 @@ svr.listen("0.0.0.0", 8080);
|
||||
#### Client
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// HTTP
|
||||
httplib::Client cli("http://cpp-httplib-server.yhirose.repl.co");
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
// HTTPS
|
||||
httplib::Client cli("https://cpp-httplib-server.yhirose.repl.co");
|
||||
|
||||
res->status; // 200
|
||||
res->body; // "Hello World!"
|
||||
auto res = cli.Get("/hi");
|
||||
res->status;
|
||||
res->body;
|
||||
```
|
||||
|
||||
### Try out the examples on Repl.it!
|
||||
@@ -120,48 +133,28 @@ svr.set_file_extension_and_mimetype_mapping("hh", "text/x-h");
|
||||
|
||||
The followings are built-in mappings:
|
||||
|
||||
| Extension | MIME Type |
|
||||
| :--------- | :-------------------------- |
|
||||
| css | text/css |
|
||||
| csv | text/csv |
|
||||
| txt | text/plain |
|
||||
| vtt | text/vtt |
|
||||
| html, htm | text/html |
|
||||
| apng | image/apng |
|
||||
| avif | image/avif |
|
||||
| bmp | image/bmp |
|
||||
| gif | image/gif |
|
||||
| png | image/png |
|
||||
| svg | image/svg+xml |
|
||||
| webp | image/webp |
|
||||
| ico | image/x-icon |
|
||||
| tif | image/tiff |
|
||||
| tiff | image/tiff |
|
||||
| jpeg, jpg | image/jpeg |
|
||||
| mp4 | video/mp4 |
|
||||
| mpeg | video/mpeg |
|
||||
| webm | video/webm |
|
||||
| mp3 | audio/mp3 |
|
||||
| mpga | audio/mpeg |
|
||||
| weba | audio/webm |
|
||||
| wav | audio/wave |
|
||||
| otf | font/otf |
|
||||
| ttf | font/ttf |
|
||||
| woff | font/woff |
|
||||
| woff2 | font/woff2 |
|
||||
| 7z | application/x-7z-compressed |
|
||||
| atom | application/atom+xml |
|
||||
| pdf | application/pdf |
|
||||
| mjs, js | application/javascript |
|
||||
| json | application/json |
|
||||
| rss | application/rss+xml |
|
||||
| tar | application/x-tar |
|
||||
| xhtml, xht | application/xhtml+xml |
|
||||
| xslt | application/xslt+xml |
|
||||
| xml | application/xml |
|
||||
| gz | application/gzip |
|
||||
| zip | application/zip |
|
||||
| wasm | application/wasm |
|
||||
| Extension | MIME Type | Extension | MIME Type |
|
||||
| :--------- | :-------------------------- | :--------- | :-------------------------- |
|
||||
| css | text/css | mpga | audio/mpeg |
|
||||
| csv | text/csv | weba | audio/webm |
|
||||
| txt | text/plain | wav | audio/wave |
|
||||
| vtt | text/vtt | otf | font/otf |
|
||||
| html, htm | text/html | ttf | font/ttf |
|
||||
| apng | image/apng | woff | font/woff |
|
||||
| avif | image/avif | woff2 | font/woff2 |
|
||||
| bmp | image/bmp | 7z | application/x-7z-compressed |
|
||||
| gif | image/gif | atom | application/atom+xml |
|
||||
| png | image/png | pdf | application/pdf |
|
||||
| svg | image/svg+xml | mjs, js | application/javascript |
|
||||
| webp | image/webp | json | application/json |
|
||||
| ico | image/x-icon | rss | application/rss+xml |
|
||||
| tif | image/tiff | tar | application/x-tar |
|
||||
| tiff | image/tiff | xhtml, xht | application/xhtml+xml |
|
||||
| jpeg, jpg | image/jpeg | xslt | application/xslt+xml |
|
||||
| mp4 | video/mp4 | xml | application/xml |
|
||||
| mpeg | video/mpeg | gz | application/gzip |
|
||||
| webm | video/webm | zip | application/zip |
|
||||
| mp3 | audio/mp3 | wasm | application/wasm |
|
||||
|
||||
NOTE: These static file server methods are not thread-safe.
|
||||
|
||||
@@ -184,6 +177,26 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
});
|
||||
```
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
|
||||
if (req.path == "/hello") {
|
||||
res.set_content("world", "text/html");
|
||||
return true; // This request is handled
|
||||
}
|
||||
return false; // Let the router handle this request
|
||||
});
|
||||
```
|
||||
|
||||
### Post routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_post_routing_handler([](const auto& req, auto& res) {
|
||||
res.set_header("ADDITIONAL_HEADER", "value");
|
||||
});
|
||||
```
|
||||
|
||||
### 'multipart/form-data' POST data
|
||||
|
||||
```cpp
|
||||
@@ -268,6 +281,7 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_chunked_content_provider(
|
||||
"text/plain",
|
||||
[](size_t offset, DataSink &sink) {
|
||||
sink.write("123", 3);
|
||||
sink.write("345", 3);
|
||||
@@ -390,6 +404,7 @@ httplib::Client cli("localhost:8080");
|
||||
httplib::Client cli("http://localhost");
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
httplib::Client cli("https://localhost");
|
||||
httplib::SSLClient cli("localhost");
|
||||
```
|
||||
|
||||
### Error code
|
||||
@@ -655,29 +670,6 @@ NOTE: This feature is not available on Windows, yet.
|
||||
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||
```
|
||||
|
||||
OpenSSL Support
|
||||
---------------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
httplib::SSLClient cli("localhost", 1234); // or `httplib::Client cli("https://localhost:1234");`
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
cli.enable_server_certificate_verification(true);
|
||||
```
|
||||
|
||||
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE
|
||||
can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its
|
||||
internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might
|
||||
be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
|
||||
|
||||
Compression
|
||||
-----------
|
||||
|
||||
@@ -714,6 +706,32 @@ res = cli.Get("/resource/foo", {{"Accept-Encoding", "gzip, deflate, br"}});
|
||||
res->body; // Compressed data
|
||||
```
|
||||
|
||||
SSL Support
|
||||
-----------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib currently supports only version 1.1.1.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "path/to/httplib.h"
|
||||
|
||||
// Server
|
||||
httplib::SSLServer svr("./cert.pem", "./key.pem");
|
||||
|
||||
// Client
|
||||
httplib::Client cli("https://localhost:1234");
|
||||
|
||||
// Use your CA bundle
|
||||
cli.set_ca_cert_path("./ca-bundle.crt");
|
||||
|
||||
// Disable cert verification
|
||||
cli.enable_server_certificate_verification(false);
|
||||
```
|
||||
|
||||
Note: When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
|
||||
@@ -192,6 +192,7 @@ using socket_t = int;
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
@@ -203,7 +204,6 @@ using socket_t = int;
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
#include <iomanip>
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include <openssl/err.h>
|
||||
@@ -597,6 +597,7 @@ inline void default_socket_options(socket_t sock) {
|
||||
class Server {
|
||||
public:
|
||||
using Handler = std::function<void(const Request &, Response &)>;
|
||||
using HandlerWithReturn = std::function<bool(const Request &, Response &)>;
|
||||
using HandlerWithContentReader = std::function<void(
|
||||
const Request &, Response &, const ContentReader &content_reader)>;
|
||||
using Expect100ContinueHandler =
|
||||
@@ -627,7 +628,11 @@ public:
|
||||
const char *mime);
|
||||
void set_file_request_handler(Handler handler);
|
||||
|
||||
void set_error_handler(HandlerWithReturn handler);
|
||||
void set_error_handler(Handler handler);
|
||||
void set_pre_routing_handler(HandlerWithReturn handler);
|
||||
void set_post_routing_handler(Handler handler);
|
||||
|
||||
void set_expect_100_continue_handler(Expect100ContinueHandler handler);
|
||||
void set_logger(Logger logger);
|
||||
|
||||
@@ -680,7 +685,8 @@ private:
|
||||
bool listen_internal();
|
||||
|
||||
bool routing(Request &req, Response &res, Stream &strm);
|
||||
bool handle_file_request(Request &req, Response &res, bool head = false);
|
||||
bool handle_file_request(const Request &req, Response &res,
|
||||
bool head = false);
|
||||
bool dispatch_request(Request &req, Response &res, const Handlers &handlers);
|
||||
bool
|
||||
dispatch_request_for_content_reader(Request &req, Response &res,
|
||||
@@ -733,7 +739,9 @@ private:
|
||||
Handlers delete_handlers_;
|
||||
HandlersForContentReader delete_handlers_for_content_reader_;
|
||||
Handlers options_handlers_;
|
||||
Handler error_handler_;
|
||||
HandlerWithReturn error_handler_;
|
||||
HandlerWithReturn pre_routing_handler_;
|
||||
Handler post_routing_handler_;
|
||||
Logger logger_;
|
||||
Expect100ContinueHandler expect_100_continue_handler_;
|
||||
|
||||
@@ -817,6 +825,10 @@ public:
|
||||
Result Head(const char *path, const Headers &headers);
|
||||
|
||||
Result Post(const char *path);
|
||||
Result Post(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Post(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Post(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Post(const char *path, const Headers &headers, const std::string &body,
|
||||
@@ -839,6 +851,10 @@ public:
|
||||
const MultipartFormDataItems &items, const std::string &boundary);
|
||||
|
||||
Result Put(const char *path);
|
||||
Result Put(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Put(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Put(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Put(const char *path, const Headers &headers, const std::string &body,
|
||||
@@ -855,6 +871,11 @@ public:
|
||||
Result Put(const char *path, const Params ¶ms);
|
||||
Result Put(const char *path, const Headers &headers, const Params ¶ms);
|
||||
|
||||
Result Patch(const char *path);
|
||||
Result Patch(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Patch(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Patch(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Patch(const char *path, const Headers &headers,
|
||||
@@ -870,9 +891,13 @@ public:
|
||||
const char *content_type);
|
||||
|
||||
Result Delete(const char *path);
|
||||
Result Delete(const char *path, const Headers &headers);
|
||||
Result Delete(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Delete(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Delete(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Delete(const char *path, const Headers &headers);
|
||||
Result Delete(const char *path, const Headers &headers,
|
||||
const std::string &body, const char *content_type);
|
||||
|
||||
@@ -1033,14 +1058,12 @@ private:
|
||||
bool close_connection, Error &error);
|
||||
std::unique_ptr<Response> send_with_content_provider(
|
||||
const char *method, const char *path, const Headers &headers,
|
||||
const std::string &body, size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *body, size_t content_length, ContentProvider content_provider,
|
||||
ContentProviderWithoutLength content_provider_without_length,
|
||||
const char *content_type, Error &error);
|
||||
Result send_with_content_provider(
|
||||
const char *method, const char *path, const Headers &headers,
|
||||
const std::string &body, size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *body, size_t content_length, ContentProvider content_provider,
|
||||
ContentProviderWithoutLength content_provider_without_length,
|
||||
const char *content_type);
|
||||
|
||||
@@ -1095,6 +1118,10 @@ public:
|
||||
Result Head(const char *path, const Headers &headers);
|
||||
|
||||
Result Post(const char *path);
|
||||
Result Post(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Post(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Post(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Post(const char *path, const Headers &headers, const std::string &body,
|
||||
@@ -1116,6 +1143,10 @@ public:
|
||||
Result Post(const char *path, const Headers &headers,
|
||||
const MultipartFormDataItems &items, const std::string &boundary);
|
||||
Result Put(const char *path);
|
||||
Result Put(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Put(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Put(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Put(const char *path, const Headers &headers, const std::string &body,
|
||||
@@ -1131,6 +1162,11 @@ public:
|
||||
const char *content_type);
|
||||
Result Put(const char *path, const Params ¶ms);
|
||||
Result Put(const char *path, const Headers &headers, const Params ¶ms);
|
||||
Result Patch(const char *path);
|
||||
Result Patch(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Patch(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Patch(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Patch(const char *path, const Headers &headers,
|
||||
@@ -1146,9 +1182,13 @@ public:
|
||||
const char *content_type);
|
||||
|
||||
Result Delete(const char *path);
|
||||
Result Delete(const char *path, const Headers &headers);
|
||||
Result Delete(const char *path, const char *body, size_t content_length,
|
||||
const char *content_type);
|
||||
Result Delete(const char *path, const Headers &headers, const char *body,
|
||||
size_t content_length, const char *content_type);
|
||||
Result Delete(const char *path, const std::string &body,
|
||||
const char *content_type);
|
||||
Result Delete(const char *path, const Headers &headers);
|
||||
Result Delete(const char *path, const Headers &headers,
|
||||
const std::string &body, const char *content_type);
|
||||
|
||||
@@ -1457,26 +1497,20 @@ inline bool is_valid_path(const std::string &path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::string encode_query_param(const std::string &value){
|
||||
inline std::string encode_query_param(const std::string &value) {
|
||||
std::ostringstream escaped;
|
||||
escaped.fill('0');
|
||||
escaped << std::hex;
|
||||
|
||||
for (char const &c: value) {
|
||||
if (std::isalnum(c) ||
|
||||
c == '-' ||
|
||||
c == '_' ||
|
||||
c == '.' ||
|
||||
c == '!' ||
|
||||
c == '~' ||
|
||||
c == '*' ||
|
||||
c == '\'' ||
|
||||
c == '(' ||
|
||||
for (auto c : value) {
|
||||
if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||
|
||||
c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' ||
|
||||
c == ')') {
|
||||
escaped << c;
|
||||
} else {
|
||||
escaped << std::uppercase;
|
||||
escaped << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
|
||||
escaped << '%' << std::setw(2)
|
||||
<< static_cast<int>(static_cast<unsigned char>(c));
|
||||
escaped << std::nouppercase;
|
||||
}
|
||||
}
|
||||
@@ -2175,9 +2209,9 @@ inline unsigned int str2tag(const std::string &s) {
|
||||
|
||||
namespace udl {
|
||||
|
||||
inline constexpr unsigned int operator"" _(const char *s, size_t l) {
|
||||
return str2tag_core(s, l, 0);
|
||||
}
|
||||
inline constexpr unsigned int operator"" _(const char *s, size_t l) {
|
||||
return str2tag_core(s, l, 0);
|
||||
}
|
||||
|
||||
} // namespace udl
|
||||
|
||||
@@ -2192,56 +2226,56 @@ find_content_type(const std::string &path,
|
||||
using udl::operator""_;
|
||||
|
||||
switch (str2tag(ext)) {
|
||||
default: return nullptr;
|
||||
case "css"_: return "text/css";
|
||||
case "csv"_: return "text/csv";
|
||||
case "txt"_: return "text/plain";
|
||||
case "vtt"_: return "text/vtt";
|
||||
case "htm"_:
|
||||
case "html"_: return "text/html";
|
||||
default: return nullptr;
|
||||
case "css"_: return "text/css";
|
||||
case "csv"_: return "text/csv";
|
||||
case "txt"_: return "text/plain";
|
||||
case "vtt"_: return "text/vtt";
|
||||
case "htm"_:
|
||||
case "html"_: return "text/html";
|
||||
|
||||
case "apng"_: return "image/apng";
|
||||
case "avif"_: return "image/avif";
|
||||
case "bmp"_: return "image/bmp";
|
||||
case "gif"_: return "image/gif";
|
||||
case "png"_: return "image/png";
|
||||
case "svg"_: return "image/svg+xml";
|
||||
case "webp"_: return "image/webp";
|
||||
case "ico"_: return "image/x-icon";
|
||||
case "tif"_: return "image/tiff";
|
||||
case "tiff"_: return "image/tiff";
|
||||
case "jpg"_:
|
||||
case "jpeg"_: return "image/jpeg";
|
||||
case "apng"_: return "image/apng";
|
||||
case "avif"_: return "image/avif";
|
||||
case "bmp"_: return "image/bmp";
|
||||
case "gif"_: return "image/gif";
|
||||
case "png"_: return "image/png";
|
||||
case "svg"_: return "image/svg+xml";
|
||||
case "webp"_: return "image/webp";
|
||||
case "ico"_: return "image/x-icon";
|
||||
case "tif"_: return "image/tiff";
|
||||
case "tiff"_: return "image/tiff";
|
||||
case "jpg"_:
|
||||
case "jpeg"_: return "image/jpeg";
|
||||
|
||||
case "mp4"_: return "video/mp4";
|
||||
case "mpeg"_: return "video/mpeg";
|
||||
case "webm"_: return "video/webm";
|
||||
case "mp4"_: return "video/mp4";
|
||||
case "mpeg"_: return "video/mpeg";
|
||||
case "webm"_: return "video/webm";
|
||||
|
||||
case "mp3"_: return "audio/mp3";
|
||||
case "mpga"_: return "audio/mpeg";
|
||||
case "weba"_: return "audio/webm";
|
||||
case "wav"_: return "audio/wave";
|
||||
case "mp3"_: return "audio/mp3";
|
||||
case "mpga"_: return "audio/mpeg";
|
||||
case "weba"_: return "audio/webm";
|
||||
case "wav"_: return "audio/wave";
|
||||
|
||||
case "otf"_: return "font/otf";
|
||||
case "ttf"_: return "font/ttf";
|
||||
case "woff"_: return "font/woff";
|
||||
case "woff2"_: return "font/woff2";
|
||||
case "otf"_: return "font/otf";
|
||||
case "ttf"_: return "font/ttf";
|
||||
case "woff"_: return "font/woff";
|
||||
case "woff2"_: return "font/woff2";
|
||||
|
||||
case "7z"_: return "application/x-7z-compressed";
|
||||
case "atom"_: return "application/atom+xml";
|
||||
case "pdf"_: return "application/pdf";
|
||||
case "js"_:
|
||||
case "mjs"_: return "application/javascript";
|
||||
case "json"_: return "application/json";
|
||||
case "rss"_: return "application/rss+xml";
|
||||
case "tar"_: return "application/x-tar";
|
||||
case "xht"_:
|
||||
case "xhtml"_: return "application/xhtml+xml";
|
||||
case "xslt"_: return "application/xslt+xml";
|
||||
case "xml"_: return "application/xml";
|
||||
case "gz"_: return "application/gzip";
|
||||
case "zip"_: return "application/zip";
|
||||
case "wasm"_: return "application/wasm";
|
||||
case "7z"_: return "application/x-7z-compressed";
|
||||
case "atom"_: return "application/atom+xml";
|
||||
case "pdf"_: return "application/pdf";
|
||||
case "js"_:
|
||||
case "mjs"_: return "application/javascript";
|
||||
case "json"_: return "application/json";
|
||||
case "rss"_: return "application/rss+xml";
|
||||
case "tar"_: return "application/x-tar";
|
||||
case "xht"_:
|
||||
case "xhtml"_: return "application/xhtml+xml";
|
||||
case "xslt"_: return "application/xslt+xml";
|
||||
case "xml"_: return "application/xml";
|
||||
case "gz"_: return "application/gzip";
|
||||
case "zip"_: return "application/zip";
|
||||
case "wasm"_: return "application/wasm";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3560,7 +3594,8 @@ inline bool load_system_certs_on_windows(X509_STORE *store) {
|
||||
if (!hStore) { return false; }
|
||||
|
||||
PCCERT_CONTEXT pContext = NULL;
|
||||
while (pContext = CertEnumCertificatesInStore(hStore, pContext)) {
|
||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
|
||||
nullptr) {
|
||||
auto encoded_cert =
|
||||
static_cast<const unsigned char *>(pContext->pbCertEncoded);
|
||||
|
||||
@@ -4132,14 +4167,23 @@ inline void Server::set_file_request_handler(Handler handler) {
|
||||
file_request_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
inline void Server::set_error_handler(Handler handler) {
|
||||
inline void Server::set_error_handler(HandlerWithReturn handler) {
|
||||
error_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
|
||||
inline void Server::set_error_handler(Handler handler) {
|
||||
error_handler_ = [handler](const Request &req, Response &res) {
|
||||
handler(req, res);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
inline void Server::set_socket_options(SocketOptions socket_options) {
|
||||
socket_options_ = std::move(socket_options);
|
||||
inline void Server::set_pre_routing_handler(HandlerWithReturn handler) {
|
||||
pre_routing_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
inline void Server::set_post_routing_handler(Handler handler) {
|
||||
post_routing_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
|
||||
@@ -4149,6 +4193,12 @@ Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
|
||||
expect_100_continue_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
|
||||
|
||||
inline void Server::set_socket_options(SocketOptions socket_options) {
|
||||
socket_options_ = std::move(socket_options);
|
||||
}
|
||||
|
||||
inline void Server::set_keep_alive_max_count(size_t count) {
|
||||
keep_alive_max_count_ = count;
|
||||
}
|
||||
@@ -4240,13 +4290,15 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
||||
bool need_apply_ranges) {
|
||||
assert(res.status != -1);
|
||||
|
||||
if (400 <= res.status && error_handler_) { error_handler_(req, res); }
|
||||
if (400 <= res.status && error_handler_ && error_handler_(req, res)) {
|
||||
need_apply_ranges = true;
|
||||
}
|
||||
|
||||
std::string content_type;
|
||||
std::string boundary;
|
||||
if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
|
||||
|
||||
// Headers
|
||||
// Prepare additional headers
|
||||
if (close_connection || req.get_header_value("Connection") == "close") {
|
||||
res.set_header("Connection", "close");
|
||||
} else {
|
||||
@@ -4270,20 +4322,24 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
||||
res.set_header("Accept-Ranges", "bytes");
|
||||
}
|
||||
|
||||
detail::BufferStream bstrm;
|
||||
if (post_routing_handler_) { post_routing_handler_(req, res); }
|
||||
|
||||
// Response line
|
||||
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
|
||||
detail::status_message(res.status))) {
|
||||
return false;
|
||||
// Response line and headers
|
||||
{
|
||||
detail::BufferStream bstrm;
|
||||
|
||||
if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
|
||||
detail::status_message(res.status))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
strm.write(data.data(), data.size());
|
||||
}
|
||||
|
||||
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
strm.write(data.data(), data.size());
|
||||
|
||||
// Body
|
||||
auto ret = true;
|
||||
if (req.method != "HEAD") {
|
||||
@@ -4351,7 +4407,6 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
|
||||
is_shutting_down);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
|
||||
@@ -4448,7 +4503,7 @@ inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Server::handle_file_request(Request &req, Response &res,
|
||||
inline bool Server::handle_file_request(const Request &req, Response &res,
|
||||
bool head) {
|
||||
for (const auto &entry : base_dirs_) {
|
||||
// Prefix match
|
||||
@@ -4572,6 +4627,8 @@ inline bool Server::listen_internal() {
|
||||
}
|
||||
|
||||
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
if (pre_routing_handler_ && pre_routing_handler_(req, res)) { return true; }
|
||||
|
||||
// File handler
|
||||
bool is_head_request = req.method == "HEAD";
|
||||
if ((req.method == "GET" || is_head_request) &&
|
||||
@@ -5270,14 +5327,7 @@ inline bool ClientImpl::write_content_with_provider(Stream &strm,
|
||||
|
||||
inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
||||
bool close_connection, Error &error) {
|
||||
detail::BufferStream bstrm;
|
||||
|
||||
// Request line
|
||||
const auto &path = detail::encode_url(req.path);
|
||||
|
||||
bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
|
||||
|
||||
// Additonal headers
|
||||
// Prepare additional headers
|
||||
Headers headers;
|
||||
if (close_connection) { headers.emplace("Connection", "close"); }
|
||||
|
||||
@@ -5347,13 +5397,21 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
||||
proxy_bearer_token_auth_token_, true));
|
||||
}
|
||||
|
||||
detail::write_headers(bstrm, req, headers);
|
||||
// Request line and headers
|
||||
{
|
||||
detail::BufferStream bstrm;
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
if (!detail::write_data(strm, data.data(), data.size())) {
|
||||
error = Error::Write;
|
||||
return false;
|
||||
const auto &path = detail::encode_url(req.path);
|
||||
bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
|
||||
|
||||
detail::write_headers(bstrm, req, headers);
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
if (!detail::write_data(strm, data.data(), data.size())) {
|
||||
error = Error::Write;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Body
|
||||
@@ -5368,8 +5426,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
||||
|
||||
inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
||||
const char *method, const char *path, const Headers &headers,
|
||||
const std::string &body, size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *body, size_t content_length, ContentProvider content_provider,
|
||||
ContentProviderWithoutLength content_provider_without_length,
|
||||
const char *content_type, Error &error) {
|
||||
|
||||
@@ -5422,7 +5479,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!compressor.compress(body.data(), body.size(), true,
|
||||
if (!compressor.compress(body, content_length, true,
|
||||
[&](const char *data, size_t data_len) {
|
||||
req.body.append(data, data_len);
|
||||
return true;
|
||||
@@ -5445,7 +5502,8 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
||||
req.is_chunked_content_provider_ = true;
|
||||
req.headers.emplace("Transfer-Encoding", "chunked");
|
||||
} else {
|
||||
req.body = body;
|
||||
req.body.assign(body, content_length);
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5455,8 +5513,7 @@ inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
||||
|
||||
inline Result ClientImpl::send_with_content_provider(
|
||||
const char *method, const char *path, const Headers &headers,
|
||||
const std::string &body, size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *body, size_t content_length, ContentProvider content_provider,
|
||||
ContentProviderWithoutLength content_provider_without_length,
|
||||
const char *content_type) {
|
||||
auto error = Error::Success;
|
||||
@@ -5661,6 +5718,19 @@ inline Result ClientImpl::Post(const char *path) {
|
||||
return Post(path, std::string(), nullptr);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Post(const char *path, const char *body,
|
||||
size_t content_length,
|
||||
const char *content_type) {
|
||||
return Post(path, Headers(), body, content_length, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("POST", path, headers, body, content_length,
|
||||
nullptr, nullptr, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Post(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return Post(path, Headers(), body, content_type);
|
||||
@@ -5669,8 +5739,9 @@ inline Result ClientImpl::Post(const char *path, const std::string &body,
|
||||
inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
||||
const std::string &body,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("POST", path, headers, body, 0, nullptr,
|
||||
nullptr, content_type);
|
||||
return send_with_content_provider("POST", path, headers, body.data(),
|
||||
body.size(), nullptr, nullptr,
|
||||
content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Post(const char *path, const Params ¶ms) {
|
||||
@@ -5694,7 +5765,7 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
||||
size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("POST", path, headers, std::string(),
|
||||
return send_with_content_provider("POST", path, headers, nullptr,
|
||||
content_length, std::move(content_provider),
|
||||
nullptr, content_type);
|
||||
}
|
||||
@@ -5702,9 +5773,8 @@ inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
||||
inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
||||
ContentProviderWithoutLength content_provider,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("POST", path, headers, std::string(), 0,
|
||||
nullptr, std::move(content_provider),
|
||||
content_type);
|
||||
return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
|
||||
std::move(content_provider), content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Post(const char *path, const Headers &headers,
|
||||
@@ -5758,6 +5828,18 @@ inline Result ClientImpl::Put(const char *path) {
|
||||
return Put(path, std::string(), nullptr);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Put(const char *path, const char *body,
|
||||
size_t content_length, const char *content_type) {
|
||||
return Put(path, Headers(), body, content_length, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PUT", path, headers, body, content_length,
|
||||
nullptr, nullptr, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Put(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return Put(path, Headers(), body, content_type);
|
||||
@@ -5766,8 +5848,9 @@ inline Result ClientImpl::Put(const char *path, const std::string &body,
|
||||
inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
||||
const std::string &body,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
|
||||
nullptr, content_type);
|
||||
return send_with_content_provider("PUT", path, headers, body.data(),
|
||||
body.size(), nullptr, nullptr,
|
||||
content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Put(const char *path, size_t content_length,
|
||||
@@ -5787,7 +5870,7 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
||||
size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PUT", path, headers, std::string(),
|
||||
return send_with_content_provider("PUT", path, headers, nullptr,
|
||||
content_length, std::move(content_provider),
|
||||
nullptr, content_type);
|
||||
}
|
||||
@@ -5795,9 +5878,8 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
||||
inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
||||
ContentProviderWithoutLength content_provider,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PUT", path, headers, std::string(), 0,
|
||||
nullptr, std::move(content_provider),
|
||||
content_type);
|
||||
return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
|
||||
std::move(content_provider), content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Put(const char *path, const Params ¶ms) {
|
||||
@@ -5810,6 +5892,24 @@ inline Result ClientImpl::Put(const char *path, const Headers &headers,
|
||||
return Put(path, headers, query, "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Patch(const char *path) {
|
||||
return Patch(path, std::string(), nullptr);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Patch(const char *path, const char *body,
|
||||
size_t content_length,
|
||||
const char *content_type) {
|
||||
return Patch(path, Headers(), body, content_length, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PATCH", path, headers, body,
|
||||
content_length, nullptr, nullptr,
|
||||
content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Patch(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return Patch(path, Headers(), body, content_type);
|
||||
@@ -5818,8 +5918,9 @@ inline Result ClientImpl::Patch(const char *path, const std::string &body,
|
||||
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
|
||||
const std::string &body,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
|
||||
nullptr, content_type);
|
||||
return send_with_content_provider("PATCH", path, headers, body.data(),
|
||||
body.size(), nullptr, nullptr,
|
||||
content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Patch(const char *path, size_t content_length,
|
||||
@@ -5839,7 +5940,7 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
|
||||
size_t content_length,
|
||||
ContentProvider content_provider,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PATCH", path, headers, std::string(),
|
||||
return send_with_content_provider("PATCH", path, headers, nullptr,
|
||||
content_length, std::move(content_provider),
|
||||
nullptr, content_type);
|
||||
}
|
||||
@@ -5847,26 +5948,26 @@ inline Result ClientImpl::Patch(const char *path, const Headers &headers,
|
||||
inline Result ClientImpl::Patch(const char *path, const Headers &headers,
|
||||
ContentProviderWithoutLength content_provider,
|
||||
const char *content_type) {
|
||||
return send_with_content_provider("PATCH", path, headers, std::string(), 0,
|
||||
nullptr, std::move(content_provider),
|
||||
content_type);
|
||||
return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
|
||||
std::move(content_provider), content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path) {
|
||||
return Delete(path, Headers(), std::string(), nullptr);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return Delete(path, Headers(), body, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const Headers &headers) {
|
||||
return Delete(path, headers, std::string(), nullptr);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const char *body,
|
||||
size_t content_length,
|
||||
const char *content_type) {
|
||||
return Delete(path, Headers(), body, content_length, content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const Headers &headers,
|
||||
const std::string &body,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
Request req;
|
||||
req.method = "DELETE";
|
||||
@@ -5875,11 +5976,22 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
|
||||
req.path = path;
|
||||
|
||||
if (content_type) { req.headers.emplace("Content-Type", content_type); }
|
||||
req.body = body;
|
||||
req.body.assign(body, content_length);
|
||||
|
||||
return send(req);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return Delete(path, Headers(), body.data(), body.size(), content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const Headers &headers,
|
||||
const std::string &body,
|
||||
const char *content_type) {
|
||||
return Delete(path, headers, body.data(), body.size(), content_type);
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Options(const char *path) {
|
||||
return Options(path, Headers());
|
||||
}
|
||||
@@ -6819,6 +6931,15 @@ inline Result Client::Head(const char *path, const Headers &headers) {
|
||||
}
|
||||
|
||||
inline Result Client::Post(const char *path) { return cli_->Post(path); }
|
||||
inline Result Client::Post(const char *path, const char *body,
|
||||
size_t content_length, const char *content_type) {
|
||||
return cli_->Post(path, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Post(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return cli_->Post(path, headers, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Post(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return cli_->Post(path, body, content_type);
|
||||
@@ -6871,6 +6992,15 @@ inline Result Client::Post(const char *path, const Headers &headers,
|
||||
return cli_->Post(path, headers, items, boundary);
|
||||
}
|
||||
inline Result Client::Put(const char *path) { return cli_->Put(path); }
|
||||
inline Result Client::Put(const char *path, const char *body,
|
||||
size_t content_length, const char *content_type) {
|
||||
return cli_->Put(path, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Put(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return cli_->Put(path, headers, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Put(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return cli_->Put(path, body, content_type);
|
||||
@@ -6909,6 +7039,16 @@ inline Result Client::Put(const char *path, const Headers &headers,
|
||||
const Params ¶ms) {
|
||||
return cli_->Put(path, headers, params);
|
||||
}
|
||||
inline Result Client::Patch(const char *path) { return cli_->Patch(path); }
|
||||
inline Result Client::Patch(const char *path, const char *body,
|
||||
size_t content_length, const char *content_type) {
|
||||
return cli_->Patch(path, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Patch(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return cli_->Patch(path, headers, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Patch(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return cli_->Patch(path, body, content_type);
|
||||
@@ -6941,13 +7081,22 @@ inline Result Client::Patch(const char *path, const Headers &headers,
|
||||
return cli_->Patch(path, headers, std::move(content_provider), content_type);
|
||||
}
|
||||
inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
|
||||
inline Result Client::Delete(const char *path, const Headers &headers) {
|
||||
return cli_->Delete(path, headers);
|
||||
}
|
||||
inline Result Client::Delete(const char *path, const char *body,
|
||||
size_t content_length, const char *content_type) {
|
||||
return cli_->Delete(path, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Delete(const char *path, const Headers &headers,
|
||||
const char *body, size_t content_length,
|
||||
const char *content_type) {
|
||||
return cli_->Delete(path, headers, body, content_length, content_type);
|
||||
}
|
||||
inline Result Client::Delete(const char *path, const std::string &body,
|
||||
const char *content_type) {
|
||||
return cli_->Delete(path, body, content_type);
|
||||
}
|
||||
inline Result Client::Delete(const char *path, const Headers &headers) {
|
||||
return cli_->Delete(path, headers);
|
||||
}
|
||||
inline Result Client::Delete(const char *path, const Headers &headers,
|
||||
const std::string &body,
|
||||
const char *content_type) {
|
||||
|
||||
+223
-25
@@ -46,16 +46,31 @@ TEST(StartupTest, WSAStartup) {
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest){
|
||||
TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest) {
|
||||
string unescapedCharacters = "-_.!~*'()";
|
||||
|
||||
EXPECT_EQ(detail::encode_query_param(unescapedCharacters), "-_.!~*'()");
|
||||
}
|
||||
|
||||
TEST(EncodeQueryParamTest, ParseReservedCharactersTest){
|
||||
TEST(EncodeQueryParamTest, ParseReservedCharactersTest) {
|
||||
string reservedCharacters = ";,/?:@&=+$";
|
||||
|
||||
EXPECT_EQ(detail::encode_query_param(reservedCharacters), "%3B%2C%2F%3F%3A%40%26%3D%2B%24");
|
||||
EXPECT_EQ(detail::encode_query_param(reservedCharacters),
|
||||
"%3B%2C%2F%3F%3A%40%26%3D%2B%24");
|
||||
}
|
||||
|
||||
TEST(EncodeQueryParamTest, TestUTF8Characters) {
|
||||
string chineseCharacters = "中国語";
|
||||
string russianCharacters = "дом";
|
||||
string brazilianCharacters = "óculos";
|
||||
|
||||
EXPECT_EQ(detail::encode_query_param(chineseCharacters),
|
||||
"%E4%B8%AD%E5%9B%BD%E8%AA%9E");
|
||||
|
||||
EXPECT_EQ(detail::encode_query_param(russianCharacters),
|
||||
"%D0%B4%D0%BE%D0%BC");
|
||||
|
||||
EXPECT_EQ(detail::encode_query_param(brazilianCharacters), "%C3%B3culos");
|
||||
}
|
||||
|
||||
TEST(TrimTests, TrimStringTests) {
|
||||
@@ -717,9 +732,8 @@ TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
TEST(AbsoluteRedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLClient cli(host);
|
||||
@@ -728,13 +742,13 @@ TEST(AbsoluteRedirectTest, Redirect) {
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/absolute-redirect/3");
|
||||
auto res = cli.Get("/httpbin/absolute-redirect/3");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLClient cli(host);
|
||||
@@ -743,13 +757,13 @@ TEST(RedirectTest, Redirect) {
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/redirect/3");
|
||||
auto res = cli.Get("/httpbin/redirect/3");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RelativeRedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLClient cli(host);
|
||||
@@ -758,13 +772,13 @@ TEST(RelativeRedirectTest, Redirect) {
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/relative-redirect/3");
|
||||
auto res = cli.Get("/httpbin/relative-redirect/3");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(TooManyRedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
auto host = "nghttp2.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLClient cli(host);
|
||||
@@ -773,11 +787,10 @@ TEST(TooManyRedirectTest, Redirect) {
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/redirect/21");
|
||||
auto res = cli.Get("/httpbin/redirect/21");
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::ExceedRedirectCount, res.error());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(YahooRedirectTest, Redirect) {
|
||||
@@ -794,16 +807,14 @@ TEST(YahooRedirectTest, Redirect) {
|
||||
EXPECT_EQ("https://yahoo.com/", res->location);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res =
|
||||
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
auto res = cli.Get(
|
||||
"/httpbin/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(RedirectToDifferentPort, Redirect) {
|
||||
Server svr8080;
|
||||
@@ -942,6 +953,77 @@ TEST(ErrorHandlerTest, ContentLength) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(RoutingHandlerTest, PreRoutingHandler) {
|
||||
Server svr;
|
||||
|
||||
svr.set_pre_routing_handler([](const Request &req, Response &res) {
|
||||
if (req.path == "/routing_handler") {
|
||||
res.set_header("PRE_ROUTING", "on");
|
||||
res.set_content("Routing Handler", "text/plain");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
svr.set_error_handler([](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Error", "text/html");
|
||||
});
|
||||
|
||||
svr.set_post_routing_handler([](const Request &req, Response &res) {
|
||||
if (req.path == "/routing_handler") {
|
||||
res.set_header("POST_ROUTING", "on");
|
||||
}
|
||||
});
|
||||
|
||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!\n", "text/plain");
|
||||
});
|
||||
|
||||
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/routing_handler");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Routing Handler", res->body);
|
||||
EXPECT_EQ(1, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ("on", res->get_header_value("PRE_ROUTING"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("POST_ROUTING"));
|
||||
EXPECT_EQ("on", res->get_header_value("POST_ROUTING"));
|
||||
}
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("Hello World!\n", res->body);
|
||||
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
|
||||
}
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/aaa");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(404, res->status);
|
||||
EXPECT_EQ("Error", res->body);
|
||||
EXPECT_EQ(0, res->get_header_value_count("PRE_ROUTING"));
|
||||
EXPECT_EQ(0, res->get_header_value_count("POST_ROUTING"));
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(InvalidFormatTest, StatusCode) {
|
||||
Server svr;
|
||||
|
||||
@@ -1373,6 +1455,38 @@ protected:
|
||||
std::string url = "/redirect/" + std::to_string(num);
|
||||
res.set_redirect(url);
|
||||
})
|
||||
.Post("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
res.set_content(req.body, "application/octet-stream");
|
||||
})
|
||||
.Put("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
res.set_content(req.body, "application/octet-stream");
|
||||
})
|
||||
.Patch("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
res.set_content(req.body, "application/octet-stream");
|
||||
})
|
||||
.Delete("/binary",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(4, req.body.size());
|
||||
EXPECT_EQ("application/octet-stream",
|
||||
req.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("4", req.get_header_value("Content-Length"));
|
||||
res.set_content(req.body, "application/octet-stream");
|
||||
})
|
||||
#if defined(CPPHTTPLIB_ZLIB_SUPPORT) || defined(CPPHTTPLIB_BROTLI_SUPPORT)
|
||||
.Get("/compress",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
@@ -1727,6 +1841,58 @@ TEST_F(ServerTest, InvalidBaseDirMount) {
|
||||
EXPECT_EQ(false, svr_.set_mount_point("invalid_mount_point", "./www3"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, Binary) {
|
||||
std::vector<char> binary{0x00, 0x01, 0x02, 0x03};
|
||||
|
||||
auto res = cli_.Post("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
|
||||
res = cli_.Put("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
|
||||
res = cli_.Patch("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
|
||||
res = cli_.Delete("/binary", binary.data(), binary.size(),
|
||||
"application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, BinaryString) {
|
||||
auto binary = std::string("\x00\x01\x02\x03", 4);
|
||||
|
||||
auto res = cli_.Post("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
|
||||
res = cli_.Put("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
|
||||
res = cli_.Patch("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
|
||||
res = cli_.Delete("/binary", binary, "application/octet-stream");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ(4, res->body.size());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, EmptyRequest) {
|
||||
auto res = cli_.Get("");
|
||||
ASSERT_TRUE(!res);
|
||||
@@ -3136,9 +3302,9 @@ TEST(MountTest, Unmount) {
|
||||
TEST(ExceptionTest, ThrowExceptionInHandler) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||
svr.Get("/hi", [&](const Request & /*req*/, Response & /*res*/) {
|
||||
throw std::runtime_error("exception...");
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
// res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
@@ -3199,6 +3365,39 @@ TEST(KeepAliveTest, ReadTimeout) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
|
||||
Server svr;
|
||||
|
||||
svr.set_error_handler([](Request const &, Response &res) -> void {
|
||||
res.set_chunked_content_provider(
|
||||
"text/plain", [](std::size_t const, DataSink &sink) -> bool {
|
||||
sink.os << "hello";
|
||||
sink.os << "world";
|
||||
sink.done();
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(404, res->status);
|
||||
EXPECT_EQ("helloworld", res->body);
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(KeepAliveTest, ReadTimeoutSSL) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
@@ -3755,15 +3954,14 @@ TEST(DecodeWithChunkedEncoding, BrotliEncoding) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
TEST(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||
Client cli("https://nghttp2.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res =
|
||||
Client("https://httpbin.org")
|
||||
.set_follow_location(true)
|
||||
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
cli.Get("/httpbin/"
|
||||
"redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+27
-30
@@ -8,29 +8,29 @@ using namespace httplib;
|
||||
template <typename T>
|
||||
void ProxyTest(T& cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
auto res = cli.Get("/get");
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(407, res->status);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLBasic) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
ProxyTest(cli, true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(ProxyTest, SSLBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
ProxyTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, NoSSLDigest) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
ProxyTest(cli, false);
|
||||
}
|
||||
|
||||
TEST(ProxyTest, SSLDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
ProxyTest(cli, false);
|
||||
}
|
||||
#endif
|
||||
@@ -54,29 +54,27 @@ void RedirectProxyText(T& cli, const char *path, bool basic) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST(RedirectTest, HTTPBinNoSSLBasic) {
|
||||
Client cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", true);
|
||||
Client cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", true);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(RedirectTest, HTTPBinNoSSLDigest) {
|
||||
Client cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", false);
|
||||
Client cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", false);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinSSLBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", true);
|
||||
SSLClient cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, HTTPBinSSLDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
RedirectProxyText(cli, "/redirect/2", false);
|
||||
SSLClient cli("nghttp2.org");
|
||||
RedirectProxyText(cli, "/httpbin/redirect/2", false);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(RedirectTest, YouTubeNoSSLBasic) {
|
||||
@@ -218,7 +216,8 @@ TEST(DigestAuthTest, NoSSL) {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void KeepAliveTest(Client& cli, bool basic) {
|
||||
template <typename T>
|
||||
void KeepAliveTest(T& cli, bool basic) {
|
||||
cli.set_proxy("localhost", basic ? 3128 : 3129);
|
||||
if (basic) {
|
||||
cli.set_proxy_basic_auth("hello", "world");
|
||||
@@ -234,20 +233,20 @@ void KeepAliveTest(Client& cli, bool basic) {
|
||||
#endif
|
||||
|
||||
{
|
||||
auto res = cli.Get("/get");
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
{
|
||||
auto res = cli.Get("/redirect/2");
|
||||
auto res = cli.Get("/httpbin/redirect/2");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> paths = {
|
||||
"/digest-auth/auth/hello/world/MD5",
|
||||
"/digest-auth/auth/hello/world/SHA-256",
|
||||
"/digest-auth/auth/hello/world/SHA-512",
|
||||
"/digest-auth/auth-int/hello/world/MD5",
|
||||
"/httpbin/digest-auth/auth/hello/world/MD5",
|
||||
"/httpbin/digest-auth/auth/hello/world/SHA-256",
|
||||
"/httpbin/digest-auth/auth/hello/world/SHA-512",
|
||||
"/httpbin/digest-auth/auth-int/hello/world/MD5",
|
||||
};
|
||||
|
||||
for (auto path: paths) {
|
||||
@@ -258,34 +257,32 @@ void KeepAliveTest(Client& cli, bool basic) {
|
||||
}
|
||||
|
||||
{
|
||||
int count = 100;
|
||||
int count = 10;
|
||||
while (count--) {
|
||||
auto res = cli.Get("/get");
|
||||
auto res = cli.Get("/httpbin/get");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(KeepAliveTest, NoSSLWithBasic) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
KeepAliveTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, SSLWithBasic) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
KeepAliveTest(cli, true);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, NoSSLWithDigest) {
|
||||
Client cli("httpbin.org");
|
||||
Client cli("nghttp2.org");
|
||||
KeepAliveTest(cli, false);
|
||||
}
|
||||
|
||||
TEST(KeepAliveTest, SSLWithDigest) {
|
||||
SSLClient cli("httpbin.org");
|
||||
SSLClient cli("nghttp2.org");
|
||||
KeepAliveTest(cli, false);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user