Compare commits

..

19 Commits

Author SHA1 Message Date
yhirose 402d47e2cd Fix #407 2020-03-31 19:42:53 -04:00
yhirose 171fc2e353 Fix #403. Added more status codes based on MDN document 2020-03-26 20:50:40 -04:00
SoenkeHeeren ced4160d05 add http status code 201 to show the right status message in return headers (#402) 2020-03-26 12:20:32 -04:00
yhirose 5b51aa6851 Revert "Add 1000-concurrency-result report"
This reverts commit b0af78e340.
2020-03-24 17:30:32 -04:00
Igor [hyperxor] dc13cde820 Minor improvements in httplib classes (#395) 2020-03-23 06:54:13 -04:00
yhirose b0af78e340 Add 1000-concurrency-result report 2020-03-21 15:49:26 +00:00
Andrew Gasparovic 914c8860e8 Accept content by value to allow moving
Previously, calling set_content always resulted in 's' being copied. With this change, there will still be the same number of copies made (1) when doing `set_content(my_value, ...)`, but there will be no copies if a caller elects to do `set_content(std::move(value), ...)` or `set_content(some_function_that_returns_a_temporary(), ...)` instead.
2020-03-21 00:39:07 -04:00
yhirose dc6a72a0fd Fix #387 2020-03-17 18:03:52 -04:00
yhirose 685533ba50 Fixed warnings on Windows due to max/min macro 2020-03-16 13:58:09 -04:00
yhirose 6e46ccb37c Updated README 2020-03-15 12:05:12 -04:00
Aristo Chen ac18b70a0f Update calculation formula for progress percentage (#386) 2020-03-15 08:29:27 -04:00
yhirose e1acb949e7 Fix #382 2020-03-13 18:43:29 -04:00
yhirose ab96f49766 Fixed problem that line end char is missing on start messagein simple
server example
2020-03-13 16:36:33 -04:00
Oleg Vorobiov 7b3cea5317 Prevent an implicit capture of 'this' via '[=]' (#381) 2020-03-12 12:31:22 -04:00
yhirose 26deffe0c6 Not to send 'EXCEPTION_WHAT' header to client 2020-03-10 17:44:36 -04:00
Rafael Leira e07c5fec01 simplest way to catch handler exceptions 2020-03-10 17:44:36 -04:00
miketsts 6e473a7c5c Fix conversion to ‘int’ from ‘long int’ warning (#377)
Co-authored-by: Michael Tseitlin <michael.tseitlin@concertio.com>
2020-03-10 14:48:14 -04:00
yhirose c74129a1c2 Fix #372 (#374) 2020-03-09 23:59:00 -04:00
yhirose 18e750b4e7 Code cleanup 2020-03-09 19:47:28 -04:00
7 changed files with 369 additions and 236 deletions
+31 -41
View File
@@ -9,6 +9,8 @@ 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!
For Windows users: Please read [this note](https://github.com/yhirose/cpp-httplib#windows).
Server Example
--------------
@@ -30,6 +32,16 @@ int main(void)
res.set_content(numbers, "text/plain");
});
svr.Get("/body-header-param", [](const Request& req, Response& res) {
if (req.has_header("Content-Length")) {
auto val = req.get_header_value("Content-Length");
}
if (req.has_param("key")) {
auto val = req.get_param_value("key");
}
res.set_content(req.body, "text/plain");
});
svr.Get("/stop", [&](const Request& req, Response& res) {
svr.stop();
});
@@ -362,7 +374,7 @@ std::shared_ptr<httplib::Response> res =
cli.Get("/", [](uint64_t len, uint64_t total) {
printf("%lld / %lld bytes => %d%% complete\n",
len, total,
(int)((len/total)*100));
(int)(len*100/total));
return true; // return 'false' if you want to cancel the request.
}
);
@@ -504,8 +516,25 @@ httplib.h httplib.cc
NOTE
----
### g++
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
### Windows
Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand.
```cpp
#include <httplib.h>
#include <Windows.h>
```
```cpp
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <httplib.h>
```
License
-------
@@ -514,43 +543,4 @@ MIT license (© 2020 Yuji Hirose)
Special Thanks To
-----------------
The following folks made great contributions to polish this library to totally another level from a simple toy!
* [Zefz](https://github.com/Zefz)
* [PixlRainbow](https://github.com/PixlRainbow)
* [sgraham](https://github.com/sgraham)
* [mrexodia](https://github.com/mrexodia)
* [hyperxor](https://github.com/hyperxor)
* [omaralvarez](https://github.com/omaralvarez)
* [vvanelslande](https://github.com/vvanelslande)
* [underscorediscovery](https://github.com/underscorediscovery)
* [sux2mfgj](https://github.com/sux2mfgj)
* [matvore](https://github.com/matvore)
* [intmain-io](https://github.com/intmain)
* [davidgfnet](https://github.com/davidgfnet)
* [crtxcr](https://github.com/crtxcr)
* [const-volatile](https://github.com/const)
* [aguadoenzo](https://github.com/aguadoenzo)
* [TheMaverickProgrammer](https://github.com/TheMaverickProgrammer)
* [vdudouyt](https://github.com/vdudouyt)
* [stupedama](https://github.com/stupedama)
* [rockwotj](https://github.com/rockwotj)
* [marknelson](https://github.com/marknelson)
* [jaspervandeven](https://github.com/jaspervandeven)
* [hans-erickson](https://github.com/hans)
* [ha11owed](https://github.com/ha11owed)
* [gulrak](https://github.com/gulrak)
* [dolphineye](https://github.com/dolphineye)
* [danielzehe](https://github.com/danielzehe)
* [batist73](https://github.com/batist73)
* [barryam3](https://github.com/barryam3)
* [adikabintang](https://github.com/adikabintang)
* [aaronalbers](https://github.com/aaronalbers)
* [Whitetigerswt](https://github.com/Whitetigerswt)
* [TangHuaiZhe](https://github.com/TangHuaiZhe)
* [Sil3ntStorm](https://github.com/Sil3ntStorm)
* [MannyClicks](https://github.com/MannyClicks)
* [DraTeots](https://github.com/DraTeots)
* [BastienDurel](https://github.com/BastienDurel)
* [vitalyster](https://github.com/vitalyster)
* [trollixx](https://github.com/trollixx)
[These folks](https://github.com/yhirose/cpp-httplib/graphs/contributors) made great contributions to polish this library to totally another level from a simple toy!
+1 -1
View File
@@ -127,7 +127,7 @@ int main(int argc, const char **argv) {
return 1;
}
cout << "The server started at port " << port << "...";
cout << "The server started at port " << port << "..." << endl;
svr.listen("localhost", port);
+251 -178
View File
@@ -41,7 +41,7 @@
#endif
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max())
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
#endif
#ifndef CPPHTTPLIB_RECV_BUFSIZ
@@ -50,7 +50,7 @@
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT \
(std::max(1u, std::thread::hardware_concurrency() - 1))
((std::max)(1u, std::thread::hardware_concurrency() - 1))
#endif
/*
@@ -156,6 +156,7 @@ using socket_t = int;
#include <openssl/x509v3.h>
#include <iomanip>
#include <iostream>
#include <sstream>
// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
@@ -313,7 +314,7 @@ struct Response {
void set_redirect(const char *url);
void set_content(const char *s, size_t n, const char *content_type);
void set_content(const std::string &s, const char *content_type);
void set_content(std::string s, const char *content_type);
void set_content_provider(
size_t length,
@@ -349,14 +350,14 @@ public:
virtual bool is_readable() const = 0;
virtual bool is_writable() const = 0;
virtual int read(char *ptr, size_t size) = 0;
virtual int write(const char *ptr, size_t size) = 0;
virtual ssize_t read(char *ptr, size_t size) = 0;
virtual ssize_t write(const char *ptr, size_t size) = 0;
virtual std::string get_remote_addr() const = 0;
template <typename... Args>
int write_format(const char *fmt, const Args &... args);
int write(const char *ptr);
int write(const std::string &s);
ssize_t write_format(const char *fmt, const Args &... args);
ssize_t write(const char *ptr);
ssize_t write(const std::string &s);
};
class TaskQueue {
@@ -511,7 +512,7 @@ private:
int bind_internal(const char *host, int port, int socket_flags);
bool listen_internal();
bool routing(Request &req, Response &res, Stream &strm, bool last_connection);
bool routing(Request &req, Response &res, Stream &strm);
bool handle_file_request(Request &req, Response &res, bool head = false);
bool dispatch_request(Request &req, Response &res, Handlers &handlers);
bool dispatch_request_for_content_reader(Request &req, Response &res,
@@ -524,14 +525,14 @@ private:
bool write_content_with_provider(Stream &strm, const Request &req,
Response &res, const std::string &boundary,
const std::string &content_type);
bool read_content(Stream &strm, bool last_connection, Request &req,
Response &res);
bool read_content_with_content_receiver(
Stream &strm, bool last_connection, Request &req, Response &res,
ContentReceiver receiver, MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, bool last_connection, Request &req,
Response &res, ContentReceiver receiver,
bool read_content(Stream &strm, Request &req, Response &res);
bool
read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver);
bool read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
ContentReceiver multipart_receiver);
@@ -845,12 +846,12 @@ public:
const char *client_ca_cert_file_path = nullptr,
const char *client_ca_cert_dir_path = nullptr);
virtual ~SSLServer();
~SSLServer() override;
virtual bool is_valid() const;
bool is_valid() const override;
private:
virtual bool process_and_close_socket(socket_t sock);
bool process_and_close_socket(socket_t sock) override;
SSL_CTX *ctx_;
std::mutex ctx_mutex_;
@@ -858,13 +859,13 @@ private:
class SSLClient : public Client {
public:
SSLClient(const std::string &host, int port = 443,
const std::string &client_cert_path = std::string(),
const std::string &client_key_path = std::string());
explicit SSLClient(const std::string &host, int port = 443,
const std::string &client_cert_path = std::string(),
const std::string &client_key_path = std::string());
virtual ~SSLClient();
~SSLClient() override;
virtual bool is_valid() const;
bool is_valid() const override;
void set_ca_cert_path(const char *ca_ceert_file_path,
const char *ca_cert_dir_path = nullptr);
@@ -876,12 +877,12 @@ public:
SSL_CTX *ssl_context() const noexcept;
private:
virtual bool process_and_close_socket(
bool process_and_close_socket(
socket_t sock, size_t request_count,
std::function<bool(Stream &strm, bool last_connection,
bool &connection_close)>
callback);
virtual bool is_ssl() const;
callback) override;
bool is_ssl() const override;
bool verify_host(X509 *server_cert) const;
bool verify_host_with_subject_alt_name(X509 *server_cert) const;
@@ -953,26 +954,26 @@ inline size_t to_utf8(int code, char *buff) {
buff[0] = (code & 0x7F);
return 1;
} else if (code < 0x0800) {
buff[0] = (0xC0 | ((code >> 6) & 0x1F));
buff[1] = (0x80 | (code & 0x3F));
buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
buff[1] = static_cast<char>(0x80 | (code & 0x3F));
return 2;
} else if (code < 0xD800) {
buff[0] = (0xE0 | ((code >> 12) & 0xF));
buff[1] = (0x80 | ((code >> 6) & 0x3F));
buff[2] = (0x80 | (code & 0x3F));
buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
buff[2] = static_cast<char>(0x80 | (code & 0x3F));
return 3;
} else if (code < 0xE000) { // D800 - DFFF is invalid...
return 0;
} else if (code < 0x10000) {
buff[0] = (0xE0 | ((code >> 12) & 0xF));
buff[1] = (0x80 | ((code >> 6) & 0x3F));
buff[2] = (0x80 | (code & 0x3F));
buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
buff[2] = static_cast<char>(0x80 | (code & 0x3F));
return 3;
} else if (code < 0x110000) {
buff[0] = (0xF0 | ((code >> 18) & 0x7));
buff[1] = (0x80 | ((code >> 12) & 0x3F));
buff[2] = (0x80 | ((code >> 6) & 0x3F));
buff[3] = (0x80 | (code & 0x3F));
buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
buff[3] = static_cast<char>(0x80 | (code & 0x3F));
return 4;
}
@@ -992,8 +993,8 @@ inline std::string base64_encode(const std::string &in) {
int val = 0;
int valb = -6;
for (uint8_t c : in) {
val = (val << 8) + c;
for (auto c : in) {
val = (val << 8) + static_cast<uint8_t>(c);
valb += 8;
while (valb >= 0) {
out.push_back(lookup[(val >> valb) & 0x3F]);
@@ -1188,7 +1189,7 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
timeval tv;
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<long>(usec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
#endif
@@ -1210,7 +1211,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
timeval tv;
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<long>(usec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
#endif
@@ -1243,7 +1244,7 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
timeval tv;
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<long>(usec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
(FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
@@ -1265,8 +1266,8 @@ public:
bool is_readable() const override;
bool is_writable() const override;
int read(char *ptr, size_t size) override;
int write(const char *ptr, size_t size) override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
std::string get_remote_addr() const override;
private:
@@ -1280,12 +1281,12 @@ class SSLSocketStream : public Stream {
public:
SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
time_t read_timeout_usec);
virtual ~SSLSocketStream();
~SSLSocketStream() override;
bool is_readable() const override;
bool is_writable() const override;
int read(char *ptr, size_t size) override;
int write(const char *ptr, size_t size) override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
std::string get_remote_addr() const override;
private:
@@ -1303,15 +1304,15 @@ public:
bool is_readable() const override;
bool is_writable() const override;
int read(char *ptr, size_t size) override;
int write(const char *ptr, size_t size) override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
std::string get_remote_addr() const override;
const std::string &get_buffer() const;
private:
std::string buffer;
int position = 0;
size_t position = 0;
};
template <typename T>
@@ -1479,7 +1480,7 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
auto ret = false;
for (auto rp = result; rp; rp = rp->ai_next) {
const auto &ai = *rp;
if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
ret = true;
break;
}
@@ -1523,7 +1524,8 @@ inline socket_t create_client_socket(const char *host, int port,
set_nonblocking(sock, true);
auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
auto ret =
::connect(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
if (ret < 0) {
if (is_connection_error() ||
!wait_until_socket_is_ready(sock, timeout_sec, 0)) {
@@ -1597,24 +1599,67 @@ find_content_type(const std::string &path,
inline const char *status_message(int status) {
switch (status) {
case 100: return "Continue";
case 101: return "Switching Protocol";
case 102: return "Processing";
case 103: return "Early Hints";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 207: return "Multi-Status";
case 208: return "Already Reported";
case 226: return "IM Used";
case 300: return "Multiple Choice";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 306: return "unused";
case 307: return "Temporary Redirect";
case 308: return "Permanent Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Payload Too Large";
case 414: return "Request-URI Too Long";
case 414: return "URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Range Not Satisfiable";
case 417: return "Expectation Failed";
case 418: return "I'm a teapot";
case 421: return "Misdirected Request";
case 422: return "Unprocessable Entity";
case 423: return "Locked";
case 424: return "Failed Dependency";
case 425: return "Too Early";
case 426: return "Upgrade Required";
case 428: return "Precondition Required";
case 429: return "Too Many Requests";
case 431: return "Request Header Fields Too Large";
case 451: return "Unavailable For Legal Reasons";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
case 506: return "Variant Also Negotiates";
case 507: return "Insufficient Storage";
case 508: return "Loop Detected";
case 510: return "Not Extended";
case 511: return "Network Authentication Required";
default:
case 500: return "Internal Server Error";
@@ -1640,7 +1685,7 @@ inline bool compress(std::string &content) {
Z_DEFAULT_STRATEGY);
if (ret != Z_OK) { return false; }
strm.avail_in = content.size();
strm.avail_in = static_cast<decltype(strm.avail_in)>(content.size());
strm.next_in =
const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
@@ -1687,7 +1732,7 @@ public:
bool decompress(const char *data, size_t data_length, T callback) {
int ret = Z_OK;
strm.avail_in = data_length;
strm.avail_in = static_cast<decltype(strm.avail_in)>(data_length);
strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
std::array<char, 16384> buff{};
@@ -1724,13 +1769,13 @@ inline bool has_header(const Headers &headers, const char *key) {
inline const char *get_header_value(const Headers &headers, const char *key,
size_t id = 0, const char *def = nullptr) {
auto it = headers.find(key);
std::advance(it, id);
std::advance(it, static_cast<int>(id));
if (it != headers.end()) { return it->second.c_str(); }
return def;
}
inline uint64_t get_header_value_uint64(const Headers &headers, const char *key,
int def = 0) {
uint64_t def = 0) {
auto it = headers.find(key);
if (it != headers.end()) {
return std::strtoull(it->second.data(), nullptr, 10);
@@ -1784,12 +1829,12 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
uint64_t r = 0;
while (r < len) {
auto read_len = static_cast<size_t>(len - r);
auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return false; }
if (!out(buf, n)) { return false; }
if (!out(buf, static_cast<size_t>(n))) { return false; }
r += n;
r += static_cast<uint64_t>(n);
if (progress) {
if (!progress(r, len)) { return false; }
@@ -1804,9 +1849,9 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
uint64_t r = 0;
while (r < len) {
auto read_len = static_cast<size_t>(len - r);
auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return; }
r += n;
r += static_cast<uint64_t>(n);
}
}
@@ -1819,7 +1864,7 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
} else if (n == 0) {
return true;
}
if (!out(buf, n)) { return false; }
if (!out(buf, static_cast<size_t>(n))) { return false; }
}
return true;
@@ -1833,7 +1878,7 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
if (!line_reader.getline()) { return false; }
auto chunk_len = std::stoi(line_reader.ptr(), 0, 16);
auto chunk_len = std::stoul(line_reader.ptr(), 0, 16);
while (chunk_len > 0) {
if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
@@ -1846,7 +1891,7 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
if (!line_reader.getline()) { return false; }
chunk_len = std::stoi(line_reader.ptr(), 0, 16);
chunk_len = std::stoul(line_reader.ptr(), 0, 16);
}
if (chunk_len == 0) {
@@ -1918,9 +1963,11 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
}
template <typename T>
inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
auto write_len = 0;
inline ssize_t write_headers(Stream &strm, const T &info,
const Headers &headers) {
ssize_t write_len = 0;
for (const auto &x : info.headers) {
if (x.first == "EXCEPTION_WHAT") { continue; }
auto len =
strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
if (len < 0) { return len; }
@@ -2009,7 +2056,7 @@ inline bool redirect(T &cli, const Request &req, Response &res,
inline std::string encode_url(const std::string &s) {
std::string result;
for (auto i = 0; s[i]; i++) {
for (size_t i = 0; s[i]; i++) {
switch (s[i]) {
case ' ': result += "%20"; break;
case '+': result += "%2B"; break;
@@ -2024,9 +2071,9 @@ inline std::string encode_url(const std::string &s) {
if (c >= 0x80) {
result += '%';
char hex[4];
size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
assert(len == 2);
result.append(hex, len);
result.append(hex, static_cast<size_t>(len));
} else {
result += s[i];
}
@@ -2037,7 +2084,8 @@ inline std::string encode_url(const std::string &s) {
return result;
}
inline std::string decode_url(const std::string &s) {
inline std::string decode_url(const std::string &s,
bool convert_plus_to_space) {
std::string result;
for (size_t i = 0; i < s.size(); i++) {
@@ -2063,7 +2111,7 @@ inline std::string decode_url(const std::string &s) {
result += s[i];
}
}
} else if (s[i] == '+') {
} else if (convert_plus_to_space && s[i] == '+') {
result += ' ';
} else {
result += s[i];
@@ -2097,7 +2145,7 @@ inline void parse_query_text(const std::string &s, Params &params) {
val.assign(b2, e2);
}
});
params.emplace(key, decode_url(val));
params.emplace(decode_url(key, true), decode_url(val, true));
});
}
@@ -2114,8 +2162,8 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
std::smatch m;
if (std::regex_match(s, m, re_first_range)) {
auto pos = m.position(1);
auto len = m.length(1);
auto pos = static_cast<size_t>(m.position(1));
auto len = static_cast<size_t>(m.length(1));
bool all_valid_ranges = true;
split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
if (!all_valid_ranges) return;
@@ -2146,9 +2194,9 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
class MultipartFormDataParser {
public:
MultipartFormDataParser() {}
MultipartFormDataParser() = default;
void set_boundary(const std::string &boundary) { boundary_ = boundary; }
void set_boundary(std::string boundary) { boundary_ = std::move(boundary); }
bool is_valid() const { return is_valid_; }
@@ -2161,6 +2209,8 @@ public:
"^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
"\"(.*?)\")?\\s*$",
std::regex_constants::icase);
static const std::string dash_ = "--";
static const std::string crlf_ = "\r\n";
buf_.append(buf, n); // TODO: performance improvement
@@ -2300,8 +2350,6 @@ private:
file_.content_type.clear();
}
const std::string dash_ = "--";
const std::string crlf_ = "\r\n";
std::string boundary_;
std::string buf_;
@@ -2347,12 +2395,14 @@ get_range_offset_and_length(const Request &req, size_t content_length,
return std::make_pair(0, content_length);
}
auto slen = static_cast<ssize_t>(content_length);
if (r.first == -1) {
r.first = content_length - r.second;
r.second = content_length - 1;
r.first = slen - r.second;
r.second = slen - 1;
}
if (r.second == -1) { r.second = content_length - 1; }
if (r.second == -1) { r.second = slen - 1; }
return std::make_pair(r.first, r.second - r.first + 1);
}
@@ -2456,7 +2506,9 @@ get_range_offset_and_length(const Request &req, const Response &res,
size_t index) {
auto r = req.ranges[index];
if (r.second == -1) { r.second = res.content_length - 1; }
if (r.second == -1) {
r.second = static_cast<ssize_t>(res.content_length) - 1;
}
return std::make_pair(r.first, r.second - r.first + 1);
}
@@ -2611,9 +2663,13 @@ inline bool parse_www_authenticate(const httplib::Response &res,
auto beg = std::sregex_iterator(s.begin(), s.end(), re);
for (auto i = beg; i != std::sregex_iterator(); ++i) {
auto m = *i;
auto key = s.substr(m.position(1), m.length(1));
auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2))
: s.substr(m.position(3), m.length(3));
auto key = s.substr(static_cast<size_t>(m.position(1)),
static_cast<size_t>(m.length(1)));
auto val = m.length(2) > 0
? s.substr(static_cast<size_t>(m.position(2)),
static_cast<size_t>(m.length(2)))
: s.substr(static_cast<size_t>(m.position(3)),
static_cast<size_t>(m.length(3)));
auth[key] = val;
}
return true;
@@ -2630,7 +2686,7 @@ inline std::string random_string(size_t length) {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const size_t max_index = (sizeof(charset) - 1);
return charset[rand() % max_index];
return charset[static_cast<size_t>(rand()) % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
@@ -2648,7 +2704,7 @@ inline std::string Request::get_header_value(const char *key, size_t id) const {
inline size_t Request::get_header_value_count(const char *key) const {
auto r = headers.equal_range(key);
return std::distance(r.first, r.second);
return static_cast<size_t>(std::distance(r.first, r.second));
}
inline void Request::set_header(const char *key, const char *val) {
@@ -2665,14 +2721,14 @@ inline bool Request::has_param(const char *key) const {
inline std::string Request::get_param_value(const char *key, size_t id) const {
auto it = params.find(key);
std::advance(it, id);
std::advance(it, static_cast<ssize_t>(id));
if (it != params.end()) { return it->second; }
return std::string();
}
inline size_t Request::get_param_value_count(const char *key) const {
auto r = params.equal_range(key);
return std::distance(r.first, r.second);
return static_cast<size_t>(std::distance(r.first, r.second));
}
inline bool Request::is_multipart_form_data() const {
@@ -2702,7 +2758,7 @@ inline std::string Response::get_header_value(const char *key,
inline size_t Response::get_header_value_count(const char *key) const {
auto r = headers.equal_range(key);
return std::distance(r.first, r.second);
return static_cast<size_t>(std::distance(r.first, r.second));
}
inline void Response::set_header(const char *key, const char *val) {
@@ -2724,9 +2780,8 @@ inline void Response::set_content(const char *s, size_t n,
set_header("Content-Type", content_type);
}
inline void Response::set_content(const std::string &s,
const char *content_type) {
body = s;
inline void Response::set_content(std::string s, const char *content_type) {
body = std::move(s);
set_header("Content-Type", content_type);
}
@@ -2753,33 +2808,39 @@ inline void Response::set_chunked_content_provider(
}
// Rstream implementation
inline int Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); }
inline ssize_t Stream::write(const char *ptr) {
return write(ptr, strlen(ptr));
}
inline int Stream::write(const std::string &s) {
inline ssize_t Stream::write(const std::string &s) {
return write(s.data(), s.size());
}
template <typename... Args>
inline int Stream::write_format(const char *fmt, const Args &... args) {
inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
std::array<char, 2048> buf;
#if defined(_MSC_VER) && _MSC_VER < 1900
auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
#else
auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...);
auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
#endif
if (n <= 0) { return n; }
if (sn <= 0) { return sn; }
if (n >= static_cast<int>(buf.size()) - 1) {
auto n = static_cast<size_t>(sn);
if (n >= buf.size() - 1) {
std::vector<char> glowable_buf(buf.size());
while (n >= static_cast<int>(glowable_buf.size() - 1)) {
while (n >= glowable_buf.size() - 1) {
glowable_buf.resize(glowable_buf.size() * 2);
#if defined(_MSC_VER) && _MSC_VER < 1900
n = _snprintf_s(&glowable_buf[0], glowable_buf.size(),
glowable_buf.size() - 1, fmt, args...);
n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
glowable_buf.size() - 1, fmt,
args...));
#else
n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
n = static_cast<size_t>(
snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
#endif
}
return write(&glowable_buf[0], n);
@@ -2806,13 +2867,13 @@ inline bool SocketStream::is_writable() const {
return detail::select_write(sock_, 0, 0) > 0;
}
inline int SocketStream::read(char *ptr, size_t size) {
if (is_readable()) { return recv(sock_, ptr, static_cast<int>(size), 0); }
inline ssize_t SocketStream::read(char *ptr, size_t size) {
if (is_readable()) { return recv(sock_, ptr, size, 0); }
return -1;
}
inline int SocketStream::write(const char *ptr, size_t size) {
if (is_writable()) { return send(sock_, ptr, static_cast<int>(size), 0); }
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
if (is_writable()) { return send(sock_, ptr, size, 0); }
return -1;
}
@@ -2825,19 +2886,19 @@ inline bool BufferStream::is_readable() const { return true; }
inline bool BufferStream::is_writable() const { return true; }
inline int BufferStream::read(char *ptr, size_t size) {
inline ssize_t BufferStream::read(char *ptr, size_t size) {
#if defined(_MSC_VER) && _MSC_VER < 1900
int len_read = static_cast<int>(buffer._Copy_s(ptr, size, size, position));
auto len_read = buffer._Copy_s(ptr, size, size, position);
#else
int len_read = static_cast<int>(buffer.copy(ptr, size, position));
auto len_read = buffer.copy(ptr, size, position);
#endif
position += len_read;
return len_read;
position += static_cast<size_t>(len_read);
return static_cast<ssize_t>(len_read);
}
inline int BufferStream::write(const char *ptr, size_t size) {
inline ssize_t BufferStream::write(const char *ptr, size_t size) {
buffer.append(ptr, size);
return static_cast<int>(size);
return static_cast<ssize_t>(size);
}
inline std::string BufferStream::get_remote_addr() const { return ""; }
@@ -3005,7 +3066,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
req.version = std::string(m[5]);
req.method = std::string(m[1]);
req.target = std::string(m[2]);
req.path = detail::decode_url(m[3]);
req.path = detail::decode_url(m[3], false);
// Parse query text
auto len = std::distance(m[4].first, m[4].second);
@@ -3183,47 +3244,45 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
return true;
}
inline bool Server::read_content(Stream &strm, bool last_connection,
Request &req, Response &res) {
inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
MultipartFormDataMap::iterator cur;
auto ret = read_content_core(
strm, last_connection, req, res,
// Regular
[&](const char *buf, size_t n) {
if (req.body.size() + n > req.body.max_size()) { return false; }
req.body.append(buf, n);
return true;
},
// Multipart
[&](const MultipartFormData &file) {
cur = req.files.emplace(file.name, file);
return true;
},
[&](const char *buf, size_t n) {
auto &content = cur->second.content;
if (content.size() + n > content.max_size()) { return false; }
content.append(buf, n);
return true;
});
const auto &content_type = req.get_header_value("Content-Type");
if (!content_type.find("application/x-www-form-urlencoded")) {
detail::parse_query_text(req.body, req.params);
if (read_content_core(
strm, req, res,
// Regular
[&](const char *buf, size_t n) {
if (req.body.size() + n > req.body.max_size()) { return false; }
req.body.append(buf, n);
return true;
},
// Multipart
[&](const MultipartFormData &file) {
cur = req.files.emplace(file.name, file);
return true;
},
[&](const char *buf, size_t n) {
auto &content = cur->second.content;
if (content.size() + n > content.max_size()) { return false; }
content.append(buf, n);
return true;
})) {
const auto &content_type = req.get_header_value("Content-Type");
if (!content_type.find("application/x-www-form-urlencoded")) {
detail::parse_query_text(req.body, req.params);
}
return true;
}
return ret;
return false;
}
inline bool Server::read_content_with_content_receiver(
Stream &strm, bool last_connection, Request &req, Response &res,
ContentReceiver receiver, MultipartContentHeader multipart_header,
Stream &strm, Request &req, Response &res, ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) {
return read_content_core(strm, last_connection, req, res, receiver,
multipart_header, multipart_receiver);
return read_content_core(strm, req, res, receiver, multipart_header,
multipart_receiver);
}
inline bool Server::read_content_core(Stream &strm, bool last_connection,
Request &req, Response &res,
inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
ContentReceiver multipart_receiver) {
@@ -3235,10 +3294,10 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
std::string boundary;
if (!detail::parse_multipart_boundary(content_type, boundary)) {
res.status = 400;
return write_response(strm, last_connection, req, res);
return false;
}
multipart_form_data_parser.set_boundary(boundary);
multipart_form_data_parser.set_boundary(std::move(boundary));
out = [&](const char *buf, size_t n) {
return multipart_form_data_parser.parse(buf, n, multipart_receiver,
mulitpart_header);
@@ -3249,13 +3308,13 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
if (!detail::read_content(strm, req, payload_max_length_, res.status,
Progress(), out)) {
return write_response(strm, last_connection, req, res);
return false;
}
if (req.is_multipart_form_data()) {
if (!multipart_form_data_parser.is_valid()) {
res.status = 400;
return write_response(strm, last_connection, req, res);
return false;
}
}
@@ -3297,7 +3356,7 @@ inline socket_t Server::create_server_socket(const char *host, int port,
return detail::create_socket(
host, port,
[](socket_t sock, struct addrinfo &ai) -> bool {
if (::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
return false;
}
if (::listen(sock, 5)) { // Listen through 5 channels
@@ -3371,7 +3430,11 @@ inline bool Server::listen_internal() {
break;
}
#if __cplusplus > 201703L
task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
#else
task_queue->enqueue([=]() { process_and_close_socket(sock); });
#endif
}
task_queue->shutdown();
@@ -3381,8 +3444,7 @@ inline bool Server::listen_internal() {
return ret;
}
inline bool Server::routing(Request &req, Response &res, Stream &strm,
bool last_connection) {
inline bool Server::routing(Request &req, Response &res, Stream &strm) {
// File handler
bool is_head_request = req.method == "HEAD";
if ((req.method == "GET" || is_head_request) &&
@@ -3395,12 +3457,12 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
{
ContentReader reader(
[&](ContentReceiver receiver) {
return read_content_with_content_receiver(
strm, last_connection, req, res, receiver, nullptr, nullptr);
return read_content_with_content_receiver(strm, req, res, receiver,
nullptr, nullptr);
},
[&](MultipartContentHeader header, ContentReceiver receiver) {
return read_content_with_content_receiver(
strm, last_connection, req, res, nullptr, header, receiver);
return read_content_with_content_receiver(strm, req, res, nullptr,
header, receiver);
});
if (req.method == "POST") {
@@ -3422,7 +3484,7 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
}
// Read content into `req.body`
if (!read_content(strm, last_connection, req, res)) { return false; }
if (!read_content(strm, req, res)) { return false; }
}
// Regular handler
@@ -3446,14 +3508,23 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
inline bool Server::dispatch_request(Request &req, Response &res,
Handlers &handlers) {
for (const auto &x : handlers) {
const auto &pattern = x.first;
const auto &handler = x.second;
if (std::regex_match(req.path, req.matches, pattern)) {
handler(req, res);
return true;
try {
for (const auto &x : handlers) {
const auto &pattern = x.first;
const auto &handler = x.second;
if (std::regex_match(req.path, req.matches, pattern)) {
handler(req, res);
return true;
}
}
} catch (const std::exception &ex) {
res.status = 500;
res.set_header("EXCEPTION_WHAT", ex.what());
} catch (...) {
res.status = 500;
res.set_header("EXCEPTION_WHAT", "UNKNOWN");
}
return false;
}
@@ -3540,7 +3611,7 @@ Server::process_request(Stream &strm, bool last_connection,
}
// Rounting
if (routing(req, res, strm, last_connection)) {
if (routing(req, res, strm)) {
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
} else {
if (res.status == -1) { res.status = 404; }
@@ -3875,7 +3946,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
auto written_length = strm.write(d, l);
offset += written_length;
offset += static_cast<size_t>(written_length);
};
data_sink.is_writable = [&](void) { return strm.is_writable(); };
@@ -3974,7 +4045,7 @@ inline bool Client::process_request(Stream &strm, const Request &req,
}
int dummy_status;
if (!detail::read_content(strm, res, std::numeric_limits<size_t>::max(),
if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
dummy_status, req.progress, out)) {
return false;
}
@@ -3991,7 +4062,7 @@ inline bool Client::process_and_close_socket(
std::function<bool(Stream &strm, bool last_connection,
bool &connection_close)>
callback) {
request_count = std::min(request_count, keep_alive_max_count_);
request_count = (std::min)(request_count, keep_alive_max_count_);
return detail::process_and_close_socket(true, sock, request_count,
read_timeout_sec_, read_timeout_usec_,
callback);
@@ -4476,7 +4547,7 @@ inline bool SSLSocketStream::is_writable() const {
return detail::select_write(sock_, 0, 0) > 0;
}
inline int SSLSocketStream::read(char *ptr, size_t size) {
inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
if (SSL_pending(ssl_) > 0 ||
select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
return SSL_read(ssl_, ptr, static_cast<int>(size));
@@ -4484,7 +4555,7 @@ inline int SSLSocketStream::read(char *ptr, size_t size) {
return -1;
}
inline int SSLSocketStream::write(const char *ptr, size_t size) {
inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); }
return -1;
}
@@ -4744,7 +4815,9 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
name, sizeof(name));
if (name_len != -1) { return check_host_name(name, name_len); }
if (name_len != -1) {
return check_host_name(name, static_cast<size_t>(name_len));
}
}
return false;
+1 -1
View File
@@ -1,6 +1,6 @@
#CXX = clang++
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits -Wconversion
OPENSSL_DIR = /usr/local/opt/openssl
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
+14 -1
View File
@@ -38,6 +38,14 @@
// when it's fused.
#include "gtest/gtest.h"
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion"
#elif __GNUC__
#pragma gcc diagnostic push
#pragma gcc diagnostic ignored "-Wsign-conversion"
#endif
// The following lines pull in the real gtest *.cc files.
// Copyright 2005, Google Inc.
// All rights reserved.
@@ -9039,7 +9047,6 @@ void HasNewFatalFailureHelper::ReportTestPartResult(
//
// Author: wan@google.com (Zhanyong Wan)
namespace testing {
namespace internal {
@@ -9116,3 +9123,9 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
} // namespace internal
} // namespace testing
#if __clang__
#pragma clang diagnostic pop
#elif __GNUC__
#pragma gcc diagnostic pop
#endif
+15 -1
View File
@@ -59,6 +59,14 @@
#pragma warning(disable : 4996)
#endif
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-compare"
#elif __GNUC__
#pragma gcc diagnostic push
#pragma gcc diagnostic ignored "-Wsign-compare"
#endif
// Copyright 2005, Google Inc.
// All rights reserved.
//
@@ -7311,7 +7319,7 @@ inline const char* SkipComma(const char* str) {
// the entire string if it contains no comma.
inline String GetPrefixUntilComma(const char* str) {
const char* comma = strchr(str, ',');
return comma == NULL ? String(str) : String(str, comma - str);
return comma == NULL ? String(str) : String(str, static_cast<size_t>(comma - str));
}
// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
@@ -19553,4 +19561,10 @@ bool StaticAssertTypeEq() {
#pragma warning( pop )
#endif
#if __clang__
#pragma clang diagnostic pop
#elif __GNUC__
#pragma gcc diagnostic pop
#endif
#endif // GTEST_INCLUDE_GTEST_GTEST_H_
+56 -13
View File
@@ -78,18 +78,18 @@ TEST(ParseQueryTest, ParseQueryString) {
}
TEST(ParamsToQueryTest, ConvertParamsToQuery) {
Params dic;
Params dic;
EXPECT_EQ(detail::params_to_query_str(dic), "");
EXPECT_EQ(detail::params_to_query_str(dic), "");
dic.emplace("key1", "val1");
dic.emplace("key1", "val1");
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1");
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1");
dic.emplace("key2", "val2");
dic.emplace("key3", "val3");
dic.emplace("key2", "val2");
dic.emplace("key3", "val3");
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1&key2=val2&key3=val3");
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1&key2=val2&key3=val3");
}
TEST(GetHeaderValueTest, DefaultValue) {
@@ -668,7 +668,8 @@ TEST(Server, BindAndListenSeparately) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
TEST(SSLServer, BindAndListenSeparately) {
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, CLIENT_CA_CERT_DIR);
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
CLIENT_CA_CERT_DIR);
int port = svr.bind_to_any_port("0.0.0.0");
ASSERT_TRUE(svr.is_valid());
ASSERT_TRUE(port > 0);
@@ -710,6 +711,12 @@ protected:
[&](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
})
.Get("/a\\+\\+b",
[&](const Request &req, Response &res) {
ASSERT_TRUE(req.has_param("a +b"));
auto val = req.get_param_value("a +b");
res.set_content(val, "text/plain");
})
.Get("/", [&](const Request & /*req*/,
Response &res) { res.set_redirect("/hi"); })
.Post("/person",
@@ -1459,6 +1466,13 @@ TEST_F(ServerTest, EndWithPercentCharacterInQuery) {
EXPECT_EQ(404, res->status);
}
TEST_F(ServerTest, PlusSignEncoding) {
auto res = cli_.Get("/a+%2Bb?a %2bb=a %2Bb");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
EXPECT_EQ("a +b", res->body);
}
TEST_F(ServerTest, MultipartFormData) {
MultipartFormDataItems items = {
{"text1", "text default", "", ""},
@@ -1727,7 +1741,8 @@ TEST_F(ServerTest, PutContentWithDeflate) {
httplib::Headers headers;
headers.emplace("Content-Encoding", "deflate");
// PUT in deflate format:
auto res = cli_.Put("/put", headers, "\170\234\013\010\015\001\0\001\361\0\372", "text/plain");
auto res = cli_.Put("/put", headers,
"\170\234\013\010\015\001\0\001\361\0\372", "text/plain");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(200, res->status);
@@ -1858,7 +1873,7 @@ TEST_F(ServerTest, KeepAlive) {
ASSERT_TRUE(ret == true);
ASSERT_TRUE(requests.size() == responses.size());
for (int i = 0; i < 3; i++) {
for (size_t i = 0; i < 3; i++) {
auto &res = responses[i];
EXPECT_EQ(200, res.status);
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
@@ -2050,7 +2065,7 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
}
// Sends a raw request and verifies that there isn't a crash or exception.
static void test_raw_request(const std::string& req) {
static void test_raw_request(const std::string &req) {
Server svr;
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
res.set_content("ok", "text/plain");
@@ -2129,7 +2144,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
res.set_header("Cache-Control", "no-cache");
res.set_chunked_content_provider([](size_t offset, const DataSink &sink) {
char buffer[27];
int size = sprintf(buffer, "data:%ld\n\n", offset);
auto size = static_cast<size_t>(sprintf(buffer, "data:%ld\n\n", offset));
sink.write(buffer, size);
std::this_thread::sleep_for(std::chrono::seconds(1));
});
@@ -2205,6 +2220,34 @@ TEST(MountTest, Unmount) {
ASSERT_FALSE(svr.is_running());
}
TEST(ExceptionTest, ThrowExceptionInHandler) {
Server svr;
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
throw std::runtime_error("exception...");
res.set_content("Hello World!", "text/plain");
});
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("/hi");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(500, res->status);
ASSERT_FALSE(res->has_header("EXCEPTION_WHAT"));
svr.stop();
listen_thread.join();
ASSERT_FALSE(svr.is_running());
}
class ServerTestWithAI_PASSIVE : public ::testing::Test {
protected:
ServerTestWithAI_PASSIVE()
@@ -2389,7 +2432,7 @@ TEST(SSLClientServerTest, ClientCertPresent) {
char name[BUFSIZ];
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
name, sizeof(name));
common_name.assign(name, name_len);
common_name.assign(name, static_cast<size_t>(name_len));
}
EXPECT_EQ("Common Name", common_name);