Compare commits

..

19 Commits

Author SHA1 Message Date
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
4 changed files with 622 additions and 260 deletions
+86 -68
View File
@@ -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
-------------------------------
+286 -137
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>
@@ -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 &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);
@@ -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,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 &params) {
@@ -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 &params) {
@@ -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 &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);
@@ -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
View File
@@ -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
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