Compare commits

..

34 Commits

Author SHA1 Message Date
Eric Lee 8d9a477edb No content check (#823)
* No content check

* unit test for no content

* fixing merge conflict break

* oops during manual merge conflict
2021-01-13 17:51:40 -05:00
yhirose 85b4abbf16 Updated the simple example 2021-01-07 19:56:33 -05:00
yhirose e42a358da8 Updated README 2021-01-07 19:00:24 -05:00
yhirose f008fe4539 Added middleware support (#816) 2021-01-07 18:40:52 -05:00
yhirose ddf41d29ef Added const *char and size_t interface 2021-01-06 22:39:58 -05:00
yhirose 3f88a46c4a Code format 2020-12-31 18:14:28 -05:00
yhirose 242706ea34 Fix #820 2020-12-31 18:14:05 -05:00
yhirose a9f5f8683f Fixed warnings on Visual C++ 2020-12-31 11:35:11 -05:00
yhirose 60c2213893 Fix #817 2020-12-31 10:58:44 -05:00
yhirose eb2d28bca2 Code cleanup 2020-12-31 10:37:06 -05:00
Yuri Santos 86f637a246 Added encode_uri_param tests with UTF-8 characters (#818)
Remove additional lines between tests
2020-12-30 23:06:36 -05:00
yhirose 2c07ec4600 Code cleanup 2020-12-29 09:39:19 -05:00
yhirose 871d8d67b0 Made Request paramater const in handle_file_request 2020-12-28 22:03:44 -05:00
Anonymous 7299713195 Fix readme Response::set_chunked_content_provider (#811)
`Response::set_chunked_content_provider` has formal parameters, not one.
2020-12-25 19:55:57 -05:00
yhirose 96afa7e108 Updated README 2020-12-21 13:40:32 -05:00
yhirose 55f57af0b9 Update README 2020-12-21 09:27:36 -05:00
yhirose 6b35cd0116 Updated README 2020-12-21 08:15:06 -05:00
yhirose 99f2229e48 Updated README 2020-12-19 22:43:31 -05:00
yhirose b9641048fc Switch to hghttp2.org/httpbin for redirect test. (#538) 2020-12-19 20:27:52 -05:00
yhirose e9c6c6e609 Code format 2020-12-19 20:14:53 -05:00
yhirose 40db42108f Fixed problem with invalid requests including spaces in URL path 2020-12-19 12:03:08 -05:00
Anonymous 24bb1387d6 Update README.md (#806) 2020-12-19 11:12:44 -05:00
Jeremie Rahm d0bd4afb0b Ensure socket is closed after processing in SSLServer (#804) 2020-12-18 19:29:36 -05:00
Yuri Santos 78ea786abd [PR] Special function to encode query params (#801)
* Special function to encode query params

* Fix #include <iomanip>

* Added unescaped charsets to encode_query_param

* Unit tests for encode_query_param
2020-12-18 17:51:11 -05:00
Miosame 9cac2c9ceb typo: specitic => specific (#802) 2020-12-18 15:12:21 -05:00
Anonymous 0cff3245df Extend built-in extension MIME mapping (#799)
* Update README.md

* Update httplib.h

* Update httplib.h

* Update httplib.h

* Update httplib.h

* Remove duplicate cases

Someone left a bunch of duplicate cases, idiot, couldn't have been me.

* Reformat

Modify spacing and whatnot

* Update README.md
2020-12-18 09:32:19 -05:00
yhirose 0e3925db3f Fixed build error 2020-12-18 00:07:48 +00:00
yhirose c9a13d214b Changed not to use string_view 2020-12-17 18:48:27 -05:00
yhirose 0954af2d4c Use user-defined literals for file extention match 2020-12-17 18:27:04 -05:00
yhirose 7c1c952f5a Don't allow invalid status code format (It sould be a three-digit code.) 2020-12-15 20:25:24 -05:00
yhirose a6edfc730a Added a unit test for static file with range 2020-12-15 18:47:51 -05:00
yhirose c1264bfedc Fix problem with mp4 w/ Range header 2020-12-14 22:41:05 -05:00
yhirose 90a5b6ceb0 Updated README 2020-12-04 19:39:39 -05:00
yhirose eb240ad2e5 Code cleanup 2020-12-03 16:03:12 -05:00
4 changed files with 763 additions and 230 deletions
+97 -53
View File
@@ -5,7 +5,7 @@ cpp-httplib
A C++11 single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include **httplib.h** file in your code!
It's extremely easy to setup. Just include the **httplib.h** file in your code!
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
@@ -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,24 +133,30 @@ 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 | 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 the static file server methods are not thread safe.
NOTE: These static file server methods are not thread-safe.
### Logging
@@ -158,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
@@ -171,7 +210,7 @@ svr.Post("/multipart", [&](const auto& req, auto& res) {
});
```
### Receive content with Content receiver
### Receive content with a content receiver
```cpp
svr.Post("/content_receiver",
@@ -198,7 +237,7 @@ svr.Post("/content_receiver",
});
```
### Send content with Content provider
### Send content with the content provider
```cpp
const size_t DATA_CHUNK_SIZE = 4;
@@ -242,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);
@@ -255,7 +295,7 @@ svr.Get("/chunked", [&](const Request& req, Response& res) {
### 'Expect: 100-continue' handler
As default, the server sends `100 Continue` response for `Expect: 100-continue` header.
By default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.
```cpp
// Send a '417 Expectation Failed' response.
@@ -286,7 +326,7 @@ svr.set_write_timeout(5, 0); // 5 seconds
svr.set_idle_interval(0, 100000); // 100 milliseconds
```
### Set maximum payload length for reading request body
### Set maximum payload length for reading a request body
```c++
svr.set_payload_max_length(1024 * 1024 * 512); // 512MB
@@ -364,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
@@ -471,7 +512,7 @@ cli.set_read_timeout(5, 0); // 5 seconds
cli.set_write_timeout(5, 0); // 5 seconds
```
### Receive content with Content receiver
### Receive content with a content receiver
```c++
std::string body;
@@ -498,7 +539,7 @@ auto res = cli.Get(
});
```
### Send content with Content provider
### Send content with a content provider
```cpp
std::string body = ...;
@@ -621,7 +662,7 @@ res = cli.Get("/");
res->status; // 200
```
### Use a specitic network interface
### Use a specific network interface
NOTE: This feature is not available on Windows, yet.
@@ -629,33 +670,10 @@ 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
-----------
The server can applie compression to the following MIME type contents:
The server can apply compression to the following MIME type contents:
* all text types except text/event-stream
* image/svg+xml
@@ -688,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
-------------------------------
@@ -719,7 +763,7 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
#include <httplib.h>
```
Note: Cygwin on Windows is not supported.
Note: Windows 8 or lower and Cygwin on Windows are not supported.
License
-------
+341 -125
View File
@@ -192,6 +192,7 @@ using socket_t = int;
#include <fcntl.h>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
@@ -214,7 +215,6 @@ using socket_t = int;
#include <openssl/applink.c>
#endif
#include <iomanip>
#include <iostream>
#include <sstream>
@@ -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 &params);
Result Put(const char *path, const Headers &headers, const Params &params);
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);
@@ -1025,7 +1050,7 @@ protected:
private:
socket_t create_client_socket(Error &error) const;
bool read_response_line(Stream &strm, Response &res);
bool read_response_line(Stream &strm, const Request &req, Response &res);
bool write_request(Stream &strm, const Request &req, bool close_connection,
Error &error);
bool redirect(const Request &req, Response &res, Error &error);
@@ -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 &params);
Result Put(const char *path, const Headers &headers, const Params &params);
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,6 +1497,27 @@ inline bool is_valid_path(const std::string &path) {
return true;
}
inline std::string encode_query_param(const std::string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
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::nouppercase;
}
}
return escaped.str();
}
inline std::string encode_url(const std::string &s) {
std::string result;
@@ -2135,6 +2196,25 @@ inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
}
}
inline constexpr unsigned int str2tag_core(const char *s, size_t l,
unsigned int h) {
return (l == 0) ? h
: str2tag_core(s + 1, l - 1,
(h * 33) ^ static_cast<unsigned char>(*s));
}
inline unsigned int str2tag(const std::string &s) {
return str2tag_core(s.data(), s.size(), 0);
}
namespace udl {
inline constexpr unsigned int operator"" _(const char *s, size_t l) {
return str2tag_core(s, l, 0);
}
} // namespace udl
inline const char *
find_content_type(const std::string &path,
const std::map<std::string, std::string> &user_data) {
@@ -2143,36 +2223,60 @@ find_content_type(const std::string &path,
auto it = user_data.find(ext);
if (it != user_data.end()) { return it->second.c_str(); }
if (ext == "txt") {
return "text/plain";
} else if (ext == "html" || ext == "htm") {
return "text/html";
} else if (ext == "css") {
return "text/css";
} else if (ext == "jpeg" || ext == "jpg") {
return "image/jpg";
} else if (ext == "png") {
return "image/png";
} else if (ext == "gif") {
return "image/gif";
} else if (ext == "svg") {
return "image/svg+xml";
} else if (ext == "ico") {
return "image/x-icon";
} else if (ext == "json") {
return "application/json";
} else if (ext == "pdf") {
return "application/pdf";
} else if (ext == "js") {
return "application/javascript";
} else if (ext == "wasm") {
return "application/wasm";
} else if (ext == "xml") {
return "application/xml";
} else if (ext == "xhtml") {
return "application/xhtml+xml";
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";
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 "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 "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";
}
return nullptr;
}
inline const char *status_message(int status) {
@@ -3002,7 +3106,7 @@ inline std::string params_to_query_str(const Params &params) {
if (it != params.begin()) { query += "&"; }
query += it->first;
query += "=";
query += encode_url(it->second);
query += encode_query_param(it->second);
}
return query;
}
@@ -3490,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);
@@ -4062,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); }
@@ -4079,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;
}
@@ -4134,7 +4254,7 @@ inline void Server::stop() {
inline bool Server::parse_request_line(const char *s, Request &req) {
const static std::regex re(
"(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
"(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n");
"(([^? ]+)(?:\\?([^ ]*?))?) (HTTP/1\\.[01])\r\n");
std::cmatch m;
if (std::regex_match(s, m, re)) {
@@ -4170,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 {
@@ -4200,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") {
@@ -4281,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) {
@@ -4378,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
@@ -4396,7 +4521,7 @@ inline bool Server::handle_file_request(Request &req, Response &res,
for (const auto &kv : entry.headers) {
res.set_header(kv.first.c_str(), kv.second);
}
res.status = 200;
res.status = req.has_header("Range") ? 206 : 200;
if (!head && file_request_handler_) {
file_request_handler_(req, res);
}
@@ -4502,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) &&
@@ -4943,17 +5070,20 @@ inline void ClientImpl::lock_socket_and_shutdown_and_close() {
close_socket(socket_);
}
inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
Response &res) {
std::array<char, 2048> buf;
detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
if (!line_reader.getline()) { return false; }
const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n");
const static std::regex re("(HTTP/1\\.[01]) (\\d{3}) (.*?)\r\n");
std::cmatch m;
if (!std::regex_match(line_reader.ptr(), m, re)) { return true; }
if (!std::regex_match(line_reader.ptr(), m, re)) {
return req.method == "CONNECT";
}
res.version = std::string(m[1]);
res.status = std::stoi(std::string(m[2]));
res.reason = std::string(m[3]);
@@ -5197,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"); }
@@ -5274,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
@@ -5295,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) {
@@ -5349,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;
@@ -5372,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);
;
}
}
@@ -5382,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;
@@ -5400,7 +5530,7 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
if (!write_request(strm, req, close_connection, error)) { return false; }
// Receive response and headers
if (!read_response_line(strm, res) ||
if (!read_response_line(strm, req, res) ||
!detail::read_headers(strm, res.headers)) {
error = Error::Read;
return false;
@@ -5414,7 +5544,7 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
}
// Body
if (req.method != "HEAD" && req.method != "CONNECT") {
if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") {
auto out =
req.content_receiver_
? static_cast<ContentReceiverWithProgress>(
@@ -5441,18 +5571,12 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
};
int dummy_status;
// std::cout << "A" << std::endl;
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
dummy_status, std::move(progress), std::move(out),
decompress_)) {
// std::cout << "B" << std::endl;
if (error != Error::Canceled) {
// std::cout << "C" << std::endl;
error = Error::Read;
}
if (error != Error::Canceled) { error = Error::Read; }
return false;
}
// std::cout << "D" << std::endl;
}
if (res.get_header_value("Connection") == "close" ||
@@ -5594,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);
@@ -5602,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 &params) {
@@ -5627,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);
}
@@ -5635,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,
@@ -5691,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);
@@ -5699,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,
@@ -5720,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);
}
@@ -5728,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 &params) {
@@ -5743,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);
@@ -5751,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,
@@ -5772,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);
}
@@ -5780,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";
@@ -5808,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());
}
@@ -6234,8 +6413,9 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
},
[](SSL * /*ssl*/) { return true; });
bool ret = false;
if (ssl) {
auto ret = detail::process_server_socket_ssl(
ret = detail::process_server_socket_ssl(
ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
write_timeout_usec_,
@@ -6249,12 +6429,11 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
// the connection appeared to be closed.
const bool shutdown_gracefully = ret;
detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
return ret;
}
detail::shutdown_socket(sock);
detail::close_socket(sock);
return false;
return ret;
}
// SSL HTTP client implementation
@@ -6752,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);
@@ -6804,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);
@@ -6842,6 +7039,16 @@ inline Result Client::Put(const char *path, const Headers &headers,
const Params &params) {
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);
@@ -6874,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) {
+298 -22
View File
@@ -46,6 +46,33 @@ TEST(StartupTest, WSAStartup) {
}
#endif
TEST(EncodeQueryParamTest, ParseUnescapedChararactersTest) {
string unescapedCharacters = "-_.!~*'()";
EXPECT_EQ(detail::encode_query_param(unescapedCharacters), "-_.!~*'()");
}
TEST(EncodeQueryParamTest, ParseReservedCharactersTest) {
string reservedCharacters = ";,/?:@&=+$";
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) {
EXPECT_EQ("abc", detail::trim_copy("abc"));
EXPECT_EQ("abc", detail::trim_copy(" abc "));
@@ -705,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);
@@ -716,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);
@@ -731,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);
@@ -746,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);
@@ -761,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) {
@@ -782,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;
@@ -930,6 +953,127 @@ TEST(ErrorHandlerTest, ContentLength) {
ASSERT_FALSE(svr.is_running());
}
TEST(NoContentTest, ContentLength) {
Server svr;
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
res.status = 204;
});
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("/hi");
ASSERT_TRUE(res);
EXPECT_EQ(204, res->status);
EXPECT_EQ("0", res->get_header_value("Content-Length"));
}
svr.stop();
thread.join();
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;
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
res.set_content("Hello World!\n", "text/plain");
res.status = 9999; // Status should be a three-digit code...
});
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("/hi");
ASSERT_FALSE(res);
}
svr.stop();
thread.join();
ASSERT_FALSE(svr.is_running());
}
class ServerTest : public ::testing::Test {
protected:
ServerTest()
@@ -1336,6 +1480,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) {
@@ -1676,10 +1852,72 @@ TEST_F(ServerTest, UserDefinedMIMETypeMapping) {
EXPECT_EQ("abcde", res->body);
}
TEST_F(ServerTest, StaticFileRange) {
auto res = cli_.Get("/dir/test.abcde", {{make_range_header({{2, 3}})}});
ASSERT_TRUE(res);
EXPECT_EQ(206, res->status);
EXPECT_EQ("text/abcde", res->get_header_value("Content-Type"));
EXPECT_EQ("2", res->get_header_value("Content-Length"));
EXPECT_EQ(true, res->has_header("Content-Range"));
EXPECT_EQ(std::string("cd"), res->body);
}
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);
@@ -2957,6 +3195,12 @@ TEST(ServerRequestParsingTest, InvalidHeaderTextWithExtraCR) {
"Content-Type: text/plain\r\n\r");
}
TEST(ServerRequestParsingTest, InvalidSpaceInURL) {
std::string out;
test_raw_request("GET /h i HTTP/1.1\r\n\r\n", &out);
EXPECT_EQ("HTTP/1.1 400 Bad Request", out.substr(0, 24));
}
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
Server svr;
@@ -3083,9 +3327,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); });
@@ -3146,6 +3390,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);
@@ -3702,15 +3979,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
View File
@@ -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