summaryrefslogtreecommitdiff
path: root/externals/httplib/httplib.h
diff options
context:
space:
mode:
authorGravatar Lioncash2020-10-13 08:10:50 -0400
committerGravatar Lioncash2020-10-13 13:16:49 -0400
commit39c8d18feba8eafcd43fbb55e73ae150a1947aad (patch)
tree9565ff464bbb9e5a0aa66e6e310098314e88d019 /externals/httplib/httplib.h
parentMerge pull request #3929 from FearlessTobi/ticket-keys (diff)
downloadyuzu-39c8d18feba8eafcd43fbb55e73ae150a1947aad.tar.gz
yuzu-39c8d18feba8eafcd43fbb55e73ae150a1947aad.tar.xz
yuzu-39c8d18feba8eafcd43fbb55e73ae150a1947aad.zip
core/CMakeLists: Make some warnings errors
Makes our error coverage a little more consistent across the board by applying it to Linux side of things as well. This also makes it more consistent with the warning settings in other libraries in the project. This also updates httplib to 0.7.9, as there are several warning cleanups made that allow us to enable several warnings as errors.
Diffstat (limited to '')
-rw-r--r--externals/httplib/httplib.h4451
1 files changed, 3077 insertions, 1374 deletions
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index e03842e6d..5139b7f05 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -16,14 +16,18 @@
16#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 16#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
17#endif 17#endif
18 18
19#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
20#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
21#endif
22
23#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT 19#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
24#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 20#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
25#endif 21#endif
26 22
23#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
24#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
25#endif
26
27#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
28#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
29#endif
30
27#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND 31#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
28#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 32#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
29#endif 33#endif
@@ -32,6 +36,26 @@
32#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 36#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
33#endif 37#endif
34 38
39#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
40#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
41#endif
42
43#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
44#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
45#endif
46
47#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
48#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
49#endif
50
51#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
52#ifdef _WIN32
53#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
54#else
55#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
56#endif
57#endif
58
35#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 59#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
36#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 60#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
37#endif 61#endif
@@ -41,16 +65,26 @@
41#endif 65#endif
42 66
43#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH 67#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
44#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max()) 68#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
69#endif
70
71#ifndef CPPHTTPLIB_TCP_NODELAY
72#define CPPHTTPLIB_TCP_NODELAY false
45#endif 73#endif
46 74
47#ifndef CPPHTTPLIB_RECV_BUFSIZ 75#ifndef CPPHTTPLIB_RECV_BUFSIZ
48#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) 76#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
49#endif 77#endif
50 78
79#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
80#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
81#endif
82
51#ifndef CPPHTTPLIB_THREAD_POOL_COUNT 83#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
52#define CPPHTTPLIB_THREAD_POOL_COUNT \ 84#define CPPHTTPLIB_THREAD_POOL_COUNT \
53 (std::max(1u, std::thread::hardware_concurrency() - 1)) 85 ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
86 ? std::thread::hardware_concurrency() - 1 \
87 : 0))
54#endif 88#endif
55 89
56/* 90/*
@@ -92,6 +126,8 @@ using ssize_t = int;
92 126
93#include <io.h> 127#include <io.h>
94#include <winsock2.h> 128#include <winsock2.h>
129
130#include <wincrypt.h>
95#include <ws2tcpip.h> 131#include <ws2tcpip.h>
96 132
97#ifndef WSA_FLAG_NO_HANDLE_INHERIT 133#ifndef WSA_FLAG_NO_HANDLE_INHERIT
@@ -100,6 +136,8 @@ using ssize_t = int;
100 136
101#ifdef _MSC_VER 137#ifdef _MSC_VER
102#pragma comment(lib, "ws2_32.lib") 138#pragma comment(lib, "ws2_32.lib")
139#pragma comment(lib, "crypt32.lib")
140#pragma comment(lib, "cryptui.lib")
103#endif 141#endif
104 142
105#ifndef strcasecmp 143#ifndef strcasecmp
@@ -118,6 +156,10 @@ using socket_t = SOCKET;
118#include <ifaddrs.h> 156#include <ifaddrs.h>
119#include <netdb.h> 157#include <netdb.h>
120#include <netinet/in.h> 158#include <netinet/in.h>
159#ifdef __linux__
160#include <resolv.h>
161#endif
162#include <netinet/tcp.h>
121#ifdef CPPHTTPLIB_USE_POLL 163#ifdef CPPHTTPLIB_USE_POLL
122#include <poll.h> 164#include <poll.h>
123#endif 165#endif
@@ -134,17 +176,21 @@ using socket_t = int;
134#include <array> 176#include <array>
135#include <atomic> 177#include <atomic>
136#include <cassert> 178#include <cassert>
179#include <cctype>
180#include <climits>
137#include <condition_variable> 181#include <condition_variable>
138#include <errno.h> 182#include <errno.h>
139#include <fcntl.h> 183#include <fcntl.h>
140#include <fstream> 184#include <fstream>
141#include <functional> 185#include <functional>
186#include <iostream>
142#include <list> 187#include <list>
143#include <map> 188#include <map>
144#include <memory> 189#include <memory>
145#include <mutex> 190#include <mutex>
146#include <random> 191#include <random>
147#include <regex> 192#include <regex>
193#include <sstream>
148#include <string> 194#include <string>
149#include <sys/stat.h> 195#include <sys/stat.h>
150#include <thread> 196#include <thread>
@@ -155,12 +201,17 @@ using socket_t = int;
155#include <openssl/ssl.h> 201#include <openssl/ssl.h>
156#include <openssl/x509v3.h> 202#include <openssl/x509v3.h>
157 203
204#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
205#include <openssl/applink.c>
206#endif
207
158#include <iomanip> 208#include <iomanip>
209#include <iostream>
159#include <sstream> 210#include <sstream>
160 211
161// #if OPENSSL_VERSION_NUMBER < 0x1010100fL 212#if OPENSSL_VERSION_NUMBER < 0x1010100fL
162// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported 213#error Sorry, OpenSSL versions prior to 1.1.1 are not supported
163// #endif 214#endif
164 215
165#if OPENSSL_VERSION_NUMBER < 0x10100000L 216#if OPENSSL_VERSION_NUMBER < 0x10100000L
166#include <openssl/crypto.h> 217#include <openssl/crypto.h>
@@ -174,6 +225,11 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
174#include <zlib.h> 225#include <zlib.h>
175#endif 226#endif
176 227
228#ifdef CPPHTTPLIB_BROTLI_SUPPORT
229#include <brotli/decode.h>
230#include <brotli/encode.h>
231#endif
232
177/* 233/*
178 * Declaration 234 * Declaration
179 */ 235 */
@@ -212,7 +268,8 @@ using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
212 268
213class DataSink { 269class DataSink {
214public: 270public:
215 DataSink() = default; 271 DataSink() : os(&sb_), sb_(*this) {}
272
216 DataSink(const DataSink &) = delete; 273 DataSink(const DataSink &) = delete;
217 DataSink &operator=(const DataSink &) = delete; 274 DataSink &operator=(const DataSink &) = delete;
218 DataSink(DataSink &&) = delete; 275 DataSink(DataSink &&) = delete;
@@ -221,10 +278,31 @@ public:
221 std::function<void(const char *data, size_t data_len)> write; 278 std::function<void(const char *data, size_t data_len)> write;
222 std::function<void()> done; 279 std::function<void()> done;
223 std::function<bool()> is_writable; 280 std::function<bool()> is_writable;
281 std::ostream os;
282
283private:
284 class data_sink_streambuf : public std::streambuf {
285 public:
286 explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
287
288 protected:
289 std::streamsize xsputn(const char *s, std::streamsize n) {
290 sink_.write(s, static_cast<size_t>(n));
291 return n;
292 }
293
294 private:
295 DataSink &sink_;
296 };
297
298 data_sink_streambuf sb_;
224}; 299};
225 300
226using ContentProvider = 301using ContentProvider =
227 std::function<void(size_t offset, size_t length, DataSink &sink)>; 302 std::function<bool(size_t offset, size_t length, DataSink &sink)>;
303
304using ContentProviderWithoutLength =
305 std::function<bool(size_t offset, DataSink &sink)>;
228 306
229using ContentReceiver = 307using ContentReceiver =
230 std::function<bool(const char *data, size_t data_length)>; 308 std::function<bool(const char *data, size_t data_length)>;
@@ -238,18 +316,18 @@ public:
238 using MultipartReader = std::function<bool(MultipartContentHeader header, 316 using MultipartReader = std::function<bool(MultipartContentHeader header,
239 ContentReceiver receiver)>; 317 ContentReceiver receiver)>;
240 318
241 ContentReader(Reader reader, MultipartReader muitlpart_reader) 319 ContentReader(Reader reader, MultipartReader multipart_reader)
242 : reader_(reader), muitlpart_reader_(muitlpart_reader) {} 320 : reader_(reader), multipart_reader_(multipart_reader) {}
243 321
244 bool operator()(MultipartContentHeader header, 322 bool operator()(MultipartContentHeader header,
245 ContentReceiver receiver) const { 323 ContentReceiver receiver) const {
246 return muitlpart_reader_(header, receiver); 324 return multipart_reader_(header, receiver);
247 } 325 }
248 326
249 bool operator()(ContentReceiver receiver) const { return reader_(receiver); } 327 bool operator()(ContentReceiver receiver) const { return reader_(receiver); }
250 328
251 Reader reader_; 329 Reader reader_;
252 MultipartReader muitlpart_reader_; 330 MultipartReader multipart_reader_;
253}; 331};
254 332
255using Range = std::pair<ssize_t, ssize_t>; 333using Range = std::pair<ssize_t, ssize_t>;
@@ -261,6 +339,9 @@ struct Request {
261 Headers headers; 339 Headers headers;
262 std::string body; 340 std::string body;
263 341
342 std::string remote_addr;
343 int remote_port = -1;
344
264 // for server 345 // for server
265 std::string version; 346 std::string version;
266 std::string target; 347 std::string target;
@@ -273,6 +354,8 @@ struct Request {
273 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; 354 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
274 ResponseHandler response_handler; 355 ResponseHandler response_handler;
275 ContentReceiver content_receiver; 356 ContentReceiver content_receiver;
357 size_t content_length = 0;
358 ContentProvider content_provider;
276 Progress progress; 359 Progress progress;
277 360
278#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 361#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -281,6 +364,8 @@ struct Request {
281 364
282 bool has_header(const char *key) const; 365 bool has_header(const char *key) const;
283 std::string get_header_value(const char *key, size_t id = 0) const; 366 std::string get_header_value(const char *key, size_t id = 0) const;
367 template <typename T>
368 T get_header_value(const char *key, size_t id = 0) const;
284 size_t get_header_value_count(const char *key) const; 369 size_t get_header_value_count(const char *key) const;
285 void set_header(const char *key, const char *val); 370 void set_header(const char *key, const char *val);
286 void set_header(const char *key, const std::string &val); 371 void set_header(const char *key, const std::string &val);
@@ -295,35 +380,40 @@ struct Request {
295 MultipartFormData get_file_value(const char *key) const; 380 MultipartFormData get_file_value(const char *key) const;
296 381
297 // private members... 382 // private members...
298 size_t content_length; 383 size_t authorization_count_ = 0;
299 ContentProvider content_provider;
300}; 384};
301 385
302struct Response { 386struct Response {
303 std::string version; 387 std::string version;
304 int status = -1; 388 int status = -1;
389 std::string reason;
305 Headers headers; 390 Headers headers;
306 std::string body; 391 std::string body;
307 392
308 bool has_header(const char *key) const; 393 bool has_header(const char *key) const;
309 std::string get_header_value(const char *key, size_t id = 0) const; 394 std::string get_header_value(const char *key, size_t id = 0) const;
395 template <typename T>
396 T get_header_value(const char *key, size_t id = 0) const;
310 size_t get_header_value_count(const char *key) const; 397 size_t get_header_value_count(const char *key) const;
311 void set_header(const char *key, const char *val); 398 void set_header(const char *key, const char *val);
312 void set_header(const char *key, const std::string &val); 399 void set_header(const char *key, const std::string &val);
313 400
314 void set_redirect(const char *url); 401 void set_redirect(const char *url, int status = 302);
402 void set_redirect(const std::string &url, int status = 302);
315 void set_content(const char *s, size_t n, const char *content_type); 403 void set_content(const char *s, size_t n, const char *content_type);
316 void set_content(const std::string &s, const char *content_type); 404 void set_content(std::string s, const char *content_type);
405
406 void set_content_provider(
407 size_t length, const char *content_type, ContentProvider provider,
408 const std::function<void()> &resource_releaser = nullptr);
317 409
318 void set_content_provider( 410 void set_content_provider(
319 size_t length, 411 const char *content_type, ContentProviderWithoutLength provider,
320 std::function<void(size_t offset, size_t length, DataSink &sink)> 412 const std::function<void()> &resource_releaser = nullptr);
321 provider,
322 std::function<void()> resource_releaser = [] {});
323 413
324 void set_chunked_content_provider( 414 void set_chunked_content_provider(
325 std::function<void(size_t offset, DataSink &sink)> provider, 415 const char *content_type, ContentProviderWithoutLength provider,
326 std::function<void()> resource_releaser = [] {}); 416 const std::function<void()> &resource_releaser = nullptr);
327 417
328 Response() = default; 418 Response() = default;
329 Response(const Response &) = default; 419 Response(const Response &) = default;
@@ -331,15 +421,16 @@ struct Response {
331 Response(Response &&) = default; 421 Response(Response &&) = default;
332 Response &operator=(Response &&) = default; 422 Response &operator=(Response &&) = default;
333 ~Response() { 423 ~Response() {
334 if (content_provider_resource_releaser) { 424 if (content_provider_resource_releaser_) {
335 content_provider_resource_releaser(); 425 content_provider_resource_releaser_();
336 } 426 }
337 } 427 }
338 428
339 // private members... 429 // private members...
340 size_t content_length = 0; 430 size_t content_length_ = 0;
341 ContentProvider content_provider; 431 ContentProvider content_provider_;
342 std::function<void()> content_provider_resource_releaser; 432 std::function<void()> content_provider_resource_releaser_;
433 bool is_chunked_content_provider = false;
343}; 434};
344 435
345class Stream { 436class Stream {
@@ -349,22 +440,25 @@ public:
349 virtual bool is_readable() const = 0; 440 virtual bool is_readable() const = 0;
350 virtual bool is_writable() const = 0; 441 virtual bool is_writable() const = 0;
351 442
352 virtual int read(char *ptr, size_t size) = 0; 443 virtual ssize_t read(char *ptr, size_t size) = 0;
353 virtual int write(const char *ptr, size_t size) = 0; 444 virtual ssize_t write(const char *ptr, size_t size) = 0;
354 virtual std::string get_remote_addr() const = 0; 445 virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
355 446
356 template <typename... Args> 447 template <typename... Args>
357 int write_format(const char *fmt, const Args &... args); 448 ssize_t write_format(const char *fmt, const Args &... args);
358 int write(const char *ptr); 449 ssize_t write(const char *ptr);
359 int write(const std::string &s); 450 ssize_t write(const std::string &s);
360}; 451};
361 452
362class TaskQueue { 453class TaskQueue {
363public: 454public:
364 TaskQueue() = default; 455 TaskQueue() = default;
365 virtual ~TaskQueue() = default; 456 virtual ~TaskQueue() = default;
457
366 virtual void enqueue(std::function<void()> fn) = 0; 458 virtual void enqueue(std::function<void()> fn) = 0;
367 virtual void shutdown() = 0; 459 virtual void shutdown() = 0;
460
461 virtual void on_idle(){};
368}; 462};
369 463
370class ThreadPool : public TaskQueue { 464class ThreadPool : public TaskQueue {
@@ -439,6 +533,26 @@ private:
439 533
440using Logger = std::function<void(const Request &, const Response &)>; 534using Logger = std::function<void(const Request &, const Response &)>;
441 535
536using SocketOptions = std::function<void(socket_t sock)>;
537
538inline void default_socket_options(socket_t sock) {
539 int yes = 1;
540#ifdef _WIN32
541 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
542 sizeof(yes));
543 setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
544 reinterpret_cast<char *>(&yes), sizeof(yes));
545#else
546#ifdef SO_REUSEPORT
547 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes),
548 sizeof(yes));
549#else
550 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes),
551 sizeof(yes));
552#endif
553#endif
554}
555
442class Server { 556class Server {
443public: 557public:
444 using Handler = std::function<void(const Request &, Response &)>; 558 using Handler = std::function<void(const Request &, Response &)>;
@@ -461,23 +575,30 @@ public:
461 Server &Patch(const char *pattern, Handler handler); 575 Server &Patch(const char *pattern, Handler handler);
462 Server &Patch(const char *pattern, HandlerWithContentReader handler); 576 Server &Patch(const char *pattern, HandlerWithContentReader handler);
463 Server &Delete(const char *pattern, Handler handler); 577 Server &Delete(const char *pattern, Handler handler);
578 Server &Delete(const char *pattern, HandlerWithContentReader handler);
464 Server &Options(const char *pattern, Handler handler); 579 Server &Options(const char *pattern, Handler handler);
465 580
466 [[deprecated]] bool set_base_dir(const char *dir, 581 bool set_base_dir(const char *dir, const char *mount_point = nullptr);
467 const char *mount_point = nullptr); 582 bool set_mount_point(const char *mount_point, const char *dir,
468 bool set_mount_point(const char *mount_point, const char *dir); 583 Headers headers = Headers());
469 bool remove_mount_point(const char *mount_point); 584 bool remove_mount_point(const char *mount_point);
470 void set_file_extension_and_mimetype_mapping(const char *ext, 585 void set_file_extension_and_mimetype_mapping(const char *ext,
471 const char *mime); 586 const char *mime);
472 void set_file_request_handler(Handler handler); 587 void set_file_request_handler(Handler handler);
473 588
474 void set_error_handler(Handler handler); 589 void set_error_handler(Handler handler);
590 void set_expect_100_continue_handler(Expect100ContinueHandler handler);
475 void set_logger(Logger logger); 591 void set_logger(Logger logger);
476 592
477 void set_expect_100_continue_handler(Expect100ContinueHandler handler); 593 void set_tcp_nodelay(bool on);
594 void set_socket_options(SocketOptions socket_options);
478 595
479 void set_keep_alive_max_count(size_t count); 596 void set_keep_alive_max_count(size_t count);
480 void set_read_timeout(time_t sec, time_t usec); 597 void set_keep_alive_timeout(time_t sec);
598 void set_read_timeout(time_t sec, time_t usec = 0);
599 void set_write_timeout(time_t sec, time_t usec = 0);
600 void set_idle_interval(time_t sec, time_t usec = 0);
601
481 void set_payload_max_length(size_t length); 602 void set_payload_max_length(size_t length);
482 603
483 bool bind_to_port(const char *host, int port, int socket_flags = 0); 604 bool bind_to_port(const char *host, int port, int socket_flags = 0);
@@ -492,54 +613,66 @@ public:
492 std::function<TaskQueue *(void)> new_task_queue; 613 std::function<TaskQueue *(void)> new_task_queue;
493 614
494protected: 615protected:
495 bool process_request(Stream &strm, bool last_connection, 616 bool process_request(Stream &strm, bool close_connection,
496 bool &connection_close, 617 bool &connection_closed,
497 const std::function<void(Request &)> &setup_request); 618 const std::function<void(Request &)> &setup_request);
498 619
499 size_t keep_alive_max_count_; 620 std::atomic<socket_t> svr_sock_;
500 time_t read_timeout_sec_; 621 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
501 time_t read_timeout_usec_; 622 time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
502 size_t payload_max_length_; 623 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
624 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
625 time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
626 time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
627 time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
628 time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
629 size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
503 630
504private: 631private:
505 using Handlers = std::vector<std::pair<std::regex, Handler>>; 632 using Handlers = std::vector<std::pair<std::regex, Handler>>;
506 using HandlersForContentReader = 633 using HandlersForContentReader =
507 std::vector<std::pair<std::regex, HandlerWithContentReader>>; 634 std::vector<std::pair<std::regex, HandlerWithContentReader>>;
508 635
509 socket_t create_server_socket(const char *host, int port, 636 socket_t create_server_socket(const char *host, int port, int socket_flags,
510 int socket_flags) const; 637 SocketOptions socket_options) const;
511 int bind_internal(const char *host, int port, int socket_flags); 638 int bind_internal(const char *host, int port, int socket_flags);
512 bool listen_internal(); 639 bool listen_internal();
513 640
514 bool routing(Request &req, Response &res, Stream &strm, bool last_connection); 641 bool routing(Request &req, Response &res, Stream &strm);
515 bool handle_file_request(Request &req, Response &res, bool head = false); 642 bool handle_file_request(Request &req, Response &res, bool head = false);
516 bool dispatch_request(Request &req, Response &res, Handlers &handlers); 643 bool dispatch_request(Request &req, Response &res, const Handlers &handlers);
517 bool dispatch_request_for_content_reader(Request &req, Response &res, 644 bool
518 ContentReader content_reader, 645 dispatch_request_for_content_reader(Request &req, Response &res,
519 HandlersForContentReader &handlers); 646 ContentReader content_reader,
647 const HandlersForContentReader &handlers);
520 648
521 bool parse_request_line(const char *s, Request &req); 649 bool parse_request_line(const char *s, Request &req);
522 bool write_response(Stream &strm, bool last_connection, const Request &req, 650 bool write_response(Stream &strm, bool close_connection, const Request &req,
523 Response &res); 651 Response &res);
524 bool write_content_with_provider(Stream &strm, const Request &req, 652 bool write_content_with_provider(Stream &strm, const Request &req,
525 Response &res, const std::string &boundary, 653 Response &res, const std::string &boundary,
526 const std::string &content_type); 654 const std::string &content_type);
527 bool read_content(Stream &strm, bool last_connection, Request &req, 655 bool read_content(Stream &strm, Request &req, Response &res);
528 Response &res); 656 bool
529 bool read_content_with_content_receiver( 657 read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
530 Stream &strm, bool last_connection, Request &req, Response &res, 658 ContentReceiver receiver,
531 ContentReceiver receiver, MultipartContentHeader multipart_header, 659 MultipartContentHeader multipart_header,
532 ContentReceiver multipart_receiver); 660 ContentReceiver multipart_receiver);
533 bool read_content_core(Stream &strm, bool last_connection, Request &req, 661 bool read_content_core(Stream &strm, Request &req, Response &res,
534 Response &res, ContentReceiver receiver, 662 ContentReceiver receiver,
535 MultipartContentHeader mulitpart_header, 663 MultipartContentHeader mulitpart_header,
536 ContentReceiver multipart_receiver); 664 ContentReceiver multipart_receiver);
537 665
538 virtual bool process_and_close_socket(socket_t sock); 666 virtual bool process_and_close_socket(socket_t sock);
667
668 struct MountPointEntry {
669 std::string mount_point;
670 std::string base_dir;
671 Headers headers;
672 };
673 std::vector<MountPointEntry> base_dirs_;
539 674
540 std::atomic<bool> is_running_; 675 std::atomic<bool> is_running_;
541 std::atomic<socket_t> svr_sock_;
542 std::vector<std::pair<std::string, std::string>> base_dirs_;
543 std::map<std::string, std::string> file_extension_and_mimetype_map_; 676 std::map<std::string, std::string> file_extension_and_mimetype_map_;
544 Handler file_request_handler_; 677 Handler file_request_handler_;
545 Handlers get_handlers_; 678 Handlers get_handlers_;
@@ -550,293 +683,427 @@ private:
550 Handlers patch_handlers_; 683 Handlers patch_handlers_;
551 HandlersForContentReader patch_handlers_for_content_reader_; 684 HandlersForContentReader patch_handlers_for_content_reader_;
552 Handlers delete_handlers_; 685 Handlers delete_handlers_;
686 HandlersForContentReader delete_handlers_for_content_reader_;
553 Handlers options_handlers_; 687 Handlers options_handlers_;
554 Handler error_handler_; 688 Handler error_handler_;
555 Logger logger_; 689 Logger logger_;
556 Expect100ContinueHandler expect_100_continue_handler_; 690 Expect100ContinueHandler expect_100_continue_handler_;
557};
558
559class Client {
560public:
561 explicit Client(const std::string &host, int port = 80,
562 const std::string &client_cert_path = std::string(),
563 const std::string &client_key_path = std::string());
564
565 virtual ~Client();
566
567 virtual bool is_valid() const;
568
569 std::shared_ptr<Response> Get(const char *path);
570
571 std::shared_ptr<Response> Get(const char *path, const Headers &headers);
572
573 std::shared_ptr<Response> Get(const char *path, Progress progress);
574
575 std::shared_ptr<Response> Get(const char *path, const Headers &headers,
576 Progress progress);
577
578 std::shared_ptr<Response> Get(const char *path,
579 ContentReceiver content_receiver);
580
581 std::shared_ptr<Response> Get(const char *path, const Headers &headers,
582 ContentReceiver content_receiver);
583
584 std::shared_ptr<Response>
585 Get(const char *path, ContentReceiver content_receiver, Progress progress);
586
587 std::shared_ptr<Response> Get(const char *path, const Headers &headers,
588 ContentReceiver content_receiver,
589 Progress progress);
590 691
591 std::shared_ptr<Response> Get(const char *path, const Headers &headers, 692 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
592 ResponseHandler response_handler, 693 SocketOptions socket_options_ = default_socket_options;
593 ContentReceiver content_receiver); 694};
594
595 std::shared_ptr<Response> Get(const char *path, const Headers &headers,
596 ResponseHandler response_handler,
597 ContentReceiver content_receiver,
598 Progress progress);
599
600 std::shared_ptr<Response> Head(const char *path);
601
602 std::shared_ptr<Response> Head(const char *path, const Headers &headers);
603
604 std::shared_ptr<Response> Post(const char *path, const std::string &body,
605 const char *content_type);
606
607 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
608 const std::string &body,
609 const char *content_type);
610
611 std::shared_ptr<Response> Post(const char *path, size_t content_length,
612 ContentProvider content_provider,
613 const char *content_type);
614
615 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
616 size_t content_length,
617 ContentProvider content_provider,
618 const char *content_type);
619
620 std::shared_ptr<Response> Post(const char *path, const Params &params);
621
622 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
623 const Params &params);
624
625 std::shared_ptr<Response> Post(const char *path,
626 const MultipartFormDataItems &items);
627
628 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
629 const MultipartFormDataItems &items);
630
631 std::shared_ptr<Response> Put(const char *path, const std::string &body,
632 const char *content_type);
633
634 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
635 const std::string &body,
636 const char *content_type);
637
638 std::shared_ptr<Response> Put(const char *path, size_t content_length,
639 ContentProvider content_provider,
640 const char *content_type);
641
642 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
643 size_t content_length,
644 ContentProvider content_provider,
645 const char *content_type);
646
647 std::shared_ptr<Response> Put(const char *path, const Params &params);
648
649 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
650 const Params &params);
651
652 std::shared_ptr<Response> Patch(const char *path, const std::string &body,
653 const char *content_type);
654 695
655 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, 696enum Error {
656 const std::string &body, 697 Success = 0,
657 const char *content_type); 698 Unknown,
699 Connection,
700 BindIPAddress,
701 Read,
702 Write,
703 ExceedRedirectCount,
704 Canceled,
705 SSLConnection,
706 SSLLoadingCerts,
707 SSLServerVerification
708};
658 709
659 std::shared_ptr<Response> Patch(const char *path, size_t content_length, 710class Result {
660 ContentProvider content_provider, 711public:
661 const char *content_type); 712 Result(const std::shared_ptr<Response> &res, Error err)
713 : res_(res), err_(err) {}
714 operator bool() const { return res_ != nullptr; }
715 bool operator==(std::nullptr_t) const { return res_ == nullptr; }
716 bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
717 const Response &value() const { return *res_; }
718 const Response &operator*() const { return *res_; }
719 const Response *operator->() const { return res_.get(); }
720 Error error() const { return err_; }
662 721
663 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, 722private:
664 size_t content_length, 723 std::shared_ptr<Response> res_;
665 ContentProvider content_provider, 724 Error err_;
666 const char *content_type); 725};
667 726
668 std::shared_ptr<Response> Delete(const char *path); 727class ClientImpl {
728public:
729 explicit ClientImpl(const std::string &host);
669 730
670 std::shared_ptr<Response> Delete(const char *path, const std::string &body, 731 explicit ClientImpl(const std::string &host, int port);
671 const char *content_type);
672 732
673 std::shared_ptr<Response> Delete(const char *path, const Headers &headers); 733 explicit ClientImpl(const std::string &host, int port,
734 const std::string &client_cert_path,
735 const std::string &client_key_path);
674 736
675 std::shared_ptr<Response> Delete(const char *path, const Headers &headers, 737 virtual ~ClientImpl();
676 const std::string &body,
677 const char *content_type);
678 738
679 std::shared_ptr<Response> Options(const char *path); 739 virtual bool is_valid() const;
680 740
681 std::shared_ptr<Response> Options(const char *path, const Headers &headers); 741 Result Get(const char *path);
742 Result Get(const char *path, const Headers &headers);
743 Result Get(const char *path, Progress progress);
744 Result Get(const char *path, const Headers &headers, Progress progress);
745 Result Get(const char *path, ContentReceiver content_receiver);
746 Result Get(const char *path, const Headers &headers,
747 ContentReceiver content_receiver);
748 Result Get(const char *path, ContentReceiver content_receiver,
749 Progress progress);
750 Result Get(const char *path, const Headers &headers,
751 ContentReceiver content_receiver, Progress progress);
752 Result Get(const char *path, ResponseHandler response_handler,
753 ContentReceiver content_receiver);
754 Result Get(const char *path, const Headers &headers,
755 ResponseHandler response_handler,
756 ContentReceiver content_receiver);
757 Result Get(const char *path, ResponseHandler response_handler,
758 ContentReceiver content_receiver, Progress progress);
759 Result Get(const char *path, const Headers &headers,
760 ResponseHandler response_handler, ContentReceiver content_receiver,
761 Progress progress);
762
763 Result Head(const char *path);
764 Result Head(const char *path, const Headers &headers);
765
766 Result Post(const char *path);
767 Result Post(const char *path, const std::string &body,
768 const char *content_type);
769 Result Post(const char *path, const Headers &headers, const std::string &body,
770 const char *content_type);
771 Result Post(const char *path, size_t content_length,
772 ContentProvider content_provider, const char *content_type);
773 Result Post(const char *path, const Headers &headers, size_t content_length,
774 ContentProvider content_provider, const char *content_type);
775 Result Post(const char *path, const Params &params);
776 Result Post(const char *path, const Headers &headers, const Params &params);
777 Result Post(const char *path, const MultipartFormDataItems &items);
778 Result Post(const char *path, const Headers &headers,
779 const MultipartFormDataItems &items);
780
781 Result Put(const char *path);
782 Result Put(const char *path, const std::string &body,
783 const char *content_type);
784 Result Put(const char *path, const Headers &headers, const std::string &body,
785 const char *content_type);
786 Result Put(const char *path, size_t content_length,
787 ContentProvider content_provider, const char *content_type);
788 Result Put(const char *path, const Headers &headers, size_t content_length,
789 ContentProvider content_provider, const char *content_type);
790 Result Put(const char *path, const Params &params);
791 Result Put(const char *path, const Headers &headers, const Params &params);
792
793 Result Patch(const char *path, const std::string &body,
794 const char *content_type);
795 Result Patch(const char *path, const Headers &headers,
796 const std::string &body, const char *content_type);
797 Result Patch(const char *path, size_t content_length,
798 ContentProvider content_provider, const char *content_type);
799 Result Patch(const char *path, const Headers &headers, size_t content_length,
800 ContentProvider content_provider, const char *content_type);
801
802 Result Delete(const char *path);
803 Result Delete(const char *path, const std::string &body,
804 const char *content_type);
805 Result Delete(const char *path, const Headers &headers);
806 Result Delete(const char *path, const Headers &headers,
807 const std::string &body, const char *content_type);
808
809 Result Options(const char *path);
810 Result Options(const char *path, const Headers &headers);
682 811
683 bool send(const Request &req, Response &res); 812 bool send(const Request &req, Response &res);
684 813
685 bool send(const std::vector<Request> &requests, 814 size_t is_socket_open() const;
686 std::vector<Response> &responses);
687 815
688 void set_timeout_sec(time_t timeout_sec); 816 void stop();
689 817
690 void set_read_timeout(time_t sec, time_t usec); 818 void set_default_headers(Headers headers);
691 819
692 void set_keep_alive_max_count(size_t count); 820 void set_tcp_nodelay(bool on);
821 void set_socket_options(SocketOptions socket_options);
693 822
694 void set_basic_auth(const char *username, const char *password); 823 void set_connection_timeout(time_t sec, time_t usec = 0);
824 void set_read_timeout(time_t sec, time_t usec = 0);
825 void set_write_timeout(time_t sec, time_t usec = 0);
695 826
827 void set_basic_auth(const char *username, const char *password);
828 void set_bearer_token_auth(const char *token);
696#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 829#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
697 void set_digest_auth(const char *username, const char *password); 830 void set_digest_auth(const char *username, const char *password);
698#endif 831#endif
699 832
833 void set_keep_alive(bool on);
700 void set_follow_location(bool on); 834 void set_follow_location(bool on);
701 835
702 void set_compress(bool on); 836 void set_compress(bool on);
703 837
838 void set_decompress(bool on);
839
704 void set_interface(const char *intf); 840 void set_interface(const char *intf);
705 841
706 void set_proxy(const char *host, int port); 842 void set_proxy(const char *host, int port);
707
708 void set_proxy_basic_auth(const char *username, const char *password); 843 void set_proxy_basic_auth(const char *username, const char *password);
709 844 void set_proxy_bearer_token_auth(const char *token);
710#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 845#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
711 void set_proxy_digest_auth(const char *username, const char *password); 846 void set_proxy_digest_auth(const char *username, const char *password);
712#endif 847#endif
713 848
849#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
850 void enable_server_certificate_verification(bool enabled);
851#endif
852
714 void set_logger(Logger logger); 853 void set_logger(Logger logger);
715 854
716protected: 855protected:
856 struct Socket {
857 socket_t sock = INVALID_SOCKET;
858#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
859 SSL *ssl = nullptr;
860#endif
861
862 bool is_open() const { return sock != INVALID_SOCKET; }
863 };
864
865 virtual bool create_and_connect_socket(Socket &socket);
866 virtual void close_socket(Socket &socket, bool process_socket_ret);
867
717 bool process_request(Stream &strm, const Request &req, Response &res, 868 bool process_request(Stream &strm, const Request &req, Response &res,
718 bool last_connection, bool &connection_close); 869 bool close_connection);
719 870
871 Error get_last_error() const;
872
873 void copy_settings(const ClientImpl &rhs);
874
875 // Error state
876 mutable Error error_ = Error::Success;
877
878 // Socket endoint information
720 const std::string host_; 879 const std::string host_;
721 const int port_; 880 const int port_;
722 const std::string host_and_port_; 881 const std::string host_and_port_;
723 882
883 // Current open socket
884 Socket socket_;
885 mutable std::mutex socket_mutex_;
886 std::recursive_mutex request_mutex_;
887
888 // Default headers
889 Headers default_headers_;
890
724 // Settings 891 // Settings
725 std::string client_cert_path_; 892 std::string client_cert_path_;
726 std::string client_key_path_; 893 std::string client_key_path_;
727 894
728 time_t timeout_sec_ = 300; 895 time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
896 time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
729 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; 897 time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
730 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; 898 time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
731 899 time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
732 size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; 900 time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
733 901
734 std::string basic_auth_username_; 902 std::string basic_auth_username_;
735 std::string basic_auth_password_; 903 std::string basic_auth_password_;
904 std::string bearer_token_auth_token_;
736#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 905#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
737 std::string digest_auth_username_; 906 std::string digest_auth_username_;
738 std::string digest_auth_password_; 907 std::string digest_auth_password_;
739#endif 908#endif
740 909
910 bool keep_alive_ = false;
741 bool follow_location_ = false; 911 bool follow_location_ = false;
742 912
913 bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
914 SocketOptions socket_options_ = nullptr;
915
743 bool compress_ = false; 916 bool compress_ = false;
917 bool decompress_ = true;
744 918
745 std::string interface_; 919 std::string interface_;
746 920
747 std::string proxy_host_; 921 std::string proxy_host_;
748 int proxy_port_; 922 int proxy_port_ = -1;
749 923
750 std::string proxy_basic_auth_username_; 924 std::string proxy_basic_auth_username_;
751 std::string proxy_basic_auth_password_; 925 std::string proxy_basic_auth_password_;
926 std::string proxy_bearer_token_auth_token_;
752#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 927#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
753 std::string proxy_digest_auth_username_; 928 std::string proxy_digest_auth_username_;
754 std::string proxy_digest_auth_password_; 929 std::string proxy_digest_auth_password_;
755#endif 930#endif
756 931
757 Logger logger_;
758
759 void copy_settings(const Client &rhs) {
760 client_cert_path_ = rhs.client_cert_path_;
761 client_key_path_ = rhs.client_key_path_;
762 timeout_sec_ = rhs.timeout_sec_;
763 read_timeout_sec_ = rhs.read_timeout_sec_;
764 read_timeout_usec_ = rhs.read_timeout_usec_;
765 keep_alive_max_count_ = rhs.keep_alive_max_count_;
766 basic_auth_username_ = rhs.basic_auth_username_;
767 basic_auth_password_ = rhs.basic_auth_password_;
768#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 932#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
769 digest_auth_username_ = rhs.digest_auth_username_; 933 bool server_certificate_verification_ = true;
770 digest_auth_password_ = rhs.digest_auth_password_;
771#endif 934#endif
772 follow_location_ = rhs.follow_location_; 935
773 compress_ = rhs.compress_; 936 Logger logger_;
774 interface_ = rhs.interface_;
775 proxy_host_ = rhs.proxy_host_;
776 proxy_port_ = rhs.proxy_port_;
777 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
778 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
779#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
780 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
781 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
782#endif
783 logger_ = rhs.logger_;
784 }
785 937
786private: 938private:
787 socket_t create_client_socket() const; 939 socket_t create_client_socket() const;
788 bool read_response_line(Stream &strm, Response &res); 940 bool read_response_line(Stream &strm, Response &res);
789 bool write_request(Stream &strm, const Request &req, bool last_connection); 941 bool write_request(Stream &strm, const Request &req, bool close_connection);
790 bool redirect(const Request &req, Response &res); 942 bool redirect(const Request &req, Response &res);
791 bool handle_request(Stream &strm, const Request &req, Response &res, 943 bool handle_request(Stream &strm, const Request &req, Response &res,
792 bool last_connection, bool &connection_close); 944 bool close_connection);
793#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 945 void stop_core();
794 bool connect(socket_t sock, Response &res, bool &error);
795#endif
796
797 std::shared_ptr<Response> send_with_content_provider( 946 std::shared_ptr<Response> send_with_content_provider(
798 const char *method, const char *path, const Headers &headers, 947 const char *method, const char *path, const Headers &headers,
799 const std::string &body, size_t content_length, 948 const std::string &body, size_t content_length,
800 ContentProvider content_provider, const char *content_type); 949 ContentProvider content_provider, const char *content_type);
801 950
802 virtual bool process_and_close_socket( 951 virtual bool process_socket(Socket &socket,
803 socket_t sock, size_t request_count, 952 std::function<bool(Stream &strm)> callback);
804 std::function<bool(Stream &strm, bool last_connection,
805 bool &connection_close)>
806 callback);
807
808 virtual bool is_ssl() const; 953 virtual bool is_ssl() const;
809}; 954};
810 955
811inline void Get(std::vector<Request> &requests, const char *path, 956class Client {
812 const Headers &headers) { 957public:
813 Request req; 958 // Universal interface
814 req.method = "GET"; 959 explicit Client(const char *scheme_host_port);
815 req.path = path; 960
816 req.headers = headers; 961 explicit Client(const char *scheme_host_port,
817 requests.emplace_back(std::move(req)); 962 const std::string &client_cert_path,
818} 963 const std::string &client_key_path);
964
965 // HTTP only interface
966 explicit Client(const std::string &host, int port);
967
968 explicit Client(const std::string &host, int port,
969 const std::string &client_cert_path,
970 const std::string &client_key_path);
971
972 ~Client();
973
974 bool is_valid() const;
975
976 Result Get(const char *path);
977 Result Get(const char *path, const Headers &headers);
978 Result Get(const char *path, Progress progress);
979 Result Get(const char *path, const Headers &headers, Progress progress);
980 Result Get(const char *path, ContentReceiver content_receiver);
981 Result Get(const char *path, const Headers &headers,
982 ContentReceiver content_receiver);
983 Result Get(const char *path, ContentReceiver content_receiver,
984 Progress progress);
985 Result Get(const char *path, const Headers &headers,
986 ContentReceiver content_receiver, Progress progress);
987 Result Get(const char *path, ResponseHandler response_handler,
988 ContentReceiver content_receiver);
989 Result Get(const char *path, const Headers &headers,
990 ResponseHandler response_handler,
991 ContentReceiver content_receiver);
992 Result Get(const char *path, const Headers &headers,
993 ResponseHandler response_handler, ContentReceiver content_receiver,
994 Progress progress);
995 Result Get(const char *path, ResponseHandler response_handler,
996 ContentReceiver content_receiver, Progress progress);
997
998 Result Head(const char *path);
999 Result Head(const char *path, const Headers &headers);
1000
1001 Result Post(const char *path);
1002 Result Post(const char *path, const std::string &body,
1003 const char *content_type);
1004 Result Post(const char *path, const Headers &headers, const std::string &body,
1005 const char *content_type);
1006 Result Post(const char *path, size_t content_length,
1007 ContentProvider content_provider, const char *content_type);
1008 Result Post(const char *path, const Headers &headers, size_t content_length,
1009 ContentProvider content_provider, const char *content_type);
1010 Result Post(const char *path, const Params &params);
1011 Result Post(const char *path, const Headers &headers, const Params &params);
1012 Result Post(const char *path, const MultipartFormDataItems &items);
1013 Result Post(const char *path, const Headers &headers,
1014 const MultipartFormDataItems &items);
1015 Result Put(const char *path);
1016 Result Put(const char *path, const std::string &body,
1017 const char *content_type);
1018 Result Put(const char *path, const Headers &headers, const std::string &body,
1019 const char *content_type);
1020 Result Put(const char *path, size_t content_length,
1021 ContentProvider content_provider, const char *content_type);
1022 Result Put(const char *path, const Headers &headers, size_t content_length,
1023 ContentProvider content_provider, const char *content_type);
1024 Result Put(const char *path, const Params &params);
1025 Result Put(const char *path, const Headers &headers, const Params &params);
1026 Result Patch(const char *path, const std::string &body,
1027 const char *content_type);
1028 Result Patch(const char *path, const Headers &headers,
1029 const std::string &body, const char *content_type);
1030 Result Patch(const char *path, size_t content_length,
1031 ContentProvider content_provider, const char *content_type);
1032 Result Patch(const char *path, const Headers &headers, size_t content_length,
1033 ContentProvider content_provider, const char *content_type);
1034
1035 Result Delete(const char *path);
1036 Result Delete(const char *path, const std::string &body,
1037 const char *content_type);
1038 Result Delete(const char *path, const Headers &headers);
1039 Result Delete(const char *path, const Headers &headers,
1040 const std::string &body, const char *content_type);
1041
1042 Result Options(const char *path);
1043 Result Options(const char *path, const Headers &headers);
819 1044
820inline void Get(std::vector<Request> &requests, const char *path) { 1045 bool send(const Request &req, Response &res);
821 Get(requests, path, Headers());
822}
823 1046
824inline void Post(std::vector<Request> &requests, const char *path, 1047 size_t is_socket_open() const;
825 const Headers &headers, const std::string &body,
826 const char *content_type) {
827 Request req;
828 req.method = "POST";
829 req.path = path;
830 req.headers = headers;
831 req.headers.emplace("Content-Type", content_type);
832 req.body = body;
833 requests.emplace_back(std::move(req));
834}
835 1048
836inline void Post(std::vector<Request> &requests, const char *path, 1049 void stop();
837 const std::string &body, const char *content_type) { 1050
838 Post(requests, path, Headers(), body, content_type); 1051 void set_default_headers(Headers headers);
839} 1052
1053 void set_tcp_nodelay(bool on);
1054 void set_socket_options(SocketOptions socket_options);
1055
1056 void set_connection_timeout(time_t sec, time_t usec = 0);
1057 void set_read_timeout(time_t sec, time_t usec = 0);
1058 void set_write_timeout(time_t sec, time_t usec = 0);
1059
1060 void set_basic_auth(const char *username, const char *password);
1061 void set_bearer_token_auth(const char *token);
1062#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1063 void set_digest_auth(const char *username, const char *password);
1064#endif
1065
1066 void set_keep_alive(bool on);
1067 void set_follow_location(bool on);
1068
1069 void set_compress(bool on);
1070
1071 void set_decompress(bool on);
1072
1073 void set_interface(const char *intf);
1074
1075 void set_proxy(const char *host, int port);
1076 void set_proxy_basic_auth(const char *username, const char *password);
1077 void set_proxy_bearer_token_auth(const char *token);
1078#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1079 void set_proxy_digest_auth(const char *username, const char *password);
1080#endif
1081
1082#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1083 void enable_server_certificate_verification(bool enabled);
1084#endif
1085
1086 void set_logger(Logger logger);
1087
1088 // SSL
1089#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1090 void set_ca_cert_path(const char *ca_cert_file_path,
1091 const char *ca_cert_dir_path = nullptr);
1092
1093 void set_ca_cert_store(X509_STORE *ca_cert_store);
1094
1095 long get_openssl_verify_result() const;
1096
1097 SSL_CTX *ssl_context() const;
1098#endif
1099
1100private:
1101 std::shared_ptr<ClientImpl> cli_;
1102
1103#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1104 bool is_ssl_ = false;
1105#endif
1106}; // namespace httplib
840 1107
841#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 1108#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
842class SSLServer : public Server { 1109class SSLServer : public Server {
@@ -845,43 +1112,58 @@ public:
845 const char *client_ca_cert_file_path = nullptr, 1112 const char *client_ca_cert_file_path = nullptr,
846 const char *client_ca_cert_dir_path = nullptr); 1113 const char *client_ca_cert_dir_path = nullptr);
847 1114
848 virtual ~SSLServer(); 1115 SSLServer(X509 *cert, EVP_PKEY *private_key,
1116 X509_STORE *client_ca_cert_store = nullptr);
849 1117
850 virtual bool is_valid() const; 1118 ~SSLServer() override;
1119
1120 bool is_valid() const override;
851 1121
852private: 1122private:
853 virtual bool process_and_close_socket(socket_t sock); 1123 bool process_and_close_socket(socket_t sock) override;
854 1124
855 SSL_CTX *ctx_; 1125 SSL_CTX *ctx_;
856 std::mutex ctx_mutex_; 1126 std::mutex ctx_mutex_;
857}; 1127};
858 1128
859class SSLClient : public Client { 1129class SSLClient : public ClientImpl {
860public: 1130public:
861 SSLClient(const std::string &host, int port = 443, 1131 explicit SSLClient(const std::string &host);
862 const std::string &client_cert_path = std::string(),
863 const std::string &client_key_path = std::string());
864 1132
865 virtual ~SSLClient(); 1133 explicit SSLClient(const std::string &host, int port);
866 1134
867 virtual bool is_valid() const; 1135 explicit SSLClient(const std::string &host, int port,
1136 const std::string &client_cert_path,
1137 const std::string &client_key_path);
1138
1139 explicit SSLClient(const std::string &host, int port, X509 *client_cert,
1140 EVP_PKEY *client_key);
868 1141
869 void set_ca_cert_path(const char *ca_ceert_file_path, 1142 ~SSLClient() override;
1143
1144 bool is_valid() const override;
1145
1146 void set_ca_cert_path(const char *ca_cert_file_path,
870 const char *ca_cert_dir_path = nullptr); 1147 const char *ca_cert_dir_path = nullptr);
871 1148
872 void enable_server_certificate_verification(bool enabled); 1149 void set_ca_cert_store(X509_STORE *ca_cert_store);
873 1150
874 long get_openssl_verify_result() const; 1151 long get_openssl_verify_result() const;
875 1152
876 SSL_CTX *ssl_context() const noexcept; 1153 SSL_CTX *ssl_context() const;
877 1154
878private: 1155private:
879 virtual bool process_and_close_socket( 1156 bool create_and_connect_socket(Socket &socket) override;
880 socket_t sock, size_t request_count, 1157 void close_socket(Socket &socket, bool process_socket_ret) override;
881 std::function<bool(Stream &strm, bool last_connection, 1158
882 bool &connection_close)> 1159 bool process_socket(Socket &socket,
883 callback); 1160 std::function<bool(Stream &strm)> callback) override;
884 virtual bool is_ssl() const; 1161 bool is_ssl() const override;
1162
1163 bool connect_with_proxy(Socket &sock, Response &res, bool &success);
1164 bool initialize_ssl(Socket &socket);
1165
1166 bool load_certs();
885 1167
886 bool verify_host(X509 *server_cert) const; 1168 bool verify_host(X509 *server_cert) const;
887 bool verify_host_with_subject_alt_name(X509 *server_cert) const; 1169 bool verify_host_with_subject_alt_name(X509 *server_cert) const;
@@ -890,12 +1172,15 @@ private:
890 1172
891 SSL_CTX *ctx_; 1173 SSL_CTX *ctx_;
892 std::mutex ctx_mutex_; 1174 std::mutex ctx_mutex_;
1175 std::once_flag initialize_cert_;
1176
893 std::vector<std::string> host_components_; 1177 std::vector<std::string> host_components_;
894 1178
895 std::string ca_cert_file_path_; 1179 std::string ca_cert_file_path_;
896 std::string ca_cert_dir_path_; 1180 std::string ca_cert_dir_path_;
897 bool server_certificate_verification_ = false;
898 long verify_result_ = 0; 1181 long verify_result_ = 0;
1182
1183 friend class ClientImpl;
899}; 1184};
900#endif 1185#endif
901 1186
@@ -948,31 +1233,39 @@ inline std::string from_i_to_hex(size_t n) {
948 return ret; 1233 return ret;
949} 1234}
950 1235
1236inline bool start_with(const std::string &a, const std::string &b) {
1237 if (a.size() < b.size()) { return false; }
1238 for (size_t i = 0; i < b.size(); i++) {
1239 if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
1240 }
1241 return true;
1242}
1243
951inline size_t to_utf8(int code, char *buff) { 1244inline size_t to_utf8(int code, char *buff) {
952 if (code < 0x0080) { 1245 if (code < 0x0080) {
953 buff[0] = (code & 0x7F); 1246 buff[0] = (code & 0x7F);
954 return 1; 1247 return 1;
955 } else if (code < 0x0800) { 1248 } else if (code < 0x0800) {
956 buff[0] = (0xC0 | ((code >> 6) & 0x1F)); 1249 buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
957 buff[1] = (0x80 | (code & 0x3F)); 1250 buff[1] = static_cast<char>(0x80 | (code & 0x3F));
958 return 2; 1251 return 2;
959 } else if (code < 0xD800) { 1252 } else if (code < 0xD800) {
960 buff[0] = (0xE0 | ((code >> 12) & 0xF)); 1253 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
961 buff[1] = (0x80 | ((code >> 6) & 0x3F)); 1254 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
962 buff[2] = (0x80 | (code & 0x3F)); 1255 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
963 return 3; 1256 return 3;
964 } else if (code < 0xE000) { // D800 - DFFF is invalid... 1257 } else if (code < 0xE000) { // D800 - DFFF is invalid...
965 return 0; 1258 return 0;
966 } else if (code < 0x10000) { 1259 } else if (code < 0x10000) {
967 buff[0] = (0xE0 | ((code >> 12) & 0xF)); 1260 buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
968 buff[1] = (0x80 | ((code >> 6) & 0x3F)); 1261 buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
969 buff[2] = (0x80 | (code & 0x3F)); 1262 buff[2] = static_cast<char>(0x80 | (code & 0x3F));
970 return 3; 1263 return 3;
971 } else if (code < 0x110000) { 1264 } else if (code < 0x110000) {
972 buff[0] = (0xF0 | ((code >> 18) & 0x7)); 1265 buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
973 buff[1] = (0x80 | ((code >> 12) & 0x3F)); 1266 buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
974 buff[2] = (0x80 | ((code >> 6) & 0x3F)); 1267 buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
975 buff[3] = (0x80 | (code & 0x3F)); 1268 buff[3] = static_cast<char>(0x80 | (code & 0x3F));
976 return 4; 1269 return 4;
977 } 1270 }
978 1271
@@ -992,8 +1285,8 @@ inline std::string base64_encode(const std::string &in) {
992 int val = 0; 1285 int val = 0;
993 int valb = -6; 1286 int valb = -6;
994 1287
995 for (uint8_t c : in) { 1288 for (auto c : in) {
996 val = (val << 8) + c; 1289 val = (val << 8) + static_cast<uint8_t>(c);
997 valb += 8; 1290 valb += 8;
998 while (valb >= 0) { 1291 while (valb >= 0) {
999 out.push_back(lookup[(val >> valb) & 0x3F]); 1292 out.push_back(lookup[(val >> valb) & 0x3F]);
@@ -1057,13 +1350,81 @@ inline bool is_valid_path(const std::string &path) {
1057 return true; 1350 return true;
1058} 1351}
1059 1352
1353inline std::string encode_url(const std::string &s) {
1354 std::string result;
1355
1356 for (size_t i = 0; s[i]; i++) {
1357 switch (s[i]) {
1358 case ' ': result += "%20"; break;
1359 case '+': result += "%2B"; break;
1360 case '\r': result += "%0D"; break;
1361 case '\n': result += "%0A"; break;
1362 case '\'': result += "%27"; break;
1363 case ',': result += "%2C"; break;
1364 // case ':': result += "%3A"; break; // ok? probably...
1365 case ';': result += "%3B"; break;
1366 default:
1367 auto c = static_cast<uint8_t>(s[i]);
1368 if (c >= 0x80) {
1369 result += '%';
1370 char hex[4];
1371 auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
1372 assert(len == 2);
1373 result.append(hex, static_cast<size_t>(len));
1374 } else {
1375 result += s[i];
1376 }
1377 break;
1378 }
1379 }
1380
1381 return result;
1382}
1383
1384inline std::string decode_url(const std::string &s,
1385 bool convert_plus_to_space) {
1386 std::string result;
1387
1388 for (size_t i = 0; i < s.size(); i++) {
1389 if (s[i] == '%' && i + 1 < s.size()) {
1390 if (s[i + 1] == 'u') {
1391 int val = 0;
1392 if (from_hex_to_i(s, i + 2, 4, val)) {
1393 // 4 digits Unicode codes
1394 char buff[4];
1395 size_t len = to_utf8(val, buff);
1396 if (len > 0) { result.append(buff, len); }
1397 i += 5; // 'u0000'
1398 } else {
1399 result += s[i];
1400 }
1401 } else {
1402 int val = 0;
1403 if (from_hex_to_i(s, i + 1, 2, val)) {
1404 // 2 digits hex codes
1405 result += static_cast<char>(val);
1406 i += 2; // '00'
1407 } else {
1408 result += s[i];
1409 }
1410 }
1411 } else if (convert_plus_to_space && s[i] == '+') {
1412 result += ' ';
1413 } else {
1414 result += s[i];
1415 }
1416 }
1417
1418 return result;
1419}
1420
1060inline void read_file(const std::string &path, std::string &out) { 1421inline void read_file(const std::string &path, std::string &out) {
1061 std::ifstream fs(path, std::ios_base::binary); 1422 std::ifstream fs(path, std::ios_base::binary);
1062 fs.seekg(0, std::ios_base::end); 1423 fs.seekg(0, std::ios_base::end);
1063 auto size = fs.tellg(); 1424 auto size = fs.tellg();
1064 fs.seekg(0); 1425 fs.seekg(0);
1065 out.resize(static_cast<size_t>(size)); 1426 out.resize(static_cast<size_t>(size));
1066 fs.read(&out[0], size); 1427 fs.read(&out[0], static_cast<std::streamsize>(size));
1067} 1428}
1068 1429
1069inline std::string file_extension(const std::string &path) { 1430inline std::string file_extension(const std::string &path) {
@@ -1073,19 +1434,41 @@ inline std::string file_extension(const std::string &path) {
1073 return std::string(); 1434 return std::string();
1074} 1435}
1075 1436
1437inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
1438
1439inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
1440 size_t right) {
1441 while (b + left < e && is_space_or_tab(b[left])) {
1442 left++;
1443 }
1444 while (right > 0 && is_space_or_tab(b[right - 1])) {
1445 right--;
1446 }
1447 return std::make_pair(left, right);
1448}
1449
1450inline std::string trim_copy(const std::string &s) {
1451 auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
1452 return s.substr(r.first, r.second - r.first);
1453}
1454
1076template <class Fn> void split(const char *b, const char *e, char d, Fn fn) { 1455template <class Fn> void split(const char *b, const char *e, char d, Fn fn) {
1077 int i = 0; 1456 size_t i = 0;
1078 int beg = 0; 1457 size_t beg = 0;
1079 1458
1080 while (e ? (b + i != e) : (b[i] != '\0')) { 1459 while (e ? (b + i < e) : (b[i] != '\0')) {
1081 if (b[i] == d) { 1460 if (b[i] == d) {
1082 fn(&b[beg], &b[i]); 1461 auto r = trim(b, e, beg, i);
1462 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
1083 beg = i + 1; 1463 beg = i + 1;
1084 } 1464 }
1085 i++; 1465 i++;
1086 } 1466 }
1087 1467
1088 if (i) { fn(&b[beg], &b[i]); } 1468 if (i) {
1469 auto r = trim(b, e, beg, i);
1470 if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
1471 }
1089} 1472}
1090 1473
1091// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` 1474// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
@@ -1172,7 +1555,17 @@ inline int close_socket(socket_t sock) {
1172#endif 1555#endif
1173} 1556}
1174 1557
1175inline int select_read(socket_t sock, time_t sec, time_t usec) { 1558template <typename T> inline ssize_t handle_EINTR(T fn) {
1559 ssize_t res = false;
1560 while (true) {
1561 res = fn();
1562 if (res < 0 && errno == EINTR) { continue; }
1563 break;
1564 }
1565 return res;
1566}
1567
1568inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
1176#ifdef CPPHTTPLIB_USE_POLL 1569#ifdef CPPHTTPLIB_USE_POLL
1177 struct pollfd pfd_read; 1570 struct pollfd pfd_read;
1178 pfd_read.fd = sock; 1571 pfd_read.fd = sock;
@@ -1180,7 +1573,7 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
1180 1573
1181 auto timeout = static_cast<int>(sec * 1000 + usec / 1000); 1574 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1182 1575
1183 return poll(&pfd_read, 1, timeout); 1576 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
1184#else 1577#else
1185 fd_set fds; 1578 fd_set fds;
1186 FD_ZERO(&fds); 1579 FD_ZERO(&fds);
@@ -1188,13 +1581,15 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
1188 1581
1189 timeval tv; 1582 timeval tv;
1190 tv.tv_sec = static_cast<long>(sec); 1583 tv.tv_sec = static_cast<long>(sec);
1191 tv.tv_usec = static_cast<long>(usec); 1584 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
1192 1585
1193 return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv); 1586 return handle_EINTR([&]() {
1587 return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
1588 });
1194#endif 1589#endif
1195} 1590}
1196 1591
1197inline int select_write(socket_t sock, time_t sec, time_t usec) { 1592inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
1198#ifdef CPPHTTPLIB_USE_POLL 1593#ifdef CPPHTTPLIB_USE_POLL
1199 struct pollfd pfd_read; 1594 struct pollfd pfd_read;
1200 pfd_read.fd = sock; 1595 pfd_read.fd = sock;
@@ -1202,7 +1597,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
1202 1597
1203 auto timeout = static_cast<int>(sec * 1000 + usec / 1000); 1598 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1204 1599
1205 return poll(&pfd_read, 1, timeout); 1600 return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
1206#else 1601#else
1207 fd_set fds; 1602 fd_set fds;
1208 FD_ZERO(&fds); 1603 FD_ZERO(&fds);
@@ -1210,9 +1605,11 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
1210 1605
1211 timeval tv; 1606 timeval tv;
1212 tv.tv_sec = static_cast<long>(sec); 1607 tv.tv_sec = static_cast<long>(sec);
1213 tv.tv_usec = static_cast<long>(usec); 1608 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
1214 1609
1215 return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv); 1610 return handle_EINTR([&]() {
1611 return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
1612 });
1216#endif 1613#endif
1217} 1614}
1218 1615
@@ -1224,13 +1621,14 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1224 1621
1225 auto timeout = static_cast<int>(sec * 1000 + usec / 1000); 1622 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1226 1623
1227 if (poll(&pfd_read, 1, timeout) > 0 && 1624 auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
1228 pfd_read.revents & (POLLIN | POLLOUT)) { 1625
1626 if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
1229 int error = 0; 1627 int error = 0;
1230 socklen_t len = sizeof(error); 1628 socklen_t len = sizeof(error);
1231 return getsockopt(sock, SOL_SOCKET, SO_ERROR, 1629 auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
1232 reinterpret_cast<char *>(&error), &len) >= 0 && 1630 reinterpret_cast<char *>(&error), &len);
1233 !error; 1631 return res >= 0 && !error;
1234 } 1632 }
1235 return false; 1633 return false;
1236#else 1634#else
@@ -1243,10 +1641,13 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1243 1641
1244 timeval tv; 1642 timeval tv;
1245 tv.tv_sec = static_cast<long>(sec); 1643 tv.tv_sec = static_cast<long>(sec);
1246 tv.tv_usec = static_cast<long>(usec); 1644 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
1645
1646 auto ret = handle_EINTR([&]() {
1647 return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
1648 });
1247 1649
1248 if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 && 1650 if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
1249 (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
1250 int error = 0; 1651 int error = 0;
1251 socklen_t len = sizeof(error); 1652 socklen_t len = sizeof(error);
1252 return getsockopt(sock, SOL_SOCKET, SO_ERROR, 1653 return getsockopt(sock, SOL_SOCKET, SO_ERROR,
@@ -1259,40 +1660,45 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1259 1660
1260class SocketStream : public Stream { 1661class SocketStream : public Stream {
1261public: 1662public:
1262 SocketStream(socket_t sock, time_t read_timeout_sec, 1663 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
1263 time_t read_timeout_usec); 1664 time_t write_timeout_sec, time_t write_timeout_usec);
1264 ~SocketStream() override; 1665 ~SocketStream() override;
1265 1666
1266 bool is_readable() const override; 1667 bool is_readable() const override;
1267 bool is_writable() const override; 1668 bool is_writable() const override;
1268 int read(char *ptr, size_t size) override; 1669 ssize_t read(char *ptr, size_t size) override;
1269 int write(const char *ptr, size_t size) override; 1670 ssize_t write(const char *ptr, size_t size) override;
1270 std::string get_remote_addr() const override; 1671 void get_remote_ip_and_port(std::string &ip, int &port) const override;
1271 1672
1272private: 1673private:
1273 socket_t sock_; 1674 socket_t sock_;
1274 time_t read_timeout_sec_; 1675 time_t read_timeout_sec_;
1275 time_t read_timeout_usec_; 1676 time_t read_timeout_usec_;
1677 time_t write_timeout_sec_;
1678 time_t write_timeout_usec_;
1276}; 1679};
1277 1680
1278#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 1681#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1279class SSLSocketStream : public Stream { 1682class SSLSocketStream : public Stream {
1280public: 1683public:
1281 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, 1684 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
1282 time_t read_timeout_usec); 1685 time_t read_timeout_usec, time_t write_timeout_sec,
1283 virtual ~SSLSocketStream(); 1686 time_t write_timeout_usec);
1687 ~SSLSocketStream() override;
1284 1688
1285 bool is_readable() const override; 1689 bool is_readable() const override;
1286 bool is_writable() const override; 1690 bool is_writable() const override;
1287 int read(char *ptr, size_t size) override; 1691 ssize_t read(char *ptr, size_t size) override;
1288 int write(const char *ptr, size_t size) override; 1692 ssize_t write(const char *ptr, size_t size) override;
1289 std::string get_remote_addr() const override; 1693 void get_remote_ip_and_port(std::string &ip, int &port) const override;
1290 1694
1291private: 1695private:
1292 socket_t sock_; 1696 socket_t sock_;
1293 SSL *ssl_; 1697 SSL *ssl_;
1294 time_t read_timeout_sec_; 1698 time_t read_timeout_sec_;
1295 time_t read_timeout_usec_; 1699 time_t read_timeout_usec_;
1700 time_t write_timeout_sec_;
1701 time_t write_timeout_usec_;
1296}; 1702};
1297#endif 1703#endif
1298 1704
@@ -1303,58 +1709,76 @@ public:
1303 1709
1304 bool is_readable() const override; 1710 bool is_readable() const override;
1305 bool is_writable() const override; 1711 bool is_writable() const override;
1306 int read(char *ptr, size_t size) override; 1712 ssize_t read(char *ptr, size_t size) override;
1307 int write(const char *ptr, size_t size) override; 1713 ssize_t write(const char *ptr, size_t size) override;
1308 std::string get_remote_addr() const override; 1714 void get_remote_ip_and_port(std::string &ip, int &port) const override;
1309 1715
1310 const std::string &get_buffer() const; 1716 const std::string &get_buffer() const;
1311 1717
1312private: 1718private:
1313 std::string buffer; 1719 std::string buffer;
1314 int position = 0; 1720 size_t position = 0;
1315}; 1721};
1316 1722
1723inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
1724 using namespace std::chrono;
1725 auto start = steady_clock::now();
1726 while (true) {
1727 auto val = select_read(sock, 0, 10000);
1728 if (val < 0) {
1729 return false;
1730 } else if (val == 0) {
1731 auto current = steady_clock::now();
1732 auto duration = duration_cast<milliseconds>(current - start);
1733 auto timeout = keep_alive_timeout_sec * 1000;
1734 if (duration.count() > timeout) { return false; }
1735 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1736 } else {
1737 return true;
1738 }
1739 }
1740}
1741
1317template <typename T> 1742template <typename T>
1318inline bool process_socket(bool is_client_request, socket_t sock, 1743inline bool
1319 size_t keep_alive_max_count, time_t read_timeout_sec, 1744process_server_socket_core(socket_t sock, size_t keep_alive_max_count,
1320 time_t read_timeout_usec, T callback) { 1745 time_t keep_alive_timeout_sec, T callback) {
1321 assert(keep_alive_max_count > 0); 1746 assert(keep_alive_max_count > 0);
1322
1323 auto ret = false; 1747 auto ret = false;
1324 1748 auto count = keep_alive_max_count;
1325 if (keep_alive_max_count > 1) { 1749 while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) {
1326 auto count = keep_alive_max_count; 1750 auto close_connection = count == 1;
1327 while (count > 0 && 1751 auto connection_closed = false;
1328 (is_client_request || 1752 ret = callback(close_connection, connection_closed);
1329 select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, 1753 if (!ret || connection_closed) { break; }
1330 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { 1754 count--;
1331 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1332 auto last_connection = count == 1;
1333 auto connection_close = false;
1334
1335 ret = callback(strm, last_connection, connection_close);
1336 if (!ret || connection_close) { break; }
1337
1338 count--;
1339 }
1340 } else { // keep_alive_max_count is 0 or 1
1341 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1342 auto dummy_connection_close = false;
1343 ret = callback(strm, true, dummy_connection_close);
1344 } 1755 }
1345
1346 return ret; 1756 return ret;
1347} 1757}
1348 1758
1349template <typename T> 1759template <typename T>
1350inline bool process_and_close_socket(bool is_client_request, socket_t sock, 1760inline bool
1351 size_t keep_alive_max_count, 1761process_server_socket(socket_t sock, size_t keep_alive_max_count,
1352 time_t read_timeout_sec, 1762 time_t keep_alive_timeout_sec, time_t read_timeout_sec,
1353 time_t read_timeout_usec, T callback) { 1763 time_t read_timeout_usec, time_t write_timeout_sec,
1354 auto ret = process_socket(is_client_request, sock, keep_alive_max_count, 1764 time_t write_timeout_usec, T callback) {
1355 read_timeout_sec, read_timeout_usec, callback); 1765 return process_server_socket_core(
1356 close_socket(sock); 1766 sock, keep_alive_max_count, keep_alive_timeout_sec,
1357 return ret; 1767 [&](bool close_connection, bool &connection_closed) {
1768 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
1769 write_timeout_sec, write_timeout_usec);
1770 return callback(strm, close_connection, connection_closed);
1771 });
1772}
1773
1774template <typename T>
1775inline bool process_client_socket(socket_t sock, time_t read_timeout_sec,
1776 time_t read_timeout_usec,
1777 time_t write_timeout_sec,
1778 time_t write_timeout_usec, T callback) {
1779 SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
1780 write_timeout_sec, write_timeout_usec);
1781 return callback(strm);
1358} 1782}
1359 1783
1360inline int shutdown_socket(socket_t sock) { 1784inline int shutdown_socket(socket_t sock) {
@@ -1365,18 +1789,10 @@ inline int shutdown_socket(socket_t sock) {
1365#endif 1789#endif
1366} 1790}
1367 1791
1368template <typename Fn> 1792template <typename BindOrConnect>
1369socket_t create_socket(const char *host, int port, Fn fn, 1793socket_t create_socket(const char *host, int port, int socket_flags,
1370 int socket_flags = 0) { 1794 bool tcp_nodelay, SocketOptions socket_options,
1371#ifdef _WIN32 1795 BindOrConnect bind_or_connect) {
1372#define SO_SYNCHRONOUS_NONALERT 0x20
1373#define SO_OPENTYPE 0x7008
1374
1375 int opt = SO_SYNCHRONOUS_NONALERT;
1376 setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt,
1377 sizeof(opt));
1378#endif
1379
1380 // Get address info 1796 // Get address info
1381 struct addrinfo hints; 1797 struct addrinfo hints;
1382 struct addrinfo *result; 1798 struct addrinfo *result;
@@ -1390,6 +1806,9 @@ socket_t create_socket(const char *host, int port, Fn fn,
1390 auto service = std::to_string(port); 1806 auto service = std::to_string(port);
1391 1807
1392 if (getaddrinfo(host, service.c_str(), &hints, &result)) { 1808 if (getaddrinfo(host, service.c_str(), &hints, &result)) {
1809#ifdef __linux__
1810 res_init();
1811#endif
1393 return INVALID_SOCKET; 1812 return INVALID_SOCKET;
1394 } 1813 }
1395 1814
@@ -1424,17 +1843,22 @@ socket_t create_socket(const char *host, int port, Fn fn,
1424 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } 1843 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
1425#endif 1844#endif
1426 1845
1427 // Make 'reuse address' option available 1846 if (tcp_nodelay) {
1428 int yes = 1; 1847 int yes = 1;
1429 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes), 1848 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes),
1430 sizeof(yes)); 1849 sizeof(yes));
1431#ifdef SO_REUSEPORT 1850 }
1432 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes), 1851
1433 sizeof(yes)); 1852 if (socket_options) { socket_options(sock); }
1434#endif 1853
1854 if (rp->ai_family == AF_INET6) {
1855 int no = 0;
1856 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no),
1857 sizeof(no));
1858 }
1435 1859
1436 // bind or connect 1860 // bind or connect
1437 if (fn(sock, *rp)) { 1861 if (bind_or_connect(sock, *rp)) {
1438 freeaddrinfo(result); 1862 freeaddrinfo(result);
1439 return sock; 1863 return sock;
1440 } 1864 }
@@ -1479,7 +1903,7 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
1479 auto ret = false; 1903 auto ret = false;
1480 for (auto rp = result; rp; rp = rp->ai_next) { 1904 for (auto rp = result; rp; rp = rp->ai_next) {
1481 const auto &ai = *rp; 1905 const auto &ai = *rp;
1482 if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) { 1906 if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
1483 ret = true; 1907 ret = true;
1484 break; 1908 break;
1485 } 1909 }
@@ -1489,8 +1913,12 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
1489 return ret; 1913 return ret;
1490} 1914}
1491 1915
1916#if !defined _WIN32 && !defined ANDROID
1917#define USE_IF2IP
1918#endif
1919
1920#ifdef USE_IF2IP
1492inline std::string if2ip(const std::string &ifn) { 1921inline std::string if2ip(const std::string &ifn) {
1493#ifndef _WIN32
1494 struct ifaddrs *ifap; 1922 struct ifaddrs *ifap;
1495 getifaddrs(&ifap); 1923 getifaddrs(&ifap);
1496 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { 1924 for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
@@ -1506,51 +1934,83 @@ inline std::string if2ip(const std::string &ifn) {
1506 } 1934 }
1507 } 1935 }
1508 freeifaddrs(ifap); 1936 freeifaddrs(ifap);
1509#endif
1510 return std::string(); 1937 return std::string();
1511} 1938}
1939#endif
1512 1940
1513inline socket_t create_client_socket(const char *host, int port, 1941inline socket_t create_client_socket(const char *host, int port,
1514 time_t timeout_sec, 1942 bool tcp_nodelay,
1515 const std::string &intf) { 1943 SocketOptions socket_options,
1516 return create_socket( 1944 time_t timeout_sec, time_t timeout_usec,
1517 host, port, [&](socket_t sock, struct addrinfo &ai) -> bool { 1945 const std::string &intf, Error &error) {
1946 auto sock = create_socket(
1947 host, port, 0, tcp_nodelay, socket_options,
1948 [&](socket_t sock, struct addrinfo &ai) -> bool {
1518 if (!intf.empty()) { 1949 if (!intf.empty()) {
1950#ifdef USE_IF2IP
1519 auto ip = if2ip(intf); 1951 auto ip = if2ip(intf);
1520 if (ip.empty()) { ip = intf; } 1952 if (ip.empty()) { ip = intf; }
1521 if (!bind_ip_address(sock, ip.c_str())) { return false; } 1953 if (!bind_ip_address(sock, ip.c_str())) {
1954 error = Error::BindIPAddress;
1955 return false;
1956 }
1957#endif
1522 } 1958 }
1523 1959
1524 set_nonblocking(sock, true); 1960 set_nonblocking(sock, true);
1525 1961
1526 auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen)); 1962 auto ret =
1963 ::connect(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
1964
1527 if (ret < 0) { 1965 if (ret < 0) {
1528 if (is_connection_error() || 1966 if (is_connection_error() ||
1529 !wait_until_socket_is_ready(sock, timeout_sec, 0)) { 1967 !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) {
1530 close_socket(sock); 1968 close_socket(sock);
1969 error = Error::Connection;
1531 return false; 1970 return false;
1532 } 1971 }
1533 } 1972 }
1534 1973
1535 set_nonblocking(sock, false); 1974 set_nonblocking(sock, false);
1975 error = Error::Success;
1536 return true; 1976 return true;
1537 }); 1977 });
1538}
1539 1978
1540inline std::string get_remote_addr(socket_t sock) { 1979 if (sock != INVALID_SOCKET) {
1541 struct sockaddr_storage addr; 1980 error = Error::Success;
1542 socklen_t len = sizeof(addr); 1981 } else {
1982 if (error == Error::Success) { error = Error::Connection; }
1983 }
1543 1984
1544 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) { 1985 return sock;
1545 std::array<char, NI_MAXHOST> ipstr{}; 1986}
1546 1987
1547 if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, 1988inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
1548 ipstr.data(), static_cast<unsigned int>(ipstr.size()), nullptr, 0, NI_NUMERICHOST)) { 1989 socklen_t addr_len, std::string &ip,
1549 return ipstr.data(); 1990 int &port) {
1550 } 1991 if (addr.ss_family == AF_INET) {
1992 port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
1993 } else if (addr.ss_family == AF_INET6) {
1994 port =
1995 ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
1551 } 1996 }
1552 1997
1553 return std::string(); 1998 std::array<char, NI_MAXHOST> ipstr{};
1999 if (!getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
2000 ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
2001 0, NI_NUMERICHOST)) {
2002 ip = ipstr.data();
2003 }
2004}
2005
2006inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
2007 struct sockaddr_storage addr;
2008 socklen_t addr_len = sizeof(addr);
2009
2010 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
2011 &addr_len)) {
2012 get_remote_ip_and_port(addr, addr_len, ip, port);
2013 }
1554} 2014}
1555 2015
1556inline const char * 2016inline const char *
@@ -1596,121 +2056,336 @@ find_content_type(const std::string &path,
1596inline const char *status_message(int status) { 2056inline const char *status_message(int status) {
1597 switch (status) { 2057 switch (status) {
1598 case 100: return "Continue"; 2058 case 100: return "Continue";
2059 case 101: return "Switching Protocol";
2060 case 102: return "Processing";
2061 case 103: return "Early Hints";
1599 case 200: return "OK"; 2062 case 200: return "OK";
2063 case 201: return "Created";
1600 case 202: return "Accepted"; 2064 case 202: return "Accepted";
2065 case 203: return "Non-Authoritative Information";
1601 case 204: return "No Content"; 2066 case 204: return "No Content";
2067 case 205: return "Reset Content";
1602 case 206: return "Partial Content"; 2068 case 206: return "Partial Content";
2069 case 207: return "Multi-Status";
2070 case 208: return "Already Reported";
2071 case 226: return "IM Used";
2072 case 300: return "Multiple Choice";
1603 case 301: return "Moved Permanently"; 2073 case 301: return "Moved Permanently";
1604 case 302: return "Found"; 2074 case 302: return "Found";
1605 case 303: return "See Other"; 2075 case 303: return "See Other";
1606 case 304: return "Not Modified"; 2076 case 304: return "Not Modified";
2077 case 305: return "Use Proxy";
2078 case 306: return "unused";
2079 case 307: return "Temporary Redirect";
2080 case 308: return "Permanent Redirect";
1607 case 400: return "Bad Request"; 2081 case 400: return "Bad Request";
1608 case 401: return "Unauthorized"; 2082 case 401: return "Unauthorized";
2083 case 402: return "Payment Required";
1609 case 403: return "Forbidden"; 2084 case 403: return "Forbidden";
1610 case 404: return "Not Found"; 2085 case 404: return "Not Found";
2086 case 405: return "Method Not Allowed";
2087 case 406: return "Not Acceptable";
2088 case 407: return "Proxy Authentication Required";
2089 case 408: return "Request Timeout";
2090 case 409: return "Conflict";
2091 case 410: return "Gone";
2092 case 411: return "Length Required";
2093 case 412: return "Precondition Failed";
1611 case 413: return "Payload Too Large"; 2094 case 413: return "Payload Too Large";
1612 case 414: return "Request-URI Too Long"; 2095 case 414: return "URI Too Long";
1613 case 415: return "Unsupported Media Type"; 2096 case 415: return "Unsupported Media Type";
1614 case 416: return "Range Not Satisfiable"; 2097 case 416: return "Range Not Satisfiable";
1615 case 417: return "Expectation Failed"; 2098 case 417: return "Expectation Failed";
2099 case 418: return "I'm a teapot";
2100 case 421: return "Misdirected Request";
2101 case 422: return "Unprocessable Entity";
2102 case 423: return "Locked";
2103 case 424: return "Failed Dependency";
2104 case 425: return "Too Early";
2105 case 426: return "Upgrade Required";
2106 case 428: return "Precondition Required";
2107 case 429: return "Too Many Requests";
2108 case 431: return "Request Header Fields Too Large";
2109 case 451: return "Unavailable For Legal Reasons";
2110 case 501: return "Not Implemented";
2111 case 502: return "Bad Gateway";
1616 case 503: return "Service Unavailable"; 2112 case 503: return "Service Unavailable";
2113 case 504: return "Gateway Timeout";
2114 case 505: return "HTTP Version Not Supported";
2115 case 506: return "Variant Also Negotiates";
2116 case 507: return "Insufficient Storage";
2117 case 508: return "Loop Detected";
2118 case 510: return "Not Extended";
2119 case 511: return "Network Authentication Required";
1617 2120
1618 default: 2121 default:
1619 case 500: return "Internal Server Error"; 2122 case 500: return "Internal Server Error";
1620 } 2123 }
1621} 2124}
1622 2125
1623#ifdef CPPHTTPLIB_ZLIB_SUPPORT 2126inline bool can_compress_content_type(const std::string &content_type) {
1624inline bool can_compress(const std::string &content_type) { 2127 return (!content_type.find("text/") && content_type != "text/event-stream") ||
1625 return !content_type.find("text/") || content_type == "image/svg+xml" || 2128 content_type == "image/svg+xml" ||
1626 content_type == "application/javascript" || 2129 content_type == "application/javascript" ||
1627 content_type == "application/json" || 2130 content_type == "application/json" ||
1628 content_type == "application/xml" || 2131 content_type == "application/xml" ||
1629 content_type == "application/xhtml+xml"; 2132 content_type == "application/xhtml+xml";
1630} 2133}
1631 2134
1632inline bool compress(std::string &content) { 2135enum class EncodingType { None = 0, Gzip, Brotli };
1633 z_stream strm;
1634 strm.zalloc = Z_NULL;
1635 strm.zfree = Z_NULL;
1636 strm.opaque = Z_NULL;
1637 2136
1638 auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, 2137inline EncodingType encoding_type(const Request &req, const Response &res) {
1639 Z_DEFAULT_STRATEGY); 2138 auto ret =
1640 if (ret != Z_OK) { return false; } 2139 detail::can_compress_content_type(res.get_header_value("Content-Type"));
2140 if (!ret) { return EncodingType::None; }
1641 2141
1642 strm.avail_in = content.size(); 2142 const auto &s = req.get_header_value("Accept-Encoding");
1643 strm.next_in = 2143 (void)(s);
1644 const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
1645 2144
1646 std::string compressed; 2145#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2146 // TODO: 'Accept-Encoding' has br, not br;q=0
2147 ret = s.find("br") != std::string::npos;
2148 if (ret) { return EncodingType::Brotli; }
2149#endif
1647 2150
1648 std::array<char, 16384> buff{}; 2151#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1649 do { 2152 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
1650 strm.avail_out = buff.size(); 2153 ret = s.find("gzip") != std::string::npos;
1651 strm.next_out = reinterpret_cast<Bytef *>(buff.data()); 2154 if (ret) { return EncodingType::Gzip; }
1652 ret = deflate(&strm, Z_FINISH); 2155#endif
1653 assert(ret != Z_STREAM_ERROR);
1654 compressed.append(buff.data(), buff.size() - strm.avail_out);
1655 } while (strm.avail_out == 0);
1656 2156
1657 assert(ret == Z_STREAM_END); 2157 return EncodingType::None;
1658 assert(strm.avail_in == 0); 2158}
1659 2159
1660 content.swap(compressed); 2160class compressor {
2161public:
2162 virtual ~compressor(){};
1661 2163
1662 deflateEnd(&strm); 2164 typedef std::function<bool(const char *data, size_t data_len)> Callback;
1663 return true; 2165 virtual bool compress(const char *data, size_t data_length, bool last,
1664} 2166 Callback callback) = 0;
2167};
1665 2168
1666class decompressor { 2169class decompressor {
1667public: 2170public:
1668 decompressor() { 2171 virtual ~decompressor() {}
1669 strm.zalloc = Z_NULL; 2172
1670 strm.zfree = Z_NULL; 2173 virtual bool is_valid() const = 0;
1671 strm.opaque = Z_NULL; 2174
2175 typedef std::function<bool(const char *data, size_t data_len)> Callback;
2176 virtual bool decompress(const char *data, size_t data_length,
2177 Callback callback) = 0;
2178};
2179
2180class nocompressor : public compressor {
2181public:
2182 ~nocompressor(){};
2183
2184 bool compress(const char *data, size_t data_length, bool /*last*/,
2185 Callback callback) override {
2186 if (!data_length) { return true; }
2187 return callback(data, data_length);
2188 }
2189};
2190
2191#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2192class gzip_compressor : public compressor {
2193public:
2194 gzip_compressor() {
2195 std::memset(&strm_, 0, sizeof(strm_));
2196 strm_.zalloc = Z_NULL;
2197 strm_.zfree = Z_NULL;
2198 strm_.opaque = Z_NULL;
2199
2200 is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
2201 Z_DEFAULT_STRATEGY) == Z_OK;
2202 }
2203
2204 ~gzip_compressor() { deflateEnd(&strm_); }
2205
2206 bool compress(const char *data, size_t data_length, bool last,
2207 Callback callback) override {
2208 assert(is_valid_);
2209
2210 auto flush = last ? Z_FINISH : Z_NO_FLUSH;
2211
2212 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
2213 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
2214
2215 int ret = Z_OK;
2216
2217 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2218 do {
2219 strm_.avail_out = buff.size();
2220 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
2221
2222 ret = deflate(&strm_, flush);
2223 assert(ret != Z_STREAM_ERROR);
2224
2225 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
2226 return false;
2227 }
2228 } while (strm_.avail_out == 0);
2229
2230 assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK));
2231 assert(strm_.avail_in == 0);
2232 return true;
2233 }
2234
2235private:
2236 bool is_valid_ = false;
2237 z_stream strm_;
2238};
2239
2240class gzip_decompressor : public decompressor {
2241public:
2242 gzip_decompressor() {
2243 std::memset(&strm_, 0, sizeof(strm_));
2244 strm_.zalloc = Z_NULL;
2245 strm_.zfree = Z_NULL;
2246 strm_.opaque = Z_NULL;
1672 2247
1673 // 15 is the value of wbits, which should be at the maximum possible value 2248 // 15 is the value of wbits, which should be at the maximum possible value
1674 // to ensure that any gzip stream can be decoded. The offset of 16 specifies 2249 // to ensure that any gzip stream can be decoded. The offset of 32 specifies
1675 // that the stream to decompress will be formatted with a gzip wrapper. 2250 // that the stream type should be automatically detected either gzip or
1676 is_valid_ = inflateInit2(&strm, 16 + 15) == Z_OK; 2251 // deflate.
2252 is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
1677 } 2253 }
1678 2254
1679 ~decompressor() { inflateEnd(&strm); } 2255 ~gzip_decompressor() { inflateEnd(&strm_); }
1680 2256
1681 bool is_valid() const { return is_valid_; } 2257 bool is_valid() const override { return is_valid_; }
2258
2259 bool decompress(const char *data, size_t data_length,
2260 Callback callback) override {
2261 assert(is_valid_);
1682 2262
1683 template <typename T>
1684 bool decompress(const char *data, size_t data_length, T callback) {
1685 int ret = Z_OK; 2263 int ret = Z_OK;
1686 2264
1687 strm.avail_in = data_length; 2265 strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
1688 strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data)); 2266 strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
1689 2267
1690 std::array<char, 16384> buff{}; 2268 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
1691 do { 2269 while (strm_.avail_in > 0) {
1692 strm.avail_out = buff.size(); 2270 strm_.avail_out = buff.size();
1693 strm.next_out = reinterpret_cast<Bytef *>(buff.data()); 2271 strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
1694 2272
1695 ret = inflate(&strm, Z_NO_FLUSH); 2273 ret = inflate(&strm_, Z_NO_FLUSH);
1696 assert(ret != Z_STREAM_ERROR); 2274 assert(ret != Z_STREAM_ERROR);
1697 switch (ret) { 2275 switch (ret) {
1698 case Z_NEED_DICT: 2276 case Z_NEED_DICT:
1699 case Z_DATA_ERROR: 2277 case Z_DATA_ERROR:
1700 case Z_MEM_ERROR: inflateEnd(&strm); return false; 2278 case Z_MEM_ERROR: inflateEnd(&strm_); return false;
1701 } 2279 }
1702 2280
1703 if (!callback(buff.data(), buff.size() - strm.avail_out)) { 2281 if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
1704 return false; 2282 return false;
1705 } 2283 }
1706 } while (strm.avail_out == 0); 2284 }
1707 2285
1708 return ret == Z_OK || ret == Z_STREAM_END; 2286 return ret == Z_OK || ret == Z_STREAM_END;
1709 } 2287 }
1710 2288
1711private: 2289private:
1712 bool is_valid_; 2290 bool is_valid_ = false;
1713 z_stream strm; 2291 z_stream strm_;
2292};
2293#endif
2294
2295#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2296class brotli_compressor : public compressor {
2297public:
2298 brotli_compressor() {
2299 state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
2300 }
2301
2302 ~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
2303
2304 bool compress(const char *data, size_t data_length, bool last,
2305 Callback callback) override {
2306 std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2307
2308 auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
2309 auto available_in = data_length;
2310 auto next_in = reinterpret_cast<const uint8_t *>(data);
2311
2312 for (;;) {
2313 if (last) {
2314 if (BrotliEncoderIsFinished(state_)) { break; }
2315 } else {
2316 if (!available_in) { break; }
2317 }
2318
2319 auto available_out = buff.size();
2320 auto next_out = buff.data();
2321
2322 if (!BrotliEncoderCompressStream(state_, operation, &available_in,
2323 &next_in, &available_out, &next_out,
2324 nullptr)) {
2325 return false;
2326 }
2327
2328 auto output_bytes = buff.size() - available_out;
2329 if (output_bytes) {
2330 callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
2331 }
2332 }
2333
2334 return true;
2335 }
2336
2337private:
2338 BrotliEncoderState *state_ = nullptr;
2339};
2340
2341class brotli_decompressor : public decompressor {
2342public:
2343 brotli_decompressor() {
2344 decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
2345 decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
2346 : BROTLI_DECODER_RESULT_ERROR;
2347 }
2348
2349 ~brotli_decompressor() {
2350 if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
2351 }
2352
2353 bool is_valid() const override { return decoder_s; }
2354
2355 bool decompress(const char *data, size_t data_length,
2356 Callback callback) override {
2357 if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
2358 decoder_r == BROTLI_DECODER_RESULT_ERROR) {
2359 return 0;
2360 }
2361
2362 const uint8_t *next_in = (const uint8_t *)data;
2363 size_t avail_in = data_length;
2364 size_t total_out;
2365
2366 decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
2367
2368 std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
2369 while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
2370 char *next_out = buff.data();
2371 size_t avail_out = buff.size();
2372
2373 decoder_r = BrotliDecoderDecompressStream(
2374 decoder_s, &avail_in, &next_in, &avail_out,
2375 reinterpret_cast<uint8_t **>(&next_out), &total_out);
2376
2377 if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
2378
2379 if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
2380 }
2381
2382 return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
2383 decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
2384 }
2385
2386private:
2387 BrotliDecoderResult decoder_r;
2388 BrotliDecoderState *decoder_s = nullptr;
1714}; 2389};
1715#endif 2390#endif
1716 2391
@@ -1720,21 +2395,60 @@ inline bool has_header(const Headers &headers, const char *key) {
1720 2395
1721inline const char *get_header_value(const Headers &headers, const char *key, 2396inline const char *get_header_value(const Headers &headers, const char *key,
1722 size_t id = 0, const char *def = nullptr) { 2397 size_t id = 0, const char *def = nullptr) {
1723 auto it = headers.find(key); 2398 auto rng = headers.equal_range(key);
1724 std::advance(it, id); 2399 auto it = rng.first;
1725 if (it != headers.end()) { return it->second.c_str(); } 2400 std::advance(it, static_cast<ssize_t>(id));
2401 if (it != rng.second) { return it->second.c_str(); }
1726 return def; 2402 return def;
1727} 2403}
1728 2404
1729inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, 2405template <typename T>
1730 int def = 0) { 2406inline T get_header_value(const Headers & /*headers*/, const char * /*key*/,
1731 auto it = headers.find(key); 2407 size_t /*id*/ = 0, uint64_t /*def*/ = 0) {}
1732 if (it != headers.end()) { 2408
2409template <>
2410inline uint64_t get_header_value<uint64_t>(const Headers &headers,
2411 const char *key, size_t id,
2412 uint64_t def) {
2413 auto rng = headers.equal_range(key);
2414 auto it = rng.first;
2415 std::advance(it, static_cast<ssize_t>(id));
2416 if (it != rng.second) {
1733 return std::strtoull(it->second.data(), nullptr, 10); 2417 return std::strtoull(it->second.data(), nullptr, 10);
1734 } 2418 }
1735 return def; 2419 return def;
1736} 2420}
1737 2421
2422template <typename T>
2423inline bool parse_header(const char *beg, const char *end, T fn) {
2424 // Skip trailing spaces and tabs.
2425 while (beg < end && is_space_or_tab(end[-1])) {
2426 end--;
2427 }
2428
2429 auto p = beg;
2430 while (p < end && *p != ':') {
2431 p++;
2432 }
2433
2434 if (p == end) { return false; }
2435
2436 auto key_end = p;
2437
2438 if (*p++ != ':') { return false; }
2439
2440 while (p < end && is_space_or_tab(*p)) {
2441 p++;
2442 }
2443
2444 if (p < end) {
2445 fn(std::string(beg, key_end), decode_url(std::string(p, end), false));
2446 return true;
2447 }
2448
2449 return false;
2450}
2451
1738inline bool read_headers(Stream &strm, Headers &headers) { 2452inline bool read_headers(Stream &strm, Headers &headers) {
1739 const auto bufsiz = 2048; 2453 const auto bufsiz = 2048;
1740 char buf[bufsiz]; 2454 char buf[bufsiz];
@@ -1751,24 +2465,13 @@ inline bool read_headers(Stream &strm, Headers &headers) {
1751 continue; // Skip invalid line. 2465 continue; // Skip invalid line.
1752 } 2466 }
1753 2467
1754 // Skip trailing spaces and tabs. 2468 // Exclude CRLF
1755 auto end = line_reader.ptr() + line_reader.size() - 2; 2469 auto end = line_reader.ptr() + line_reader.size() - 2;
1756 while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) {
1757 end--;
1758 }
1759 2470
1760 // Horizontal tab and ' ' are considered whitespace and are ignored when on 2471 parse_header(line_reader.ptr(), end,
1761 // the left or right side of the header value: 2472 [&](std::string &&key, std::string &&val) {
1762 // - https://stackoverflow.com/questions/50179659/ 2473 headers.emplace(std::move(key), std::move(val));
1763 // - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html 2474 });
1764 static const std::regex re(R"((.+?):[\t ]*(.+))");
1765
1766 std::cmatch m;
1767 if (std::regex_match(line_reader.ptr(), end, m, re)) {
1768 auto key = std::string(m[1]);
1769 auto val = std::string(m[2]);
1770 headers.emplace(key, val);
1771 }
1772 } 2475 }
1773 2476
1774 return true; 2477 return true;
@@ -1781,12 +2484,12 @@ inline bool read_content_with_length(Stream &strm, uint64_t len,
1781 uint64_t r = 0; 2484 uint64_t r = 0;
1782 while (r < len) { 2485 while (r < len) {
1783 auto read_len = static_cast<size_t>(len - r); 2486 auto read_len = static_cast<size_t>(len - r);
1784 auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ)); 2487 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
1785 if (n <= 0) { return false; } 2488 if (n <= 0) { return false; }
1786 2489
1787 if (!out(buf, n)) { return false; } 2490 if (!out(buf, static_cast<size_t>(n))) { return false; }
1788 2491
1789 r += n; 2492 r += static_cast<uint64_t>(n);
1790 2493
1791 if (progress) { 2494 if (progress) {
1792 if (!progress(r, len)) { return false; } 2495 if (!progress(r, len)) { return false; }
@@ -1801,9 +2504,9 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
1801 uint64_t r = 0; 2504 uint64_t r = 0;
1802 while (r < len) { 2505 while (r < len) {
1803 auto read_len = static_cast<size_t>(len - r); 2506 auto read_len = static_cast<size_t>(len - r);
1804 auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ)); 2507 auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
1805 if (n <= 0) { return; } 2508 if (n <= 0) { return; }
1806 r += n; 2509 r += static_cast<uint64_t>(n);
1807 } 2510 }
1808} 2511}
1809 2512
@@ -1816,7 +2519,7 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
1816 } else if (n == 0) { 2519 } else if (n == 0) {
1817 return true; 2520 return true;
1818 } 2521 }
1819 if (!out(buf, n)) { return false; } 2522 if (!out(buf, static_cast<size_t>(n))) { return false; }
1820 } 2523 }
1821 2524
1822 return true; 2525 return true;
@@ -1830,9 +2533,17 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
1830 2533
1831 if (!line_reader.getline()) { return false; } 2534 if (!line_reader.getline()) { return false; }
1832 2535
1833 auto chunk_len = std::stoi(line_reader.ptr(), 0, 16); 2536 unsigned long chunk_len;
2537 while (true) {
2538 char *end_ptr;
2539
2540 chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
2541
2542 if (end_ptr == line_reader.ptr()) { return false; }
2543 if (chunk_len == ULONG_MAX) { return false; }
2544
2545 if (chunk_len == 0) { break; }
1834 2546
1835 while (chunk_len > 0) {
1836 if (!read_content_with_length(strm, chunk_len, nullptr, out)) { 2547 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
1837 return false; 2548 return false;
1838 } 2549 }
@@ -1842,8 +2553,6 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
1842 if (strcmp(line_reader.ptr(), "\r\n")) { break; } 2553 if (strcmp(line_reader.ptr(), "\r\n")) { break; }
1843 2554
1844 if (!line_reader.getline()) { return false; } 2555 if (!line_reader.getline()) { return false; }
1845
1846 chunk_len = std::stoi(line_reader.ptr(), 0, 16);
1847 } 2556 }
1848 2557
1849 if (chunk_len == 0) { 2558 if (chunk_len == 0) {
@@ -1860,62 +2569,86 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
1860 "chunked"); 2569 "chunked");
1861} 2570}
1862 2571
1863template <typename T> 2572template <typename T, typename U>
1864bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, 2573bool prepare_content_receiver(T &x, int &status, ContentReceiver receiver,
1865 Progress progress, ContentReceiver receiver) { 2574 bool decompress, U callback) {
1866 2575 if (decompress) {
1867 ContentReceiver out = [&](const char *buf, size_t n) { 2576 std::string encoding = x.get_header_value("Content-Encoding");
1868 return receiver(buf, n); 2577 std::shared_ptr<decompressor> decompressor;
1869 };
1870 2578
2579 if (encoding.find("gzip") != std::string::npos ||
2580 encoding.find("deflate") != std::string::npos) {
1871#ifdef CPPHTTPLIB_ZLIB_SUPPORT 2581#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1872 decompressor decompressor; 2582 decompressor = std::make_shared<gzip_decompressor>();
1873
1874 if (!decompressor.is_valid()) {
1875 status = 500;
1876 return false;
1877 }
1878
1879 if (x.get_header_value("Content-Encoding") == "gzip") {
1880 out = [&](const char *buf, size_t n) {
1881 return decompressor.decompress(
1882 buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); });
1883 };
1884 }
1885#else 2583#else
1886 if (x.get_header_value("Content-Encoding") == "gzip") { 2584 status = 415;
1887 status = 415; 2585 return false;
1888 return false;
1889 }
1890#endif 2586#endif
2587 } else if (encoding.find("br") != std::string::npos) {
2588#ifdef CPPHTTPLIB_BROTLI_SUPPORT
2589 decompressor = std::make_shared<brotli_decompressor>();
2590#else
2591 status = 415;
2592 return false;
2593#endif
2594 }
1891 2595
1892 auto ret = true; 2596 if (decompressor) {
1893 auto exceed_payload_max_length = false; 2597 if (decompressor->is_valid()) {
1894 2598 ContentReceiver out = [&](const char *buf, size_t n) {
1895 if (is_chunked_transfer_encoding(x.headers)) { 2599 return decompressor->decompress(
1896 ret = read_content_chunked(strm, out); 2600 buf, n,
1897 } else if (!has_header(x.headers, "Content-Length")) { 2601 [&](const char *buf, size_t n) { return receiver(buf, n); });
1898 ret = read_content_without_length(strm, out); 2602 };
1899 } else { 2603 return callback(out);
1900 auto len = get_header_value_uint64(x.headers, "Content-Length", 0); 2604 } else {
1901 if (len > payload_max_length) { 2605 status = 500;
1902 exceed_payload_max_length = true; 2606 return false;
1903 skip_content_with_length(strm, len); 2607 }
1904 ret = false;
1905 } else if (len > 0) {
1906 ret = read_content_with_length(strm, len, progress, out);
1907 } 2608 }
1908 } 2609 }
1909 2610
1910 if (!ret) { status = exceed_payload_max_length ? 413 : 400; } 2611 ContentReceiver out = [&](const char *buf, size_t n) {
2612 return receiver(buf, n);
2613 };
2614 return callback(out);
2615}
2616
2617template <typename T>
2618bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
2619 Progress progress, ContentReceiver receiver,
2620 bool decompress) {
2621 return prepare_content_receiver(
2622 x, status, receiver, decompress, [&](const ContentReceiver &out) {
2623 auto ret = true;
2624 auto exceed_payload_max_length = false;
2625
2626 if (is_chunked_transfer_encoding(x.headers)) {
2627 ret = read_content_chunked(strm, out);
2628 } else if (!has_header(x.headers, "Content-Length")) {
2629 ret = read_content_without_length(strm, out);
2630 } else {
2631 auto len = get_header_value<uint64_t>(x.headers, "Content-Length");
2632 if (len > payload_max_length) {
2633 exceed_payload_max_length = true;
2634 skip_content_with_length(strm, len);
2635 ret = false;
2636 } else if (len > 0) {
2637 ret = read_content_with_length(strm, len, progress, out);
2638 }
2639 }
1911 2640
1912 return ret; 2641 if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
2642 return ret;
2643 });
1913} 2644}
1914 2645
1915template <typename T> 2646template <typename T>
1916inline int write_headers(Stream &strm, const T &info, const Headers &headers) { 2647inline ssize_t write_headers(Stream &strm, const T &info,
1917 auto write_len = 0; 2648 const Headers &headers) {
2649 ssize_t write_len = 0;
1918 for (const auto &x : info.headers) { 2650 for (const auto &x : info.headers) {
2651 if (x.first == "EXCEPTION_WHAT") { continue; }
1919 auto len = 2652 auto len =
1920 strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); 2653 strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
1921 if (len < 0) { return len; } 2654 if (len < 0) { return len; }
@@ -1933,57 +2666,150 @@ inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
1933 return write_len; 2666 return write_len;
1934} 2667}
1935 2668
2669inline bool write_data(Stream &strm, const char *d, size_t l) {
2670 size_t offset = 0;
2671 while (offset < l) {
2672 auto length = strm.write(d + offset, l - offset);
2673 if (length < 0) { return false; }
2674 offset += static_cast<size_t>(length);
2675 }
2676 return true;
2677}
2678
2679template <typename T>
1936inline ssize_t write_content(Stream &strm, ContentProvider content_provider, 2680inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
1937 size_t offset, size_t length) { 2681 size_t offset, size_t length, T is_shutting_down) {
1938 size_t begin_offset = offset; 2682 size_t begin_offset = offset;
1939 size_t end_offset = offset + length; 2683 size_t end_offset = offset + length;
1940 while (offset < end_offset) { 2684 auto ok = true;
1941 ssize_t written_length = 0; 2685 DataSink data_sink;
1942 2686
1943 DataSink data_sink; 2687 data_sink.write = [&](const char *d, size_t l) {
1944 data_sink.write = [&](const char *d, size_t l) { 2688 if (ok) {
1945 offset += l; 2689 offset += l;
1946 written_length = strm.write(d, l); 2690 if (!write_data(strm, d, l)) { ok = false; }
1947 }; 2691 }
1948 data_sink.done = [&](void) { written_length = -1; }; 2692 };
1949 data_sink.is_writable = [&](void) { return strm.is_writable(); };
1950 2693
1951 content_provider(offset, end_offset - offset, data_sink); 2694 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
1952 if (written_length < 0) { return written_length; } 2695
2696 while (offset < end_offset && !is_shutting_down()) {
2697 if (!content_provider(offset, end_offset - offset, data_sink)) {
2698 return -1;
2699 }
2700 if (!ok) { return -1; }
1953 } 2701 }
2702
1954 return static_cast<ssize_t>(offset - begin_offset); 2703 return static_cast<ssize_t>(offset - begin_offset);
1955} 2704}
1956 2705
1957template <typename T> 2706template <typename T>
2707inline ssize_t write_content_without_length(Stream &strm,
2708 ContentProvider content_provider,
2709 T is_shutting_down) {
2710 size_t offset = 0;
2711 auto data_available = true;
2712 auto ok = true;
2713 DataSink data_sink;
2714
2715 data_sink.write = [&](const char *d, size_t l) {
2716 if (ok) {
2717 offset += l;
2718 if (!write_data(strm, d, l)) { ok = false; }
2719 }
2720 };
2721
2722 data_sink.done = [&](void) { data_available = false; };
2723
2724 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
2725
2726 while (data_available && !is_shutting_down()) {
2727 if (!content_provider(offset, 0, data_sink)) { return -1; }
2728 if (!ok) { return -1; }
2729 }
2730
2731 return static_cast<ssize_t>(offset);
2732}
2733
2734template <typename T, typename U>
1958inline ssize_t write_content_chunked(Stream &strm, 2735inline ssize_t write_content_chunked(Stream &strm,
1959 ContentProvider content_provider, 2736 ContentProvider content_provider,
1960 T is_shutting_down) { 2737 T is_shutting_down, U &compressor) {
1961 size_t offset = 0; 2738 size_t offset = 0;
1962 auto data_available = true; 2739 auto data_available = true;
1963 ssize_t total_written_length = 0; 2740 ssize_t total_written_length = 0;
1964 while (data_available && !is_shutting_down()) { 2741 auto ok = true;
1965 ssize_t written_length = 0; 2742 DataSink data_sink;
2743
2744 data_sink.write = [&](const char *d, size_t l) {
2745 if (!ok) { return; }
2746
2747 data_available = l > 0;
2748 offset += l;
2749
2750 std::string payload;
2751 if (!compressor.compress(d, l, false,
2752 [&](const char *data, size_t data_len) {
2753 payload.append(data, data_len);
2754 return true;
2755 })) {
2756 ok = false;
2757 return;
2758 }
1966 2759
1967 DataSink data_sink; 2760 if (!payload.empty()) {
1968 data_sink.write = [&](const char *d, size_t l) { 2761 // Emit chunked response header and footer for each chunk
1969 data_available = l > 0; 2762 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
1970 offset += l; 2763 if (write_data(strm, chunk.data(), chunk.size())) {
2764 total_written_length += chunk.size();
2765 } else {
2766 ok = false;
2767 return;
2768 }
2769 }
2770 };
1971 2771
2772 data_sink.done = [&](void) {
2773 if (!ok) { return; }
2774
2775 data_available = false;
2776
2777 std::string payload;
2778 if (!compressor.compress(nullptr, 0, true,
2779 [&](const char *data, size_t data_len) {
2780 payload.append(data, data_len);
2781 return true;
2782 })) {
2783 ok = false;
2784 return;
2785 }
2786
2787 if (!payload.empty()) {
1972 // Emit chunked response header and footer for each chunk 2788 // Emit chunked response header and footer for each chunk
1973 auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; 2789 auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
1974 written_length = strm.write(chunk); 2790 if (write_data(strm, chunk.data(), chunk.size())) {
1975 }; 2791 total_written_length += chunk.size();
1976 data_sink.done = [&](void) { 2792 } else {
1977 data_available = false; 2793 ok = false;
1978 written_length = strm.write("0\r\n\r\n"); 2794 return;
1979 }; 2795 }
1980 data_sink.is_writable = [&](void) { return strm.is_writable(); }; 2796 }
2797
2798 static const std::string done_marker("0\r\n\r\n");
2799 if (write_data(strm, done_marker.data(), done_marker.size())) {
2800 total_written_length += done_marker.size();
2801 } else {
2802 ok = false;
2803 }
2804 };
1981 2805
1982 content_provider(offset, 0, data_sink); 2806 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
1983 2807
1984 if (written_length < 0) { return written_length; } 2808 while (data_available && !is_shutting_down()) {
1985 total_written_length += written_length; 2809 if (!content_provider(offset, 0, data_sink)) { return -1; }
2810 if (!ok) { return -1; }
1986 } 2811 }
2812
1987 return total_written_length; 2813 return total_written_length;
1988} 2814}
1989 2815
@@ -1994,6 +2820,12 @@ inline bool redirect(T &cli, const Request &req, Response &res,
1994 new_req.path = path; 2820 new_req.path = path;
1995 new_req.redirect_count -= 1; 2821 new_req.redirect_count -= 1;
1996 2822
2823 if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
2824 new_req.method = "GET";
2825 new_req.body.clear();
2826 new_req.headers.clear();
2827 }
2828
1997 Response new_res; 2829 Response new_res;
1998 2830
1999 auto ret = cli.send(new_req, new_res); 2831 auto ret = cli.send(new_req, new_res);
@@ -2001,75 +2833,20 @@ inline bool redirect(T &cli, const Request &req, Response &res,
2001 return ret; 2833 return ret;
2002} 2834}
2003 2835
2004inline std::string encode_url(const std::string &s) { 2836inline std::string params_to_query_str(const Params &params) {
2005 std::string result; 2837 std::string query;
2006
2007 for (auto i = 0; s[i]; i++) {
2008 switch (s[i]) {
2009 case ' ': result += "%20"; break;
2010 case '+': result += "%2B"; break;
2011 case '\r': result += "%0D"; break;
2012 case '\n': result += "%0A"; break;
2013 case '\'': result += "%27"; break;
2014 case ',': result += "%2C"; break;
2015 // case ':': result += "%3A"; break; // ok? probably...
2016 case ';': result += "%3B"; break;
2017 default:
2018 auto c = static_cast<uint8_t>(s[i]);
2019 if (c >= 0x80) {
2020 result += '%';
2021 char hex[4];
2022 size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
2023 assert(len == 2);
2024 result.append(hex, len);
2025 } else {
2026 result += s[i];
2027 }
2028 break;
2029 }
2030 }
2031
2032 return result;
2033}
2034
2035inline std::string decode_url(const std::string &s) {
2036 std::string result;
2037 2838
2038 for (size_t i = 0; i < s.size(); i++) { 2839 for (auto it = params.begin(); it != params.end(); ++it) {
2039 if (s[i] == '%' && i + 1 < s.size()) { 2840 if (it != params.begin()) { query += "&"; }
2040 if (s[i + 1] == 'u') { 2841 query += it->first;
2041 int val = 0; 2842 query += "=";
2042 if (from_hex_to_i(s, i + 2, 4, val)) { 2843 query += encode_url(it->second);
2043 // 4 digits Unicode codes
2044 char buff[4];
2045 size_t len = to_utf8(val, buff);
2046 if (len > 0) { result.append(buff, len); }
2047 i += 5; // 'u0000'
2048 } else {
2049 result += s[i];
2050 }
2051 } else {
2052 int val = 0;
2053 if (from_hex_to_i(s, i + 1, 2, val)) {
2054 // 2 digits hex codes
2055 result += static_cast<char>(val);
2056 i += 2; // '00'
2057 } else {
2058 result += s[i];
2059 }
2060 }
2061 } else if (s[i] == '+') {
2062 result += ' ';
2063 } else {
2064 result += s[i];
2065 }
2066 } 2844 }
2067 2845 return query;
2068 return result;
2069} 2846}
2070 2847
2071inline void parse_query_text(const std::string &s, Params &params) { 2848inline void parse_query_text(const std::string &s, Params &params) {
2072 split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { 2849 split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
2073 std::string key; 2850 std::string key;
2074 std::string val; 2851 std::string val;
2075 split(b, e, '=', [&](const char *b2, const char *e2) { 2852 split(b, e, '=', [&](const char *b2, const char *e2) {
@@ -2079,7 +2856,10 @@ inline void parse_query_text(const std::string &s, Params &params) {
2079 val.assign(b2, e2); 2856 val.assign(b2, e2);
2080 } 2857 }
2081 }); 2858 });
2082 params.emplace(key, decode_url(val)); 2859
2860 if (!key.empty()) {
2861 params.emplace(decode_url(key, true), decode_url(val, true));
2862 }
2083 }); 2863 });
2084} 2864}
2085 2865
@@ -2087,17 +2867,20 @@ inline bool parse_multipart_boundary(const std::string &content_type,
2087 std::string &boundary) { 2867 std::string &boundary) {
2088 auto pos = content_type.find("boundary="); 2868 auto pos = content_type.find("boundary=");
2089 if (pos == std::string::npos) { return false; } 2869 if (pos == std::string::npos) { return false; }
2090
2091 boundary = content_type.substr(pos + 9); 2870 boundary = content_type.substr(pos + 9);
2092 return true; 2871 if (boundary.length() >= 2 && boundary.front() == '"' &&
2872 boundary.back() == '"') {
2873 boundary = boundary.substr(1, boundary.size() - 2);
2874 }
2875 return !boundary.empty();
2093} 2876}
2094 2877
2095inline bool parse_range_header(const std::string &s, Ranges &ranges) { 2878inline bool parse_range_header(const std::string &s, Ranges &ranges) {
2096 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); 2879 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
2097 std::smatch m; 2880 std::smatch m;
2098 if (std::regex_match(s, m, re_first_range)) { 2881 if (std::regex_match(s, m, re_first_range)) {
2099 auto pos = m.position(1); 2882 auto pos = static_cast<size_t>(m.position(1));
2100 auto len = m.length(1); 2883 auto len = static_cast<size_t>(m.length(1));
2101 bool all_valid_ranges = true; 2884 bool all_valid_ranges = true;
2102 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { 2885 split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
2103 if (!all_valid_ranges) return; 2886 if (!all_valid_ranges) return;
@@ -2128,21 +2911,21 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
2128 2911
2129class MultipartFormDataParser { 2912class MultipartFormDataParser {
2130public: 2913public:
2131 MultipartFormDataParser() {} 2914 MultipartFormDataParser() = default;
2132 2915
2133 void set_boundary(const std::string &boundary) { boundary_ = boundary; } 2916 void set_boundary(std::string &&boundary) { boundary_ = boundary; }
2134 2917
2135 bool is_valid() const { return is_valid_; } 2918 bool is_valid() const { return is_valid_; }
2136 2919
2137 template <typename T, typename U> 2920 template <typename T, typename U>
2138 bool parse(const char *buf, size_t n, T content_callback, U header_callback) { 2921 bool parse(const char *buf, size_t n, T content_callback, U header_callback) {
2139 static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)",
2140 std::regex_constants::icase);
2141 2922
2142 static const std::regex re_content_disposition( 2923 static const std::regex re_content_disposition(
2143 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" 2924 "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
2144 "\"(.*?)\")?\\s*$", 2925 "\"(.*?)\")?\\s*$",
2145 std::regex_constants::icase); 2926 std::regex_constants::icase);
2927 static const std::string dash_ = "--";
2928 static const std::string crlf_ = "\r\n";
2146 2929
2147 buf_.append(buf, n); // TODO: performance improvement 2930 buf_.append(buf, n); // TODO: performance improvement
2148 2931
@@ -2152,10 +2935,7 @@ public:
2152 auto pattern = dash_ + boundary_ + crlf_; 2935 auto pattern = dash_ + boundary_ + crlf_;
2153 if (pattern.size() > buf_.size()) { return true; } 2936 if (pattern.size() > buf_.size()) { return true; }
2154 auto pos = buf_.find(pattern); 2937 auto pos = buf_.find(pattern);
2155 if (pos != 0) { 2938 if (pos != 0) { return false; }
2156 is_done_ = true;
2157 return false;
2158 }
2159 buf_.erase(0, pattern.size()); 2939 buf_.erase(0, pattern.size());
2160 off_ += pattern.size(); 2940 off_ += pattern.size();
2161 state_ = 1; 2941 state_ = 1;
@@ -2173,7 +2953,6 @@ public:
2173 if (pos == 0) { 2953 if (pos == 0) {
2174 if (!header_callback(file_)) { 2954 if (!header_callback(file_)) {
2175 is_valid_ = false; 2955 is_valid_ = false;
2176 is_done_ = false;
2177 return false; 2956 return false;
2178 } 2957 }
2179 buf_.erase(0, crlf_.size()); 2958 buf_.erase(0, crlf_.size());
@@ -2182,12 +2961,13 @@ public:
2182 break; 2961 break;
2183 } 2962 }
2184 2963
2185 auto header = buf_.substr(0, pos); 2964 static const std::string header_name = "content-type:";
2186 { 2965 const auto header = buf_.substr(0, pos);
2966 if (start_with(header, header_name)) {
2967 file_.content_type = trim_copy(header.substr(header_name.size()));
2968 } else {
2187 std::smatch m; 2969 std::smatch m;
2188 if (std::regex_match(header, m, re_content_type)) { 2970 if (std::regex_match(header, m, re_content_disposition)) {
2189 file_.content_type = m[1];
2190 } else if (std::regex_match(header, m, re_content_disposition)) {
2191 file_.name = m[1]; 2971 file_.name = m[1];
2192 file_.filename = m[2]; 2972 file_.filename = m[2];
2193 } 2973 }
@@ -2197,6 +2977,7 @@ public:
2197 off_ += pos + crlf_.size(); 2977 off_ += pos + crlf_.size();
2198 pos = buf_.find(crlf_); 2978 pos = buf_.find(crlf_);
2199 } 2979 }
2980 if (state_ != 3) { return true; }
2200 break; 2981 break;
2201 } 2982 }
2202 case 3: { // Body 2983 case 3: { // Body
@@ -2205,10 +2986,17 @@ public:
2205 if (pattern.size() > buf_.size()) { return true; } 2986 if (pattern.size() > buf_.size()) { return true; }
2206 2987
2207 auto pos = buf_.find(pattern); 2988 auto pos = buf_.find(pattern);
2208 if (pos == std::string::npos) { pos = buf_.size(); } 2989 if (pos == std::string::npos) {
2990 pos = buf_.size();
2991 while (pos > 0) {
2992 auto c = buf_[pos - 1];
2993 if (c != '\r' && c != '\n' && c != '-') { break; }
2994 pos--;
2995 }
2996 }
2997
2209 if (!content_callback(buf_.data(), pos)) { 2998 if (!content_callback(buf_.data(), pos)) {
2210 is_valid_ = false; 2999 is_valid_ = false;
2211 is_done_ = false;
2212 return false; 3000 return false;
2213 } 3001 }
2214 3002
@@ -2224,7 +3012,6 @@ public:
2224 if (pos != std::string::npos) { 3012 if (pos != std::string::npos) {
2225 if (!content_callback(buf_.data(), pos)) { 3013 if (!content_callback(buf_.data(), pos)) {
2226 is_valid_ = false; 3014 is_valid_ = false;
2227 is_done_ = false;
2228 return false; 3015 return false;
2229 } 3016 }
2230 3017
@@ -2234,7 +3021,6 @@ public:
2234 } else { 3021 } else {
2235 if (!content_callback(buf_.data(), pattern.size())) { 3022 if (!content_callback(buf_.data(), pattern.size())) {
2236 is_valid_ = false; 3023 is_valid_ = false;
2237 is_done_ = false;
2238 return false; 3024 return false;
2239 } 3025 }
2240 3026
@@ -2246,20 +3032,19 @@ public:
2246 } 3032 }
2247 case 4: { // Boundary 3033 case 4: { // Boundary
2248 if (crlf_.size() > buf_.size()) { return true; } 3034 if (crlf_.size() > buf_.size()) { return true; }
2249 if (buf_.find(crlf_) == 0) { 3035 if (buf_.compare(0, crlf_.size(), crlf_) == 0) {
2250 buf_.erase(0, crlf_.size()); 3036 buf_.erase(0, crlf_.size());
2251 off_ += crlf_.size(); 3037 off_ += crlf_.size();
2252 state_ = 1; 3038 state_ = 1;
2253 } else { 3039 } else {
2254 auto pattern = dash_ + crlf_; 3040 auto pattern = dash_ + crlf_;
2255 if (pattern.size() > buf_.size()) { return true; } 3041 if (pattern.size() > buf_.size()) { return true; }
2256 if (buf_.find(pattern) == 0) { 3042 if (buf_.compare(0, pattern.size(), pattern) == 0) {
2257 buf_.erase(0, pattern.size()); 3043 buf_.erase(0, pattern.size());
2258 off_ += pattern.size(); 3044 off_ += pattern.size();
2259 is_valid_ = true; 3045 is_valid_ = true;
2260 state_ = 5; 3046 state_ = 5;
2261 } else { 3047 } else {
2262 is_done_ = true;
2263 return true; 3048 return true;
2264 } 3049 }
2265 } 3050 }
@@ -2282,14 +3067,11 @@ private:
2282 file_.content_type.clear(); 3067 file_.content_type.clear();
2283 } 3068 }
2284 3069
2285 const std::string dash_ = "--";
2286 const std::string crlf_ = "\r\n";
2287 std::string boundary_; 3070 std::string boundary_;
2288 3071
2289 std::string buf_; 3072 std::string buf_;
2290 size_t state_ = 0; 3073 size_t state_ = 0;
2291 size_t is_valid_ = false; 3074 bool is_valid_ = false;
2292 size_t is_done_ = false;
2293 size_t off_ = 0; 3075 size_t off_ = 0;
2294 MultipartFormData file_; 3076 MultipartFormData file_;
2295}; 3077};
@@ -2329,12 +3111,14 @@ get_range_offset_and_length(const Request &req, size_t content_length,
2329 return std::make_pair(0, content_length); 3111 return std::make_pair(0, content_length);
2330 } 3112 }
2331 3113
3114 auto slen = static_cast<ssize_t>(content_length);
3115
2332 if (r.first == -1) { 3116 if (r.first == -1) {
2333 r.first = content_length - r.second; 3117 r.first = slen - r.second;
2334 r.second = content_length - 1; 3118 r.second = slen - 1;
2335 } 3119 }
2336 3120
2337 if (r.second == -1) { r.second = content_length - 1; } 3121 if (r.second == -1) { r.second = slen - 1; }
2338 3122
2339 return std::make_pair(r.first, r.second - r.first + 1); 3123 return std::make_pair(r.first, r.second - r.first + 1);
2340} 3124}
@@ -2420,16 +3204,19 @@ get_multipart_ranges_data_length(const Request &req, Response &res,
2420 return data_length; 3204 return data_length;
2421} 3205}
2422 3206
3207template <typename T>
2423inline bool write_multipart_ranges_data(Stream &strm, const Request &req, 3208inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
2424 Response &res, 3209 Response &res,
2425 const std::string &boundary, 3210 const std::string &boundary,
2426 const std::string &content_type) { 3211 const std::string &content_type,
3212 T is_shutting_down) {
2427 return process_multipart_ranges_data( 3213 return process_multipart_ranges_data(
2428 req, res, boundary, content_type, 3214 req, res, boundary, content_type,
2429 [&](const std::string &token) { strm.write(token); }, 3215 [&](const std::string &token) { strm.write(token); },
2430 [&](const char *token) { strm.write(token); }, 3216 [&](const char *token) { strm.write(token); },
2431 [&](size_t offset, size_t length) { 3217 [&](size_t offset, size_t length) {
2432 return write_content(strm, res.content_provider, offset, length) >= 0; 3218 return write_content(strm, res.content_provider_, offset, length,
3219 is_shutting_down) >= 0;
2433 }); 3220 });
2434} 3221}
2435 3222
@@ -2438,20 +3225,31 @@ get_range_offset_and_length(const Request &req, const Response &res,
2438 size_t index) { 3225 size_t index) {
2439 auto r = req.ranges[index]; 3226 auto r = req.ranges[index];
2440 3227
2441 if (r.second == -1) { r.second = res.content_length - 1; } 3228 if (r.second == -1) {
3229 r.second = static_cast<ssize_t>(res.content_length_) - 1;
3230 }
2442 3231
2443 return std::make_pair(r.first, r.second - r.first + 1); 3232 return std::make_pair(r.first, r.second - r.first + 1);
2444} 3233}
2445 3234
2446inline bool expect_content(const Request &req) { 3235inline bool expect_content(const Request &req) {
2447 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || 3236 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
2448 req.method == "PRI") { 3237 req.method == "PRI" || req.method == "DELETE") {
2449 return true; 3238 return true;
2450 } 3239 }
2451 // TODO: check if Content-Length is set 3240 // TODO: check if Content-Length is set
2452 return false; 3241 return false;
2453} 3242}
2454 3243
3244inline bool has_crlf(const char *s) {
3245 auto p = s;
3246 while (*p) {
3247 if (*p == '\r' || *p == '\n') { return true; }
3248 p++;
3249 }
3250 return false;
3251}
3252
2455#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 3253#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2456template <typename CTX, typename Init, typename Update, typename Final> 3254template <typename CTX, typename Init, typename Update, typename Final>
2457inline std::string message_digest(const std::string &s, Init init, 3255inline std::string message_digest(const std::string &s, Init init,
@@ -2489,6 +3287,33 @@ inline std::string SHA_512(const std::string &s) {
2489#endif 3287#endif
2490 3288
2491#ifdef _WIN32 3289#ifdef _WIN32
3290#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3291// NOTE: This code came up with the following stackoverflow post:
3292// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
3293inline bool load_system_certs_on_windows(X509_STORE *store) {
3294 auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
3295
3296 if (!hStore) { return false; }
3297
3298 PCCERT_CONTEXT pContext = NULL;
3299 while (pContext = CertEnumCertificatesInStore(hStore, pContext)) {
3300 auto encoded_cert =
3301 static_cast<const unsigned char *>(pContext->pbCertEncoded);
3302
3303 auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
3304 if (x509) {
3305 X509_STORE_add_cert(store, x509);
3306 X509_free(x509);
3307 }
3308 }
3309
3310 CertFreeCertificateContext(pContext);
3311 CertCloseStore(hStore, 0);
3312
3313 return true;
3314}
3315#endif
3316
2492class WSInit { 3317class WSInit {
2493public: 3318public:
2494 WSInit() { 3319 WSInit() {
@@ -2502,31 +3327,6 @@ public:
2502static WSInit wsinit_; 3327static WSInit wsinit_;
2503#endif 3328#endif
2504 3329
2505} // namespace detail
2506
2507// Header utilities
2508inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
2509 std::string field = "bytes=";
2510 auto i = 0;
2511 for (auto r : ranges) {
2512 if (i != 0) { field += ", "; }
2513 if (r.first != -1) { field += std::to_string(r.first); }
2514 field += '-';
2515 if (r.second != -1) { field += std::to_string(r.second); }
2516 i++;
2517 }
2518 return std::make_pair("Range", field);
2519}
2520
2521inline std::pair<std::string, std::string>
2522make_basic_authentication_header(const std::string &username,
2523 const std::string &password,
2524 bool is_proxy = false) {
2525 auto field = "Basic " + detail::base64_encode(username + ":" + password);
2526 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2527 return std::make_pair(key, field);
2528}
2529
2530#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 3330#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2531inline std::pair<std::string, std::string> make_digest_authentication_header( 3331inline std::pair<std::string, std::string> make_digest_authentication_header(
2532 const Request &req, const std::map<std::string, std::string> &auth, 3332 const Request &req, const std::map<std::string, std::string> &auth,
@@ -2566,17 +3366,18 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
2566 ":" + qop + ":" + H(A2)); 3366 ":" + qop + ":" + H(A2));
2567 } 3367 }
2568 3368
2569 auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") + 3369 auto field = "Digest username=\"" + username + "\", realm=\"" +
2570 "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path + 3370 auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
2571 "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + nc + 3371 "\", uri=\"" + req.path + "\", algorithm=" + algo +
2572 "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\""; 3372 ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce +
3373 "\", response=\"" + response + "\"";
2573 3374
2574 auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; 3375 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
2575 return std::make_pair(key, field); 3376 return std::make_pair(key, field);
2576} 3377}
2577#endif 3378#endif
2578 3379
2579inline bool parse_www_authenticate(const httplib::Response &res, 3380inline bool parse_www_authenticate(const Response &res,
2580 std::map<std::string, std::string> &auth, 3381 std::map<std::string, std::string> &auth,
2581 bool is_proxy) { 3382 bool is_proxy) {
2582 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; 3383 auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
@@ -2593,9 +3394,13 @@ inline bool parse_www_authenticate(const httplib::Response &res,
2593 auto beg = std::sregex_iterator(s.begin(), s.end(), re); 3394 auto beg = std::sregex_iterator(s.begin(), s.end(), re);
2594 for (auto i = beg; i != std::sregex_iterator(); ++i) { 3395 for (auto i = beg; i != std::sregex_iterator(); ++i) {
2595 auto m = *i; 3396 auto m = *i;
2596 auto key = s.substr(m.position(1), m.length(1)); 3397 auto key = s.substr(static_cast<size_t>(m.position(1)),
2597 auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2)) 3398 static_cast<size_t>(m.length(1)));
2598 : s.substr(m.position(3), m.length(3)); 3399 auto val = m.length(2) > 0
3400 ? s.substr(static_cast<size_t>(m.position(2)),
3401 static_cast<size_t>(m.length(2)))
3402 : s.substr(static_cast<size_t>(m.position(3)),
3403 static_cast<size_t>(m.length(3)));
2599 auth[key] = val; 3404 auth[key] = val;
2600 } 3405 }
2601 return true; 3406 return true;
@@ -2612,13 +3417,60 @@ inline std::string random_string(size_t length) {
2612 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 3417 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2613 "abcdefghijklmnopqrstuvwxyz"; 3418 "abcdefghijklmnopqrstuvwxyz";
2614 const size_t max_index = (sizeof(charset) - 1); 3419 const size_t max_index = (sizeof(charset) - 1);
2615 return charset[rand() % max_index]; 3420 return charset[static_cast<size_t>(rand()) % max_index];
2616 }; 3421 };
2617 std::string str(length, 0); 3422 std::string str(length, 0);
2618 std::generate_n(str.begin(), length, randchar); 3423 std::generate_n(str.begin(), length, randchar);
2619 return str; 3424 return str;
2620} 3425}
2621 3426
3427class ContentProviderAdapter {
3428public:
3429 explicit ContentProviderAdapter(
3430 ContentProviderWithoutLength &&content_provider)
3431 : content_provider_(content_provider) {}
3432
3433 bool operator()(size_t offset, size_t, DataSink &sink) {
3434 return content_provider_(offset, sink);
3435 }
3436
3437private:
3438 ContentProviderWithoutLength content_provider_;
3439};
3440
3441} // namespace detail
3442
3443// Header utilities
3444inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
3445 std::string field = "bytes=";
3446 auto i = 0;
3447 for (auto r : ranges) {
3448 if (i != 0) { field += ", "; }
3449 if (r.first != -1) { field += std::to_string(r.first); }
3450 field += '-';
3451 if (r.second != -1) { field += std::to_string(r.second); }
3452 i++;
3453 }
3454 return std::make_pair("Range", field);
3455}
3456
3457inline std::pair<std::string, std::string>
3458make_basic_authentication_header(const std::string &username,
3459 const std::string &password,
3460 bool is_proxy = false) {
3461 auto field = "Basic " + detail::base64_encode(username + ":" + password);
3462 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3463 return std::make_pair(key, field);
3464}
3465
3466inline std::pair<std::string, std::string>
3467make_bearer_token_authentication_header(const std::string &token,
3468 bool is_proxy = false) {
3469 auto field = "Bearer " + token;
3470 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3471 return std::make_pair(key, field);
3472}
3473
2622// Request implementation 3474// Request implementation
2623inline bool Request::has_header(const char *key) const { 3475inline bool Request::has_header(const char *key) const {
2624 return detail::has_header(headers, key); 3476 return detail::has_header(headers, key);
@@ -2628,17 +3480,26 @@ inline std::string Request::get_header_value(const char *key, size_t id) const {
2628 return detail::get_header_value(headers, key, id, ""); 3480 return detail::get_header_value(headers, key, id, "");
2629} 3481}
2630 3482
3483template <typename T>
3484inline T Request::get_header_value(const char *key, size_t id) const {
3485 return detail::get_header_value<T>(headers, key, id, 0);
3486}
3487
2631inline size_t Request::get_header_value_count(const char *key) const { 3488inline size_t Request::get_header_value_count(const char *key) const {
2632 auto r = headers.equal_range(key); 3489 auto r = headers.equal_range(key);
2633 return std::distance(r.first, r.second); 3490 return static_cast<size_t>(std::distance(r.first, r.second));
2634} 3491}
2635 3492
2636inline void Request::set_header(const char *key, const char *val) { 3493inline void Request::set_header(const char *key, const char *val) {
2637 headers.emplace(key, val); 3494 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
3495 headers.emplace(key, val);
3496 }
2638} 3497}
2639 3498
2640inline void Request::set_header(const char *key, const std::string &val) { 3499inline void Request::set_header(const char *key, const std::string &val) {
2641 headers.emplace(key, val); 3500 if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
3501 headers.emplace(key, val);
3502 }
2642} 3503}
2643 3504
2644inline bool Request::has_param(const char *key) const { 3505inline bool Request::has_param(const char *key) const {
@@ -2646,15 +3507,16 @@ inline bool Request::has_param(const char *key) const {
2646} 3507}
2647 3508
2648inline std::string Request::get_param_value(const char *key, size_t id) const { 3509inline std::string Request::get_param_value(const char *key, size_t id) const {
2649 auto it = params.find(key); 3510 auto rng = params.equal_range(key);
2650 std::advance(it, id); 3511 auto it = rng.first;
2651 if (it != params.end()) { return it->second; } 3512 std::advance(it, static_cast<ssize_t>(id));
3513 if (it != rng.second) { return it->second; }
2652 return std::string(); 3514 return std::string();
2653} 3515}
2654 3516
2655inline size_t Request::get_param_value_count(const char *key) const { 3517inline size_t Request::get_param_value_count(const char *key) const {
2656 auto r = params.equal_range(key); 3518 auto r = params.equal_range(key);
2657 return std::distance(r.first, r.second); 3519 return static_cast<size_t>(std::distance(r.first, r.second));
2658} 3520}
2659 3521
2660inline bool Request::is_multipart_form_data() const { 3522inline bool Request::is_multipart_form_data() const {
@@ -2682,22 +3544,41 @@ inline std::string Response::get_header_value(const char *key,
2682 return detail::get_header_value(headers, key, id, ""); 3544 return detail::get_header_value(headers, key, id, "");
2683} 3545}
2684 3546
3547template <typename T>
3548inline T Response::get_header_value(const char *key, size_t id) const {
3549 return detail::get_header_value<T>(headers, key, id, 0);
3550}
3551
2685inline size_t Response::get_header_value_count(const char *key) const { 3552inline size_t Response::get_header_value_count(const char *key) const {
2686 auto r = headers.equal_range(key); 3553 auto r = headers.equal_range(key);
2687 return std::distance(r.first, r.second); 3554 return static_cast<size_t>(std::distance(r.first, r.second));
2688} 3555}
2689 3556
2690inline void Response::set_header(const char *key, const char *val) { 3557inline void Response::set_header(const char *key, const char *val) {
2691 headers.emplace(key, val); 3558 if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
3559 headers.emplace(key, val);
3560 }
2692} 3561}
2693 3562
2694inline void Response::set_header(const char *key, const std::string &val) { 3563inline void Response::set_header(const char *key, const std::string &val) {
2695 headers.emplace(key, val); 3564 if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
3565 headers.emplace(key, val);
3566 }
3567}
3568
3569inline void Response::set_redirect(const char *url, int stat) {
3570 if (!detail::has_crlf(url)) {
3571 set_header("Location", url);
3572 if (300 <= stat && stat < 400) {
3573 this->status = stat;
3574 } else {
3575 this->status = 302;
3576 }
3577 }
2696} 3578}
2697 3579
2698inline void Response::set_redirect(const char *url) { 3580inline void Response::set_redirect(const std::string &url, int stat) {
2699 set_header("Location", url); 3581 set_redirect(url.c_str(), stat);
2700 status = 302;
2701} 3582}
2702 3583
2703inline void Response::set_content(const char *s, size_t n, 3584inline void Response::set_content(const char *s, size_t n,
@@ -2706,62 +3587,79 @@ inline void Response::set_content(const char *s, size_t n,
2706 set_header("Content-Type", content_type); 3587 set_header("Content-Type", content_type);
2707} 3588}
2708 3589
2709inline void Response::set_content(const std::string &s, 3590inline void Response::set_content(std::string s, const char *content_type) {
2710 const char *content_type) { 3591 body = std::move(s);
2711 body = s;
2712 set_header("Content-Type", content_type); 3592 set_header("Content-Type", content_type);
2713} 3593}
2714 3594
2715inline void Response::set_content_provider( 3595inline void
2716 size_t in_length, 3596Response::set_content_provider(size_t in_length, const char *content_type,
2717 std::function<void(size_t offset, size_t length, DataSink &sink)> provider, 3597 ContentProvider provider,
2718 std::function<void()> resource_releaser) { 3598 const std::function<void()> &resource_releaser) {
2719 assert(in_length > 0); 3599 assert(in_length > 0);
2720 content_length = in_length; 3600 set_header("Content-Type", content_type);
2721 content_provider = [provider](size_t offset, size_t length, DataSink &sink) { 3601 content_length_ = in_length;
2722 provider(offset, length, sink); 3602 content_provider_ = std::move(provider);
2723 }; 3603 content_provider_resource_releaser_ = resource_releaser;
2724 content_provider_resource_releaser = resource_releaser; 3604 is_chunked_content_provider = false;
3605}
3606
3607inline void
3608Response::set_content_provider(const char *content_type,
3609 ContentProviderWithoutLength provider,
3610 const std::function<void()> &resource_releaser) {
3611 set_header("Content-Type", content_type);
3612 content_length_ = 0;
3613 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
3614 content_provider_resource_releaser_ = resource_releaser;
3615 is_chunked_content_provider = false;
2725} 3616}
2726 3617
2727inline void Response::set_chunked_content_provider( 3618inline void Response::set_chunked_content_provider(
2728 std::function<void(size_t offset, DataSink &sink)> provider, 3619 const char *content_type, ContentProviderWithoutLength provider,
2729 std::function<void()> resource_releaser) { 3620 const std::function<void()> &resource_releaser) {
2730 content_length = 0; 3621 set_header("Content-Type", content_type);
2731 content_provider = [provider](size_t offset, size_t, DataSink &sink) { 3622 content_length_ = 0;
2732 provider(offset, sink); 3623 content_provider_ = detail::ContentProviderAdapter(std::move(provider));
2733 }; 3624 content_provider_resource_releaser_ = resource_releaser;
2734 content_provider_resource_releaser = resource_releaser; 3625 is_chunked_content_provider = true;
2735} 3626}
2736 3627
2737// Rstream implementation 3628// Rstream implementation
2738inline int Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); } 3629inline ssize_t Stream::write(const char *ptr) {
3630 return write(ptr, strlen(ptr));
3631}
2739 3632
2740inline int Stream::write(const std::string &s) { 3633inline ssize_t Stream::write(const std::string &s) {
2741 return write(s.data(), s.size()); 3634 return write(s.data(), s.size());
2742} 3635}
2743 3636
2744template <typename... Args> 3637template <typename... Args>
2745inline int Stream::write_format(const char *fmt, const Args &... args) { 3638inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
2746 std::array<char, 2048> buf; 3639 const auto bufsiz = 2048;
3640 std::array<char, bufsiz> buf;
2747 3641
2748#if defined(_MSC_VER) && _MSC_VER < 1900 3642#if defined(_MSC_VER) && _MSC_VER < 1900
2749 auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...); 3643 auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...);
2750#else 3644#else
2751 auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...); 3645 auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
2752#endif 3646#endif
2753 if (n <= 0) { return n; } 3647 if (sn <= 0) { return sn; }
2754 3648
2755 if (n >= static_cast<int>(buf.size()) - 1) { 3649 auto n = static_cast<size_t>(sn);
3650
3651 if (n >= buf.size() - 1) {
2756 std::vector<char> glowable_buf(buf.size()); 3652 std::vector<char> glowable_buf(buf.size());
2757 3653
2758 while (n >= static_cast<int>(glowable_buf.size() - 1)) { 3654 while (n >= glowable_buf.size() - 1) {
2759 glowable_buf.resize(glowable_buf.size() * 2); 3655 glowable_buf.resize(glowable_buf.size() * 2);
2760#if defined(_MSC_VER) && _MSC_VER < 1900 3656#if defined(_MSC_VER) && _MSC_VER < 1900
2761 n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), 3657 n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
2762 glowable_buf.size() - 1, fmt, args...); 3658 glowable_buf.size() - 1, fmt,
3659 args...));
2763#else 3660#else
2764 n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); 3661 n = static_cast<size_t>(
3662 snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
2765#endif 3663#endif
2766 } 3664 }
2767 return write(&glowable_buf[0], n); 3665 return write(&glowable_buf[0], n);
@@ -2774,32 +3672,53 @@ namespace detail {
2774 3672
2775// Socket stream implementation 3673// Socket stream implementation
2776inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, 3674inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
2777 time_t read_timeout_usec) 3675 time_t read_timeout_usec,
3676 time_t write_timeout_sec,
3677 time_t write_timeout_usec)
2778 : sock_(sock), read_timeout_sec_(read_timeout_sec), 3678 : sock_(sock), read_timeout_sec_(read_timeout_sec),
2779 read_timeout_usec_(read_timeout_usec) {} 3679 read_timeout_usec_(read_timeout_usec),
3680 write_timeout_sec_(write_timeout_sec),
3681 write_timeout_usec_(write_timeout_usec) {}
2780 3682
2781inline SocketStream::~SocketStream() {} 3683inline SocketStream::~SocketStream() {}
2782 3684
2783inline bool SocketStream::is_readable() const { 3685inline bool SocketStream::is_readable() const {
2784 return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; 3686 return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
2785} 3687}
2786 3688
2787inline bool SocketStream::is_writable() const { 3689inline bool SocketStream::is_writable() const {
2788 return detail::select_write(sock_, 0, 0) > 0; 3690 return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
2789} 3691}
2790 3692
2791inline int SocketStream::read(char *ptr, size_t size) { 3693inline ssize_t SocketStream::read(char *ptr, size_t size) {
2792 if (is_readable()) { return recv(sock_, ptr, static_cast<int>(size), 0); } 3694 if (!is_readable()) { return -1; }
2793 return -1; 3695
3696#ifdef _WIN32
3697 if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
3698 return -1;
3699 }
3700 return recv(sock_, ptr, static_cast<int>(size), 0);
3701#else
3702 return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); });
3703#endif
2794} 3704}
2795 3705
2796inline int SocketStream::write(const char *ptr, size_t size) { 3706inline ssize_t SocketStream::write(const char *ptr, size_t size) {
2797 if (is_writable()) { return send(sock_, ptr, static_cast<int>(size), 0); } 3707 if (!is_writable()) { return -1; }
2798 return -1; 3708
3709#ifdef _WIN32
3710 if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
3711 return -1;
3712 }
3713 return send(sock_, ptr, static_cast<int>(size), 0);
3714#else
3715 return handle_EINTR([&]() { return send(sock_, ptr, size, 0); });
3716#endif
2799} 3717}
2800 3718
2801inline std::string SocketStream::get_remote_addr() const { 3719inline void SocketStream::get_remote_ip_and_port(std::string &ip,
2802 return detail::get_remote_addr(sock_); 3720 int &port) const {
3721 return detail::get_remote_ip_and_port(sock_, ip, port);
2803} 3722}
2804 3723
2805// Buffer stream implementation 3724// Buffer stream implementation
@@ -2807,22 +3726,23 @@ inline bool BufferStream::is_readable() const { return true; }
2807 3726
2808inline bool BufferStream::is_writable() const { return true; } 3727inline bool BufferStream::is_writable() const { return true; }
2809 3728
2810inline int BufferStream::read(char *ptr, size_t size) { 3729inline ssize_t BufferStream::read(char *ptr, size_t size) {
2811#if defined(_MSC_VER) && _MSC_VER < 1900 3730#if defined(_MSC_VER) && _MSC_VER <= 1900
2812 int len_read = static_cast<int>(buffer._Copy_s(ptr, size, size, position)); 3731 auto len_read = buffer._Copy_s(ptr, size, size, position);
2813#else 3732#else
2814 int len_read = static_cast<int>(buffer.copy(ptr, size, position)); 3733 auto len_read = buffer.copy(ptr, size, position);
2815#endif 3734#endif
2816 position += len_read; 3735 position += static_cast<size_t>(len_read);
2817 return len_read; 3736 return static_cast<ssize_t>(len_read);
2818} 3737}
2819 3738
2820inline int BufferStream::write(const char *ptr, size_t size) { 3739inline ssize_t BufferStream::write(const char *ptr, size_t size) {
2821 buffer.append(ptr, size); 3740 buffer.append(ptr, size);
2822 return static_cast<int>(size); 3741 return static_cast<ssize_t>(size);
2823} 3742}
2824 3743
2825inline std::string BufferStream::get_remote_addr() const { return ""; } 3744inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
3745 int & /*port*/) const {}
2826 3746
2827inline const std::string &BufferStream::get_buffer() const { return buffer; } 3747inline const std::string &BufferStream::get_buffer() const { return buffer; }
2828 3748
@@ -2830,15 +3750,12 @@ inline const std::string &BufferStream::get_buffer() const { return buffer; }
2830 3750
2831// HTTP server implementation 3751// HTTP server implementation
2832inline Server::Server() 3752inline Server::Server()
2833 : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), 3753 : new_task_queue(
2834 read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND), 3754 [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
2835 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND), 3755 svr_sock_(INVALID_SOCKET), is_running_(false) {
2836 payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false),
2837 svr_sock_(INVALID_SOCKET) {
2838#ifndef _WIN32 3756#ifndef _WIN32
2839 signal(SIGPIPE, SIG_IGN); 3757 signal(SIGPIPE, SIG_IGN);
2840#endif 3758#endif
2841 new_task_queue = [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); };
2842} 3759}
2843 3760
2844inline Server::~Server() {} 3761inline Server::~Server() {}
@@ -2889,6 +3806,13 @@ inline Server &Server::Delete(const char *pattern, Handler handler) {
2889 return *this; 3806 return *this;
2890} 3807}
2891 3808
3809inline Server &Server::Delete(const char *pattern,
3810 HandlerWithContentReader handler) {
3811 delete_handlers_for_content_reader_.push_back(
3812 std::make_pair(std::regex(pattern), handler));
3813 return *this;
3814}
3815
2892inline Server &Server::Options(const char *pattern, Handler handler) { 3816inline Server &Server::Options(const char *pattern, Handler handler) {
2893 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 3817 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2894 return *this; 3818 return *this;
@@ -2898,11 +3822,12 @@ inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
2898 return set_mount_point(mount_point, dir); 3822 return set_mount_point(mount_point, dir);
2899} 3823}
2900 3824
2901inline bool Server::set_mount_point(const char *mount_point, const char *dir) { 3825inline bool Server::set_mount_point(const char *mount_point, const char *dir,
3826 Headers headers) {
2902 if (detail::is_dir(dir)) { 3827 if (detail::is_dir(dir)) {
2903 std::string mnt = mount_point ? mount_point : "/"; 3828 std::string mnt = mount_point ? mount_point : "/";
2904 if (!mnt.empty() && mnt[0] == '/') { 3829 if (!mnt.empty() && mnt[0] == '/') {
2905 base_dirs_.emplace_back(mnt, dir); 3830 base_dirs_.push_back({mnt, dir, std::move(headers)});
2906 return true; 3831 return true;
2907 } 3832 }
2908 } 3833 }
@@ -2911,7 +3836,7 @@ inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
2911 3836
2912inline bool Server::remove_mount_point(const char *mount_point) { 3837inline bool Server::remove_mount_point(const char *mount_point) {
2913 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { 3838 for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
2914 if (it->first == mount_point) { 3839 if (it->mount_point == mount_point) {
2915 base_dirs_.erase(it); 3840 base_dirs_.erase(it);
2916 return true; 3841 return true;
2917 } 3842 }
@@ -2932,6 +3857,12 @@ inline void Server::set_error_handler(Handler handler) {
2932 error_handler_ = std::move(handler); 3857 error_handler_ = std::move(handler);
2933} 3858}
2934 3859
3860inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
3861
3862inline void Server::set_socket_options(SocketOptions socket_options) {
3863 socket_options_ = socket_options;
3864}
3865
2935inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } 3866inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
2936 3867
2937inline void 3868inline void
@@ -2943,11 +3874,25 @@ inline void Server::set_keep_alive_max_count(size_t count) {
2943 keep_alive_max_count_ = count; 3874 keep_alive_max_count_ = count;
2944} 3875}
2945 3876
3877inline void Server::set_keep_alive_timeout(time_t sec) {
3878 keep_alive_timeout_sec_ = sec;
3879}
3880
2946inline void Server::set_read_timeout(time_t sec, time_t usec) { 3881inline void Server::set_read_timeout(time_t sec, time_t usec) {
2947 read_timeout_sec_ = sec; 3882 read_timeout_sec_ = sec;
2948 read_timeout_usec_ = usec; 3883 read_timeout_usec_ = usec;
2949} 3884}
2950 3885
3886inline void Server::set_write_timeout(time_t sec, time_t usec) {
3887 write_timeout_sec_ = sec;
3888 write_timeout_usec_ = usec;
3889}
3890
3891inline void Server::set_idle_interval(time_t sec, time_t usec) {
3892 idle_interval_sec_ = sec;
3893 idle_interval_usec_ = usec;
3894}
3895
2951inline void Server::set_payload_max_length(size_t length) { 3896inline void Server::set_payload_max_length(size_t length) {
2952 payload_max_length_ = length; 3897 payload_max_length_ = length;
2953} 3898}
@@ -2987,7 +3932,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
2987 req.version = std::string(m[5]); 3932 req.version = std::string(m[5]);
2988 req.method = std::string(m[1]); 3933 req.method = std::string(m[1]);
2989 req.target = std::string(m[2]); 3934 req.target = std::string(m[2]);
2990 req.path = detail::decode_url(m[3]); 3935 req.path = detail::decode_url(m[3], false);
2991 3936
2992 // Parse query text 3937 // Parse query text
2993 auto len = std::distance(m[4].first, m[4].second); 3938 auto len = std::distance(m[4].first, m[4].second);
@@ -2999,7 +3944,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
2999 return false; 3944 return false;
3000} 3945}
3001 3946
3002inline bool Server::write_response(Stream &strm, bool last_connection, 3947inline bool Server::write_response(Stream &strm, bool close_connection,
3003 const Request &req, Response &res) { 3948 const Request &req, Response &res) {
3004 assert(res.status != -1); 3949 assert(res.status != -1);
3005 3950
@@ -3014,16 +3959,17 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
3014 } 3959 }
3015 3960
3016 // Headers 3961 // Headers
3017 if (last_connection || req.get_header_value("Connection") == "close") { 3962 if (close_connection || req.get_header_value("Connection") == "close") {
3018 res.set_header("Connection", "close"); 3963 res.set_header("Connection", "close");
3019 } 3964 } else {
3020 3965 std::stringstream ss;
3021 if (!last_connection && req.get_header_value("Connection") == "Keep-Alive") { 3966 ss << "timeout=" << keep_alive_timeout_sec_
3022 res.set_header("Connection", "Keep-Alive"); 3967 << ", max=" << keep_alive_max_count_;
3968 res.set_header("Keep-Alive", ss.str());
3023 } 3969 }
3024 3970
3025 if (!res.has_header("Content-Type") && 3971 if (!res.has_header("Content-Type") &&
3026 (!res.body.empty() || res.content_length > 0)) { 3972 (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
3027 res.set_header("Content-Type", "text/plain"); 3973 res.set_header("Content-Type", "text/plain");
3028 } 3974 }
3029 3975
@@ -3047,18 +3993,20 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
3047 "multipart/byteranges; boundary=" + boundary); 3993 "multipart/byteranges; boundary=" + boundary);
3048 } 3994 }
3049 3995
3996 auto type = detail::encoding_type(req, res);
3997
3050 if (res.body.empty()) { 3998 if (res.body.empty()) {
3051 if (res.content_length > 0) { 3999 if (res.content_length_ > 0) {
3052 size_t length = 0; 4000 size_t length = 0;
3053 if (req.ranges.empty()) { 4001 if (req.ranges.empty()) {
3054 length = res.content_length; 4002 length = res.content_length_;
3055 } else if (req.ranges.size() == 1) { 4003 } else if (req.ranges.size() == 1) {
3056 auto offsets = 4004 auto offsets =
3057 detail::get_range_offset_and_length(req, res.content_length, 0); 4005 detail::get_range_offset_and_length(req, res.content_length_, 0);
3058 auto offset = offsets.first; 4006 auto offset = offsets.first;
3059 length = offsets.second; 4007 length = offsets.second;
3060 auto content_range = detail::make_content_range_header_field( 4008 auto content_range = detail::make_content_range_header_field(
3061 offset, length, res.content_length); 4009 offset, length, res.content_length_);
3062 res.set_header("Content-Range", content_range); 4010 res.set_header("Content-Range", content_range);
3063 } else { 4011 } else {
3064 length = detail::get_multipart_ranges_data_length(req, res, boundary, 4012 length = detail::get_multipart_ranges_data_length(req, res, boundary,
@@ -3066,8 +4014,15 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
3066 } 4014 }
3067 res.set_header("Content-Length", std::to_string(length)); 4015 res.set_header("Content-Length", std::to_string(length));
3068 } else { 4016 } else {
3069 if (res.content_provider) { 4017 if (res.content_provider_) {
3070 res.set_header("Transfer-Encoding", "chunked"); 4018 if (res.is_chunked_content_provider) {
4019 res.set_header("Transfer-Encoding", "chunked");
4020 if (type == detail::EncodingType::Gzip) {
4021 res.set_header("Content-Encoding", "gzip");
4022 } else if (type == detail::EncodingType::Brotli) {
4023 res.set_header("Content-Encoding", "br");
4024 }
4025 }
3071 } else { 4026 } else {
3072 res.set_header("Content-Length", "0"); 4027 res.set_header("Content-Length", "0");
3073 } 4028 }
@@ -3089,16 +4044,35 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
3089 detail::make_multipart_ranges_data(req, res, boundary, content_type); 4044 detail::make_multipart_ranges_data(req, res, boundary, content_type);
3090 } 4045 }
3091 4046
4047 if (type != detail::EncodingType::None) {
4048 std::shared_ptr<detail::compressor> compressor;
4049
4050 if (type == detail::EncodingType::Gzip) {
3092#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4051#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3093 // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 4052 compressor = std::make_shared<detail::gzip_compressor>();
3094 const auto &encodings = req.get_header_value("Accept-Encoding");
3095 if (encodings.find("gzip") != std::string::npos &&
3096 detail::can_compress(res.get_header_value("Content-Type"))) {
3097 if (detail::compress(res.body)) {
3098 res.set_header("Content-Encoding", "gzip"); 4053 res.set_header("Content-Encoding", "gzip");
4054#endif
4055 } else if (type == detail::EncodingType::Brotli) {
4056#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4057 compressor = std::make_shared<detail::brotli_compressor>();
4058 res.set_header("Content-Encoding", "brotli");
4059#endif
4060 }
4061
4062 if (compressor) {
4063 std::string compressed;
4064
4065 if (!compressor->compress(res.body.data(), res.body.size(), true,
4066 [&](const char *data, size_t data_len) {
4067 compressed.append(data, data_len);
4068 return true;
4069 })) {
4070 return false;
4071 }
4072
4073 res.body.swap(compressed);
3099 } 4074 }
3100 } 4075 }
3101#endif
3102 4076
3103 auto length = std::to_string(res.body.size()); 4077 auto length = std::to_string(res.body.size());
3104 res.set_header("Content-Length", length); 4078 res.set_header("Content-Length", length);
@@ -3111,13 +4085,14 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
3111 strm.write(data.data(), data.size()); 4085 strm.write(data.data(), data.size());
3112 4086
3113 // Body 4087 // Body
4088 auto ret = true;
3114 if (req.method != "HEAD") { 4089 if (req.method != "HEAD") {
3115 if (!res.body.empty()) { 4090 if (!res.body.empty()) {
3116 if (!strm.write(res.body)) { return false; } 4091 if (!strm.write(res.body)) { ret = false; }
3117 } else if (res.content_provider) { 4092 } else if (res.content_provider_) {
3118 if (!write_content_with_provider(strm, req, res, boundary, 4093 if (!write_content_with_provider(strm, req, res, boundary,
3119 content_type)) { 4094 content_type)) {
3120 return false; 4095 ret = false;
3121 } 4096 }
3122 } 4097 }
3123 } 4098 }
@@ -3125,87 +4100,109 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
3125 // Log 4100 // Log
3126 if (logger_) { logger_(req, res); } 4101 if (logger_) { logger_(req, res); }
3127 4102
3128 return true; 4103 return ret;
3129} 4104}
3130 4105
3131inline bool 4106inline bool
3132Server::write_content_with_provider(Stream &strm, const Request &req, 4107Server::write_content_with_provider(Stream &strm, const Request &req,
3133 Response &res, const std::string &boundary, 4108 Response &res, const std::string &boundary,
3134 const std::string &content_type) { 4109 const std::string &content_type) {
3135 if (res.content_length) { 4110 auto is_shutting_down = [this]() {
4111 return this->svr_sock_ == INVALID_SOCKET;
4112 };
4113
4114 if (res.content_length_ > 0) {
3136 if (req.ranges.empty()) { 4115 if (req.ranges.empty()) {
3137 if (detail::write_content(strm, res.content_provider, 0, 4116 if (detail::write_content(strm, res.content_provider_, 0,
3138 res.content_length) < 0) { 4117 res.content_length_, is_shutting_down) < 0) {
3139 return false; 4118 return false;
3140 } 4119 }
3141 } else if (req.ranges.size() == 1) { 4120 } else if (req.ranges.size() == 1) {
3142 auto offsets = 4121 auto offsets =
3143 detail::get_range_offset_and_length(req, res.content_length, 0); 4122 detail::get_range_offset_and_length(req, res.content_length_, 0);
3144 auto offset = offsets.first; 4123 auto offset = offsets.first;
3145 auto length = offsets.second; 4124 auto length = offsets.second;
3146 if (detail::write_content(strm, res.content_provider, offset, length) < 4125 if (detail::write_content(strm, res.content_provider_, offset, length,
3147 0) { 4126 is_shutting_down) < 0) {
3148 return false; 4127 return false;
3149 } 4128 }
3150 } else { 4129 } else {
3151 if (!detail::write_multipart_ranges_data(strm, req, res, boundary, 4130 if (!detail::write_multipart_ranges_data(
3152 content_type)) { 4131 strm, req, res, boundary, content_type, is_shutting_down)) {
3153 return false; 4132 return false;
3154 } 4133 }
3155 } 4134 }
3156 } else { 4135 } else {
3157 auto is_shutting_down = [this]() { 4136 if (res.is_chunked_content_provider) {
3158 return this->svr_sock_ == INVALID_SOCKET; 4137 auto type = detail::encoding_type(req, res);
3159 }; 4138
3160 if (detail::write_content_chunked(strm, res.content_provider, 4139 std::shared_ptr<detail::compressor> compressor;
3161 is_shutting_down) < 0) { 4140 if (type == detail::EncodingType::Gzip) {
3162 return false; 4141#ifdef CPPHTTPLIB_ZLIB_SUPPORT
4142 compressor = std::make_shared<detail::gzip_compressor>();
4143#endif
4144 } else if (type == detail::EncodingType::Brotli) {
4145#ifdef CPPHTTPLIB_BROTLI_SUPPORT
4146 compressor = std::make_shared<detail::brotli_compressor>();
4147#endif
4148 } else {
4149 compressor = std::make_shared<detail::nocompressor>();
4150 }
4151 assert(compressor != nullptr);
4152
4153 if (detail::write_content_chunked(strm, res.content_provider_,
4154 is_shutting_down, *compressor) < 0) {
4155 return false;
4156 }
4157 } else {
4158 if (detail::write_content_without_length(strm, res.content_provider_,
4159 is_shutting_down) < 0) {
4160 return false;
4161 }
3163 } 4162 }
3164 } 4163 }
3165 return true; 4164 return true;
3166} 4165}
3167 4166
3168inline bool Server::read_content(Stream &strm, bool last_connection, 4167inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
3169 Request &req, Response &res) {
3170 MultipartFormDataMap::iterator cur; 4168 MultipartFormDataMap::iterator cur;
3171 auto ret = read_content_core( 4169 if (read_content_core(
3172 strm, last_connection, req, res, 4170 strm, req, res,
3173 // Regular 4171 // Regular
3174 [&](const char *buf, size_t n) { 4172 [&](const char *buf, size_t n) {
3175 if (req.body.size() + n > req.body.max_size()) { return false; } 4173 if (req.body.size() + n > req.body.max_size()) { return false; }
3176 req.body.append(buf, n); 4174 req.body.append(buf, n);
3177 return true; 4175 return true;
3178 }, 4176 },
3179 // Multipart 4177 // Multipart
3180 [&](const MultipartFormData &file) { 4178 [&](const MultipartFormData &file) {
3181 cur = req.files.emplace(file.name, file); 4179 cur = req.files.emplace(file.name, file);
3182 return true; 4180 return true;
3183 }, 4181 },
3184 [&](const char *buf, size_t n) { 4182 [&](const char *buf, size_t n) {
3185 auto &content = cur->second.content; 4183 auto &content = cur->second.content;
3186 if (content.size() + n > content.max_size()) { return false; } 4184 if (content.size() + n > content.max_size()) { return false; }
3187 content.append(buf, n); 4185 content.append(buf, n);
3188 return true; 4186 return true;
3189 }); 4187 })) {
3190 4188 const auto &content_type = req.get_header_value("Content-Type");
3191 const auto &content_type = req.get_header_value("Content-Type"); 4189 if (!content_type.find("application/x-www-form-urlencoded")) {
3192 if (!content_type.find("application/x-www-form-urlencoded")) { 4190 detail::parse_query_text(req.body, req.params);
3193 detail::parse_query_text(req.body, req.params); 4191 }
4192 return true;
3194 } 4193 }
3195 4194 return false;
3196 return ret;
3197} 4195}
3198 4196
3199inline bool Server::read_content_with_content_receiver( 4197inline bool Server::read_content_with_content_receiver(
3200 Stream &strm, bool last_connection, Request &req, Response &res, 4198 Stream &strm, Request &req, Response &res, ContentReceiver receiver,
3201 ContentReceiver receiver, MultipartContentHeader multipart_header, 4199 MultipartContentHeader multipart_header,
3202 ContentReceiver multipart_receiver) { 4200 ContentReceiver multipart_receiver) {
3203 return read_content_core(strm, last_connection, req, res, receiver, 4201 return read_content_core(strm, req, res, receiver, multipart_header,
3204 multipart_header, multipart_receiver); 4202 multipart_receiver);
3205} 4203}
3206 4204
3207inline bool Server::read_content_core(Stream &strm, bool last_connection, 4205inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
3208 Request &req, Response &res,
3209 ContentReceiver receiver, 4206 ContentReceiver receiver,
3210 MultipartContentHeader mulitpart_header, 4207 MultipartContentHeader mulitpart_header,
3211 ContentReceiver multipart_receiver) { 4208 ContentReceiver multipart_receiver) {
@@ -3217,11 +4214,22 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
3217 std::string boundary; 4214 std::string boundary;
3218 if (!detail::parse_multipart_boundary(content_type, boundary)) { 4215 if (!detail::parse_multipart_boundary(content_type, boundary)) {
3219 res.status = 400; 4216 res.status = 400;
3220 return write_response(strm, last_connection, req, res); 4217 return false;
3221 } 4218 }
3222 4219
3223 multipart_form_data_parser.set_boundary(boundary); 4220 multipart_form_data_parser.set_boundary(std::move(boundary));
3224 out = [&](const char *buf, size_t n) { 4221 out = [&](const char *buf, size_t n) {
4222 /* For debug
4223 size_t pos = 0;
4224 while (pos < n) {
4225 auto read_size = std::min<size_t>(1, n - pos);
4226 auto ret = multipart_form_data_parser.parse(
4227 buf + pos, read_size, multipart_receiver, mulitpart_header);
4228 if (!ret) { return false; }
4229 pos += read_size;
4230 }
4231 return true;
4232 */
3225 return multipart_form_data_parser.parse(buf, n, multipart_receiver, 4233 return multipart_form_data_parser.parse(buf, n, multipart_receiver,
3226 mulitpart_header); 4234 mulitpart_header);
3227 }; 4235 };
@@ -3229,15 +4237,19 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
3229 out = receiver; 4237 out = receiver;
3230 } 4238 }
3231 4239
3232 if (!detail::read_content(strm, req, payload_max_length_, res.status, 4240 if (req.method == "DELETE" && !req.has_header("Content-Length")) {
3233 Progress(), out)) { 4241 return true;
3234 return write_response(strm, last_connection, req, res); 4242 }
4243
4244 if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
4245 out, true)) {
4246 return false;
3235 } 4247 }
3236 4248
3237 if (req.is_multipart_form_data()) { 4249 if (req.is_multipart_form_data()) {
3238 if (!multipart_form_data_parser.is_valid()) { 4250 if (!multipart_form_data_parser.is_valid()) {
3239 res.status = 400; 4251 res.status = 400;
3240 return write_response(strm, last_connection, req, res); 4252 return false;
3241 } 4253 }
3242 } 4254 }
3243 4255
@@ -3246,15 +4258,12 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
3246 4258
3247inline bool Server::handle_file_request(Request &req, Response &res, 4259inline bool Server::handle_file_request(Request &req, Response &res,
3248 bool head) { 4260 bool head) {
3249 for (const auto &kv : base_dirs_) { 4261 for (const auto &entry : base_dirs_) {
3250 const auto &mount_point = kv.first;
3251 const auto &base_dir = kv.second;
3252
3253 // Prefix match 4262 // Prefix match
3254 if (!req.path.find(mount_point)) { 4263 if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
3255 std::string sub_path = "/" + req.path.substr(mount_point.size()); 4264 std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
3256 if (detail::is_valid_path(sub_path)) { 4265 if (detail::is_valid_path(sub_path)) {
3257 auto path = base_dir + sub_path; 4266 auto path = entry.base_dir + sub_path;
3258 if (path.back() == '/') { path += "index.html"; } 4267 if (path.back() == '/') { path += "index.html"; }
3259 4268
3260 if (detail::is_file(path)) { 4269 if (detail::is_file(path)) {
@@ -3262,6 +4271,9 @@ inline bool Server::handle_file_request(Request &req, Response &res,
3262 auto type = 4271 auto type =
3263 detail::find_content_type(path, file_extension_and_mimetype_map_); 4272 detail::find_content_type(path, file_extension_and_mimetype_map_);
3264 if (type) { res.set_header("Content-Type", type); } 4273 if (type) { res.set_header("Content-Type", type); }
4274 for (const auto& kv : entry.headers) {
4275 res.set_header(kv.first.c_str(), kv.second);
4276 }
3265 res.status = 200; 4277 res.status = 200;
3266 if (!head && file_request_handler_) { 4278 if (!head && file_request_handler_) {
3267 file_request_handler_(req, res); 4279 file_request_handler_(req, res);
@@ -3274,40 +4286,39 @@ inline bool Server::handle_file_request(Request &req, Response &res,
3274 return false; 4286 return false;
3275} 4287}
3276 4288
3277inline socket_t Server::create_server_socket(const char *host, int port, 4289inline socket_t
3278 int socket_flags) const { 4290Server::create_server_socket(const char *host, int port, int socket_flags,
4291 SocketOptions socket_options) const {
3279 return detail::create_socket( 4292 return detail::create_socket(
3280 host, port, 4293 host, port, socket_flags, tcp_nodelay_, socket_options,
3281 [](socket_t sock, struct addrinfo &ai) -> bool { 4294 [](socket_t sock, struct addrinfo &ai) -> bool {
3282 if (::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) { 4295 if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
3283 return false; 4296 return false;
3284 } 4297 }
3285 if (::listen(sock, 5)) { // Listen through 5 channels 4298 if (::listen(sock, 5)) { // Listen through 5 channels
3286 return false; 4299 return false;
3287 } 4300 }
3288 return true; 4301 return true;
3289 }, 4302 });
3290 socket_flags);
3291} 4303}
3292 4304
3293inline int Server::bind_internal(const char *host, int port, int socket_flags) { 4305inline int Server::bind_internal(const char *host, int port, int socket_flags) {
3294 if (!is_valid()) { return -1; } 4306 if (!is_valid()) { return -1; }
3295 4307
3296 svr_sock_ = create_server_socket(host, port, socket_flags); 4308 svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
3297 if (svr_sock_ == INVALID_SOCKET) { return -1; } 4309 if (svr_sock_ == INVALID_SOCKET) { return -1; }
3298 4310
3299 if (port == 0) { 4311 if (port == 0) {
3300 struct sockaddr_storage address; 4312 struct sockaddr_storage addr;
3301 socklen_t len = sizeof(address); 4313 socklen_t addr_len = sizeof(addr);
3302 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&address), 4314 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
3303 &len) == -1) { 4315 &addr_len) == -1) {
3304 return -1; 4316 return -1;
3305 } 4317 }
3306 if (address.ss_family == AF_INET) { 4318 if (addr.ss_family == AF_INET) {
3307 return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port); 4319 return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
3308 } else if (address.ss_family == AF_INET6) { 4320 } else if (addr.ss_family == AF_INET6) {
3309 return ntohs( 4321 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
3310 reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
3311 } else { 4322 } else {
3312 return -1; 4323 return -1;
3313 } 4324 }
@@ -3323,18 +4334,19 @@ inline bool Server::listen_internal() {
3323 { 4334 {
3324 std::unique_ptr<TaskQueue> task_queue(new_task_queue()); 4335 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
3325 4336
3326 for (;;) { 4337 while (svr_sock_ != INVALID_SOCKET) {
3327 if (svr_sock_ == INVALID_SOCKET) { 4338#ifndef _WIN32
3328 // The server socket was closed by 'stop' method. 4339 if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
3329 break; 4340#endif
3330 } 4341 auto val = detail::select_read(svr_sock_, idle_interval_sec_,
3331 4342 idle_interval_usec_);
3332 auto val = detail::select_read(svr_sock_, 0, 100000); 4343 if (val == 0) { // Timeout
3333 4344 task_queue->on_idle();
3334 if (val == 0) { // Timeout 4345 continue;
3335 continue; 4346 }
4347#ifndef _WIN32
3336 } 4348 }
3337 4349#endif
3338 socket_t sock = accept(svr_sock_, nullptr, nullptr); 4350 socket_t sock = accept(svr_sock_, nullptr, nullptr);
3339 4351
3340 if (sock == INVALID_SOCKET) { 4352 if (sock == INVALID_SOCKET) {
@@ -3353,7 +4365,11 @@ inline bool Server::listen_internal() {
3353 break; 4365 break;
3354 } 4366 }
3355 4367
4368#if __cplusplus > 201703L
4369 task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
4370#else
3356 task_queue->enqueue([=]() { process_and_close_socket(sock); }); 4371 task_queue->enqueue([=]() { process_and_close_socket(sock); });
4372#endif
3357 } 4373 }
3358 4374
3359 task_queue->shutdown(); 4375 task_queue->shutdown();
@@ -3363,8 +4379,7 @@ inline bool Server::listen_internal() {
3363 return ret; 4379 return ret;
3364} 4380}
3365 4381
3366inline bool Server::routing(Request &req, Response &res, Stream &strm, 4382inline bool Server::routing(Request &req, Response &res, Stream &strm) {
3367 bool last_connection) {
3368 // File handler 4383 // File handler
3369 bool is_head_request = req.method == "HEAD"; 4384 bool is_head_request = req.method == "HEAD";
3370 if ((req.method == "GET" || is_head_request) && 4385 if ((req.method == "GET" || is_head_request) &&
@@ -3377,12 +4392,12 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
3377 { 4392 {
3378 ContentReader reader( 4393 ContentReader reader(
3379 [&](ContentReceiver receiver) { 4394 [&](ContentReceiver receiver) {
3380 return read_content_with_content_receiver( 4395 return read_content_with_content_receiver(strm, req, res, receiver,
3381 strm, last_connection, req, res, receiver, nullptr, nullptr); 4396 nullptr, nullptr);
3382 }, 4397 },
3383 [&](MultipartContentHeader header, ContentReceiver receiver) { 4398 [&](MultipartContentHeader header, ContentReceiver receiver) {
3384 return read_content_with_content_receiver( 4399 return read_content_with_content_receiver(strm, req, res, nullptr,
3385 strm, last_connection, req, res, nullptr, header, receiver); 4400 header, receiver);
3386 }); 4401 });
3387 4402
3388 if (req.method == "POST") { 4403 if (req.method == "POST") {
@@ -3400,11 +4415,16 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
3400 req, res, reader, patch_handlers_for_content_reader_)) { 4415 req, res, reader, patch_handlers_for_content_reader_)) {
3401 return true; 4416 return true;
3402 } 4417 }
4418 } else if (req.method == "DELETE") {
4419 if (dispatch_request_for_content_reader(
4420 req, res, reader, delete_handlers_for_content_reader_)) {
4421 return true;
4422 }
3403 } 4423 }
3404 } 4424 }
3405 4425
3406 // Read content into `req.body` 4426 // Read content into `req.body`
3407 if (!read_content(strm, last_connection, req, res)) { return false; } 4427 if (!read_content(strm, req, res)) { return false; }
3408 } 4428 }
3409 4429
3410 // Regular handler 4430 // Regular handler
@@ -3427,22 +4447,31 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
3427} 4447}
3428 4448
3429inline bool Server::dispatch_request(Request &req, Response &res, 4449inline bool Server::dispatch_request(Request &req, Response &res,
3430 Handlers &handlers) { 4450 const Handlers &handlers) {
3431 for (const auto &x : handlers) {
3432 const auto &pattern = x.first;
3433 const auto &handler = x.second;
3434 4451
3435 if (std::regex_match(req.path, req.matches, pattern)) { 4452 try {
3436 handler(req, res); 4453 for (const auto &x : handlers) {
3437 return true; 4454 const auto &pattern = x.first;
4455 const auto &handler = x.second;
4456
4457 if (std::regex_match(req.path, req.matches, pattern)) {
4458 handler(req, res);
4459 return true;
4460 }
3438 } 4461 }
4462 } catch (const std::exception &ex) {
4463 res.status = 500;
4464 res.set_header("EXCEPTION_WHAT", ex.what());
4465 } catch (...) {
4466 res.status = 500;
4467 res.set_header("EXCEPTION_WHAT", "UNKNOWN");
3439 } 4468 }
3440 return false; 4469 return false;
3441} 4470}
3442 4471
3443inline bool Server::dispatch_request_for_content_reader( 4472inline bool Server::dispatch_request_for_content_reader(
3444 Request &req, Response &res, ContentReader content_reader, 4473 Request &req, Response &res, ContentReader content_reader,
3445 HandlersForContentReader &handlers) { 4474 const HandlersForContentReader &handlers) {
3446 for (const auto &x : handlers) { 4475 for (const auto &x : handlers) {
3447 const auto &pattern = x.first; 4476 const auto &pattern = x.first;
3448 const auto &handler = x.second; 4477 const auto &handler = x.second;
@@ -3456,8 +4485,8 @@ inline bool Server::dispatch_request_for_content_reader(
3456} 4485}
3457 4486
3458inline bool 4487inline bool
3459Server::process_request(Stream &strm, bool last_connection, 4488Server::process_request(Stream &strm, bool close_connection,
3460 bool &connection_close, 4489 bool &connection_closed,
3461 const std::function<void(Request &)> &setup_request) { 4490 const std::function<void(Request &)> &setup_request) {
3462 std::array<char, 2048> buf{}; 4491 std::array<char, 2048> buf{};
3463 4492
@@ -3476,26 +4505,28 @@ Server::process_request(Stream &strm, bool last_connection,
3476 Headers dummy; 4505 Headers dummy;
3477 detail::read_headers(strm, dummy); 4506 detail::read_headers(strm, dummy);
3478 res.status = 414; 4507 res.status = 414;
3479 return write_response(strm, last_connection, req, res); 4508 return write_response(strm, close_connection, req, res);
3480 } 4509 }
3481 4510
3482 // Request line and headers 4511 // Request line and headers
3483 if (!parse_request_line(line_reader.ptr(), req) || 4512 if (!parse_request_line(line_reader.ptr(), req) ||
3484 !detail::read_headers(strm, req.headers)) { 4513 !detail::read_headers(strm, req.headers)) {
3485 res.status = 400; 4514 res.status = 400;
3486 return write_response(strm, last_connection, req, res); 4515 return write_response(strm, close_connection, req, res);
3487 } 4516 }
3488 4517
3489 if (req.get_header_value("Connection") == "close") { 4518 if (req.get_header_value("Connection") == "close") {
3490 connection_close = true; 4519 connection_closed = true;
3491 } 4520 }
3492 4521
3493 if (req.version == "HTTP/1.0" && 4522 if (req.version == "HTTP/1.0" &&
3494 req.get_header_value("Connection") != "Keep-Alive") { 4523 req.get_header_value("Connection") != "Keep-Alive") {
3495 connection_close = true; 4524 connection_closed = true;
3496 } 4525 }
3497 4526
3498 req.set_header("REMOTE_ADDR", strm.get_remote_addr()); 4527 strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
4528 req.set_header("REMOTE_ADDR", req.remote_addr);
4529 req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
3499 4530
3500 if (req.has_header("Range")) { 4531 if (req.has_header("Range")) {
3501 const auto &range_header_value = req.get_header_value("Range"); 4532 const auto &range_header_value = req.get_header_value("Range");
@@ -3517,136 +4548,211 @@ Server::process_request(Stream &strm, bool last_connection,
3517 strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, 4548 strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
3518 detail::status_message(status)); 4549 detail::status_message(status));
3519 break; 4550 break;
3520 default: return write_response(strm, last_connection, req, res); 4551 default: return write_response(strm, close_connection, req, res);
3521 } 4552 }
3522 } 4553 }
3523 4554
3524 // Rounting 4555 // Rounting
3525 if (routing(req, res, strm, last_connection)) { 4556 if (routing(req, res, strm)) {
3526 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } 4557 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
3527 } else { 4558 } else {
3528 if (res.status == -1) { res.status = 404; } 4559 if (res.status == -1) { res.status = 404; }
3529 } 4560 }
3530 4561
3531 return write_response(strm, last_connection, req, res); 4562 return write_response(strm, close_connection, req, res);
3532} 4563}
3533 4564
3534inline bool Server::is_valid() const { return true; } 4565inline bool Server::is_valid() const { return true; }
3535 4566
3536inline bool Server::process_and_close_socket(socket_t sock) { 4567inline bool Server::process_and_close_socket(socket_t sock) {
3537 return detail::process_and_close_socket( 4568 auto ret = detail::process_server_socket(
3538 false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, 4569 sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
3539 [this](Stream &strm, bool last_connection, bool &connection_close) { 4570 read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
3540 return process_request(strm, last_connection, connection_close, 4571 [this](Stream &strm, bool close_connection, bool &connection_closed) {
4572 return process_request(strm, close_connection, connection_closed,
3541 nullptr); 4573 nullptr);
3542 }); 4574 });
4575
4576 detail::shutdown_socket(sock);
4577 detail::close_socket(sock);
4578 return ret;
3543} 4579}
3544 4580
3545// HTTP client implementation 4581// HTTP client implementation
3546inline Client::Client(const std::string &host, int port, 4582inline ClientImpl::ClientImpl(const std::string &host)
3547 const std::string &client_cert_path, 4583 : ClientImpl(host, 80, std::string(), std::string()) {}
3548 const std::string &client_key_path) 4584
4585inline ClientImpl::ClientImpl(const std::string &host, int port)
4586 : ClientImpl(host, port, std::string(), std::string()) {}
4587
4588inline ClientImpl::ClientImpl(const std::string &host, int port,
4589 const std::string &client_cert_path,
4590 const std::string &client_key_path)
3549 : host_(host), port_(port), 4591 : host_(host), port_(port),
3550 host_and_port_(host_ + ":" + std::to_string(port_)), 4592 host_and_port_(host_ + ":" + std::to_string(port_)),
3551 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} 4593 client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
3552 4594
3553inline Client::~Client() {} 4595inline ClientImpl::~ClientImpl() { stop_core(); }
4596
4597inline bool ClientImpl::is_valid() const { return true; }
3554 4598
3555inline bool Client::is_valid() const { return true; } 4599inline Error ClientImpl::get_last_error() const { return error_; }
3556 4600
3557inline socket_t Client::create_client_socket() const { 4601inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
3558 if (!proxy_host_.empty()) { 4602 client_cert_path_ = rhs.client_cert_path_;
3559 return detail::create_client_socket(proxy_host_.c_str(), proxy_port_, 4603 client_key_path_ = rhs.client_key_path_;
3560 timeout_sec_, interface_); 4604 connection_timeout_sec_ = rhs.connection_timeout_sec_;
4605 read_timeout_sec_ = rhs.read_timeout_sec_;
4606 read_timeout_usec_ = rhs.read_timeout_usec_;
4607 write_timeout_sec_ = rhs.write_timeout_sec_;
4608 write_timeout_usec_ = rhs.write_timeout_usec_;
4609 basic_auth_username_ = rhs.basic_auth_username_;
4610 basic_auth_password_ = rhs.basic_auth_password_;
4611 bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
4612#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4613 digest_auth_username_ = rhs.digest_auth_username_;
4614 digest_auth_password_ = rhs.digest_auth_password_;
4615#endif
4616 keep_alive_ = rhs.keep_alive_;
4617 follow_location_ = rhs.follow_location_;
4618 tcp_nodelay_ = rhs.tcp_nodelay_;
4619 socket_options_ = rhs.socket_options_;
4620 compress_ = rhs.compress_;
4621 decompress_ = rhs.decompress_;
4622 interface_ = rhs.interface_;
4623 proxy_host_ = rhs.proxy_host_;
4624 proxy_port_ = rhs.proxy_port_;
4625 proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
4626 proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
4627 proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
4628#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4629 proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
4630 proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
4631#endif
4632#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4633 server_certificate_verification_ = rhs.server_certificate_verification_;
4634#endif
4635 logger_ = rhs.logger_;
4636}
4637
4638inline socket_t ClientImpl::create_client_socket() const {
4639 if (!proxy_host_.empty() && proxy_port_ != -1) {
4640 return detail::create_client_socket(
4641 proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_,
4642 connection_timeout_sec_, connection_timeout_usec_, interface_, error_);
3561 } 4643 }
3562 return detail::create_client_socket(host_.c_str(), port_, timeout_sec_, 4644 return detail::create_client_socket(
3563 interface_); 4645 host_.c_str(), port_, tcp_nodelay_, socket_options_,
4646 connection_timeout_sec_, connection_timeout_usec_, interface_, error_);
3564} 4647}
3565 4648
3566inline bool Client::read_response_line(Stream &strm, Response &res) { 4649inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
4650 auto sock = create_client_socket();
4651 if (sock == INVALID_SOCKET) { return false; }
4652 socket.sock = sock;
4653 return true;
4654}
4655
4656inline void ClientImpl::close_socket(Socket &socket,
4657 bool /*process_socket_ret*/) {
4658 detail::close_socket(socket.sock);
4659 socket_.sock = INVALID_SOCKET;
4660#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4661 socket_.ssl = nullptr;
4662#endif
4663}
4664
4665inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
3567 std::array<char, 2048> buf; 4666 std::array<char, 2048> buf;
3568 4667
3569 detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); 4668 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
3570 4669
3571 if (!line_reader.getline()) { return false; } 4670 if (!line_reader.getline()) { return false; }
3572 4671
3573 const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); 4672 const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n");
3574 4673
3575 std::cmatch m; 4674 std::cmatch m;
3576 if (std::regex_match(line_reader.ptr(), m, re)) { 4675 if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
4676 res.version = std::string(m[1]);
4677 res.status = std::stoi(std::string(m[2]));
4678 res.reason = std::string(m[3]);
4679
4680 // Ignore '100 Continue'
4681 while (res.status == 100) {
4682 if (!line_reader.getline()) { return false; } // CRLF
4683 if (!line_reader.getline()) { return false; } // next response line
4684
4685 if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
3577 res.version = std::string(m[1]); 4686 res.version = std::string(m[1]);
3578 res.status = std::stoi(std::string(m[2])); 4687 res.status = std::stoi(std::string(m[2]));
4688 res.reason = std::string(m[3]);
3579 } 4689 }
3580 4690
3581 return true; 4691 return true;
3582} 4692}
3583 4693
3584inline bool Client::send(const Request &req, Response &res) { 4694inline bool ClientImpl::send(const Request &req, Response &res) {
3585 auto sock = create_client_socket(); 4695 std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
3586 if (sock == INVALID_SOCKET) { return false; }
3587 4696
3588#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4697 {
3589 if (is_ssl() && !proxy_host_.empty()) { 4698 std::lock_guard<std::mutex> guard(socket_mutex_);
3590 bool error;
3591 if (!connect(sock, res, error)) { return error; }
3592 }
3593#endif
3594 4699
3595 return process_and_close_socket( 4700 auto is_alive = false;
3596 sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) { 4701 if (socket_.is_open()) {
3597 return handle_request(strm, req, res, last_connection, 4702 is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
3598 connection_close); 4703 if (!is_alive) { close_socket(socket_, false); }
3599 }); 4704 }
3600}
3601 4705
3602inline bool Client::send(const std::vector<Request> &requests, 4706 if (!is_alive) {
3603 std::vector<Response> &responses) { 4707 if (!create_and_connect_socket(socket_)) { return false; }
3604 size_t i = 0;
3605 while (i < requests.size()) {
3606 auto sock = create_client_socket();
3607 if (sock == INVALID_SOCKET) { return false; }
3608 4708
3609#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4709#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3610 if (is_ssl() && !proxy_host_.empty()) { 4710 // TODO: refactoring
3611 Response res; 4711 if (is_ssl()) {
3612 bool error; 4712 auto &scli = static_cast<SSLClient &>(*this);
3613 if (!connect(sock, res, error)) { return false; } 4713 if (!proxy_host_.empty() && proxy_port_ != -1) {
3614 } 4714 bool success = false;
3615#endif 4715 if (!scli.connect_with_proxy(socket_, res, success)) {
4716 return success;
4717 }
4718 }
3616 4719
3617 if (!process_and_close_socket(sock, requests.size() - i, 4720 if (!scli.initialize_ssl(socket_)) { return false; }
3618 [&](Stream &strm, bool last_connection, 4721 }
3619 bool &connection_close) -> bool { 4722#endif
3620 auto &req = requests[i++];
3621 auto res = Response();
3622 auto ret = handle_request(strm, req, res,
3623 last_connection,
3624 connection_close);
3625 if (ret) {
3626 responses.emplace_back(std::move(res));
3627 }
3628 return ret;
3629 })) {
3630 return false;
3631 } 4723 }
3632 } 4724 }
3633 4725
3634 return true; 4726 auto close_connection = !keep_alive_;
4727
4728 auto ret = process_socket(socket_, [&](Stream &strm) {
4729 return handle_request(strm, req, res, close_connection);
4730 });
4731
4732 if (close_connection || !ret) { stop_core(); }
4733
4734 if (!ret) {
4735 if (error_ == Error::Success) { error_ = Error::Unknown; }
4736 }
4737
4738 return ret;
3635} 4739}
3636 4740
3637inline bool Client::handle_request(Stream &strm, const Request &req, 4741inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
3638 Response &res, bool last_connection, 4742 Response &res, bool close_connection) {
3639 bool &connection_close) { 4743 if (req.path.empty()) {
3640 if (req.path.empty()) { return false; } 4744 error_ = Error::Connection;
4745 return false;
4746 }
3641 4747
3642 bool ret; 4748 bool ret;
3643 4749
3644 if (!is_ssl() && !proxy_host_.empty()) { 4750 if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
3645 auto req2 = req; 4751 auto req2 = req;
3646 req2.path = "http://" + host_and_port_ + req.path; 4752 req2.path = "http://" + host_and_port_ + req.path;
3647 ret = process_request(strm, req2, res, last_connection, connection_close); 4753 ret = process_request(strm, req2, res, close_connection);
3648 } else { 4754 } else {
3649 ret = process_request(strm, req, res, last_connection, connection_close); 4755 ret = process_request(strm, req, res, close_connection);
3650 } 4756 }
3651 4757
3652 if (!ret) { return false; } 4758 if (!ret) { return false; }
@@ -3656,7 +4762,8 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
3656 } 4762 }
3657 4763
3658#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4764#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3659 if (res.status == 401 || res.status == 407) { 4765 if ((res.status == 401 || res.status == 407) &&
4766 req.authorization_count_ < 5) {
3660 auto is_proxy = res.status == 407; 4767 auto is_proxy = res.status == 407;
3661 const auto &username = 4768 const auto &username =
3662 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; 4769 is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
@@ -3665,12 +4772,14 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
3665 4772
3666 if (!username.empty() && !password.empty()) { 4773 if (!username.empty() && !password.empty()) {
3667 std::map<std::string, std::string> auth; 4774 std::map<std::string, std::string> auth;
3668 if (parse_www_authenticate(res, auth, is_proxy)) { 4775 if (detail::parse_www_authenticate(res, auth, is_proxy)) {
3669 Request new_req = req; 4776 Request new_req = req;
3670 auto key = is_proxy ? "Proxy-Authorization" : "WWW-Authorization"; 4777 new_req.authorization_count_ += 1;
4778 auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
3671 new_req.headers.erase(key); 4779 new_req.headers.erase(key);
3672 new_req.headers.insert(make_digest_authentication_header( 4780 new_req.headers.insert(detail::make_digest_authentication_header(
3673 req, auth, 1, random_string(10), username, password, is_proxy)); 4781 req, auth, new_req.authorization_count_, detail::random_string(10),
4782 username, password, is_proxy));
3674 4783
3675 Response new_res; 4784 Response new_res;
3676 4785
@@ -3684,102 +4793,64 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
3684 return ret; 4793 return ret;
3685} 4794}
3686 4795
3687#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4796inline bool ClientImpl::redirect(const Request &req, Response &res) {
3688inline bool Client::connect(socket_t sock, Response &res, bool &error) { 4797 if (req.redirect_count == 0) {
3689 error = true; 4798 error_ = Error::ExceedRedirectCount;
3690 Response res2;
3691
3692 if (!detail::process_socket(
3693 true, sock, 1, read_timeout_sec_, read_timeout_usec_,
3694 [&](Stream &strm, bool /*last_connection*/, bool &connection_close) {
3695 Request req2;
3696 req2.method = "CONNECT";
3697 req2.path = host_and_port_;
3698 return process_request(strm, req2, res2, false, connection_close);
3699 })) {
3700 detail::close_socket(sock);
3701 error = false;
3702 return false; 4799 return false;
3703 } 4800 }
3704 4801
3705 if (res2.status == 407) { 4802 auto location = detail::decode_url(res.get_header_value("location"), true);
3706 if (!proxy_digest_auth_username_.empty() &&
3707 !proxy_digest_auth_password_.empty()) {
3708 std::map<std::string, std::string> auth;
3709 if (parse_www_authenticate(res2, auth, true)) {
3710 Response res3;
3711 if (!detail::process_socket(
3712 true, sock, 1, read_timeout_sec_, read_timeout_usec_,
3713 [&](Stream &strm, bool /*last_connection*/,
3714 bool &connection_close) {
3715 Request req3;
3716 req3.method = "CONNECT";
3717 req3.path = host_and_port_;
3718 req3.headers.insert(make_digest_authentication_header(
3719 req3, auth, 1, random_string(10),
3720 proxy_digest_auth_username_, proxy_digest_auth_password_,
3721 true));
3722 return process_request(strm, req3, res3, false,
3723 connection_close);
3724 })) {
3725 detail::close_socket(sock);
3726 error = false;
3727 return false;
3728 }
3729 }
3730 } else {
3731 res = res2;
3732 return false;
3733 }
3734 }
3735
3736 return true;
3737}
3738#endif
3739
3740inline bool Client::redirect(const Request &req, Response &res) {
3741 if (req.redirect_count == 0) { return false; }
3742
3743 auto location = res.get_header_value("location");
3744 if (location.empty()) { return false; } 4803 if (location.empty()) { return false; }
3745 4804
3746 const static std::regex re( 4805 const static std::regex re(
3747 R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); 4806 R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
3748 4807
3749 std::smatch m; 4808 std::smatch m;
3750 if (!regex_match(location, m, re)) { return false; } 4809 if (!std::regex_match(location, m, re)) { return false; }
3751 4810
3752 auto scheme = is_ssl() ? "https" : "http"; 4811 auto scheme = is_ssl() ? "https" : "http";
3753 4812
3754 auto next_scheme = m[1].str(); 4813 auto next_scheme = m[1].str();
3755 auto next_host = m[2].str(); 4814 auto next_host = m[2].str();
3756 auto next_path = m[3].str(); 4815 auto port_str = m[3].str();
3757 if (next_scheme.empty()) { next_scheme = scheme; } 4816 auto next_path = m[4].str();
4817
4818 auto next_port = port_;
4819 if (!port_str.empty()) {
4820 next_port = std::stoi(port_str);
4821 } else if (!next_scheme.empty()) {
4822 next_port = next_scheme == "https" ? 443 : 80;
4823 }
4824
3758 if (next_scheme.empty()) { next_scheme = scheme; } 4825 if (next_scheme.empty()) { next_scheme = scheme; }
3759 if (next_host.empty()) { next_host = host_; } 4826 if (next_host.empty()) { next_host = host_; }
3760 if (next_path.empty()) { next_path = "/"; } 4827 if (next_path.empty()) { next_path = "/"; }
3761 4828
3762 if (next_scheme == scheme && next_host == host_) { 4829 if (next_scheme == scheme && next_host == host_ && next_port == port_) {
3763 return detail::redirect(*this, req, res, next_path); 4830 return detail::redirect(*this, req, res, next_path);
3764 } else { 4831 } else {
3765 if (next_scheme == "https") { 4832 if (next_scheme == "https") {
3766#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 4833#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3767 SSLClient cli(next_host.c_str()); 4834 SSLClient cli(next_host.c_str(), next_port);
3768 cli.copy_settings(*this); 4835 cli.copy_settings(*this);
3769 return detail::redirect(cli, req, res, next_path); 4836 auto ret = detail::redirect(cli, req, res, next_path);
4837 if (!ret) { error_ = cli.get_last_error(); }
4838 return ret;
3770#else 4839#else
3771 return false; 4840 return false;
3772#endif 4841#endif
3773 } else { 4842 } else {
3774 Client cli(next_host.c_str()); 4843 ClientImpl cli(next_host.c_str(), next_port);
3775 cli.copy_settings(*this); 4844 cli.copy_settings(*this);
3776 return detail::redirect(cli, req, res, next_path); 4845 auto ret = detail::redirect(cli, req, res, next_path);
4846 if (!ret) { error_ = cli.get_last_error(); }
4847 return ret;
3777 } 4848 }
3778 } 4849 }
3779} 4850}
3780 4851
3781inline bool Client::write_request(Stream &strm, const Request &req, 4852inline bool ClientImpl::write_request(Stream &strm, const Request &req,
3782 bool last_connection) { 4853 bool close_connection) {
3783 detail::BufferStream bstrm; 4854 detail::BufferStream bstrm;
3784 4855
3785 // Request line 4856 // Request line
@@ -3789,7 +4860,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3789 4860
3790 // Additonal headers 4861 // Additonal headers
3791 Headers headers; 4862 Headers headers;
3792 if (last_connection) { headers.emplace("Connection", "close"); } 4863 if (close_connection) { headers.emplace("Connection", "close"); }
3793 4864
3794 if (!req.has_header("Host")) { 4865 if (!req.has_header("Host")) {
3795 if (is_ssl()) { 4866 if (is_ssl()) {
@@ -3810,7 +4881,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3810 if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); } 4881 if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
3811 4882
3812 if (!req.has_header("User-Agent")) { 4883 if (!req.has_header("User-Agent")) {
3813 headers.emplace("User-Agent", "cpp-httplib/0.5"); 4884 headers.emplace("User-Agent", "cpp-httplib/0.7");
3814 } 4885 }
3815 4886
3816 if (req.body.empty()) { 4887 if (req.body.empty()) {
@@ -3818,7 +4889,10 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3818 auto length = std::to_string(req.content_length); 4889 auto length = std::to_string(req.content_length);
3819 headers.emplace("Content-Length", length); 4890 headers.emplace("Content-Length", length);
3820 } else { 4891 } else {
3821 headers.emplace("Content-Length", "0"); 4892 if (req.method == "POST" || req.method == "PUT" ||
4893 req.method == "PATCH") {
4894 headers.emplace("Content-Length", "0");
4895 }
3822 } 4896 }
3823 } else { 4897 } else {
3824 if (!req.has_header("Content-Type")) { 4898 if (!req.has_header("Content-Type")) {
@@ -3831,7 +4905,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3831 } 4905 }
3832 } 4906 }
3833 4907
3834 if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) { 4908 if (!basic_auth_password_.empty()) {
3835 headers.insert(make_basic_authentication_header( 4909 headers.insert(make_basic_authentication_header(
3836 basic_auth_username_, basic_auth_password_, false)); 4910 basic_auth_username_, basic_auth_password_, false));
3837 } 4911 }
@@ -3842,11 +4916,24 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3842 proxy_basic_auth_username_, proxy_basic_auth_password_, true)); 4916 proxy_basic_auth_username_, proxy_basic_auth_password_, true));
3843 } 4917 }
3844 4918
4919 if (!bearer_token_auth_token_.empty()) {
4920 headers.insert(make_bearer_token_authentication_header(
4921 bearer_token_auth_token_, false));
4922 }
4923
4924 if (!proxy_bearer_token_auth_token_.empty()) {
4925 headers.insert(make_bearer_token_authentication_header(
4926 proxy_bearer_token_auth_token_, true));
4927 }
4928
3845 detail::write_headers(bstrm, req, headers); 4929 detail::write_headers(bstrm, req, headers);
3846 4930
3847 // Flush buffer 4931 // Flush buffer
3848 auto &data = bstrm.get_buffer(); 4932 auto &data = bstrm.get_buffer();
3849 strm.write(data.data(), data.size()); 4933 if (!detail::write_data(strm, data.data(), data.size())) {
4934 error_ = Error::Write;
4935 return false;
4936 }
3850 4937
3851 // Body 4938 // Body
3852 if (req.body.empty()) { 4939 if (req.body.empty()) {
@@ -3854,55 +4941,95 @@ inline bool Client::write_request(Stream &strm, const Request &req,
3854 size_t offset = 0; 4941 size_t offset = 0;
3855 size_t end_offset = req.content_length; 4942 size_t end_offset = req.content_length;
3856 4943
4944 bool ok = true;
4945
3857 DataSink data_sink; 4946 DataSink data_sink;
3858 data_sink.write = [&](const char *d, size_t l) { 4947 data_sink.write = [&](const char *d, size_t l) {
3859 auto written_length = strm.write(d, l); 4948 if (ok) {
3860 offset += written_length; 4949 if (detail::write_data(strm, d, l)) {
4950 offset += l;
4951 } else {
4952 ok = false;
4953 }
4954 }
3861 }; 4955 };
3862 data_sink.is_writable = [&](void) { return strm.is_writable(); }; 4956 data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
3863 4957
3864 while (offset < end_offset) { 4958 while (offset < end_offset) {
3865 req.content_provider(offset, end_offset - offset, data_sink); 4959 if (!req.content_provider(offset, end_offset - offset, data_sink)) {
4960 error_ = Error::Canceled;
4961 return false;
4962 }
4963 if (!ok) {
4964 error_ = Error::Write;
4965 return false;
4966 }
3866 } 4967 }
3867 } 4968 }
3868 } else { 4969 } else {
3869 strm.write(req.body); 4970 return detail::write_data(strm, req.body.data(), req.body.size());
3870 } 4971 }
3871 4972
3872 return true; 4973 return true;
3873} 4974}
3874 4975
3875inline std::shared_ptr<Response> Client::send_with_content_provider( 4976inline std::shared_ptr<Response> ClientImpl::send_with_content_provider(
3876 const char *method, const char *path, const Headers &headers, 4977 const char *method, const char *path, const Headers &headers,
3877 const std::string &body, size_t content_length, 4978 const std::string &body, size_t content_length,
3878 ContentProvider content_provider, const char *content_type) { 4979 ContentProvider content_provider, const char *content_type) {
4980
3879 Request req; 4981 Request req;
3880 req.method = method; 4982 req.method = method;
3881 req.headers = headers; 4983 req.headers = default_headers_;
4984 req.headers.insert(headers.begin(), headers.end());
3882 req.path = path; 4985 req.path = path;
3883 4986
3884 req.headers.emplace("Content-Type", content_type); 4987 if (content_type) { req.headers.emplace("Content-Type", content_type); }
3885 4988
3886#ifdef CPPHTTPLIB_ZLIB_SUPPORT 4989#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3887 if (compress_) { 4990 if (compress_) {
4991 detail::gzip_compressor compressor;
4992
3888 if (content_provider) { 4993 if (content_provider) {
4994 auto ok = true;
3889 size_t offset = 0; 4995 size_t offset = 0;
3890 4996
3891 DataSink data_sink; 4997 DataSink data_sink;
3892 data_sink.write = [&](const char *data, size_t data_len) { 4998 data_sink.write = [&](const char *data, size_t data_len) {
3893 req.body.append(data, data_len); 4999 if (ok) {
3894 offset += data_len; 5000 auto last = offset + data_len == content_length;
5001
5002 auto ret = compressor.compress(
5003 data, data_len, last, [&](const char *data, size_t data_len) {
5004 req.body.append(data, data_len);
5005 return true;
5006 });
5007
5008 if (ret) {
5009 offset += data_len;
5010 } else {
5011 ok = false;
5012 }
5013 }
3895 }; 5014 };
3896 data_sink.is_writable = [&](void) { return true; }; 5015 data_sink.is_writable = [&](void) { return ok && true; };
3897 5016
3898 while (offset < content_length) { 5017 while (ok && offset < content_length) {
3899 content_provider(offset, content_length - offset, data_sink); 5018 if (!content_provider(offset, content_length - offset, data_sink)) {
5019 error_ = Error::Canceled;
5020 return nullptr;
5021 }
3900 } 5022 }
3901 } else { 5023 } else {
3902 req.body = body; 5024 if (!compressor.compress(body.data(), body.size(), true,
5025 [&](const char *data, size_t data_len) {
5026 req.body.append(data, data_len);
5027 return true;
5028 })) {
5029 return nullptr;
5030 }
3903 } 5031 }
3904 5032
3905 if (!detail::compress(req.body)) { return nullptr; }
3906 req.headers.emplace("Content-Encoding", "gzip"); 5033 req.headers.emplace("Content-Encoding", "gzip");
3907 } else 5034 } else
3908#endif 5035#endif
@@ -3920,215 +5047,232 @@ inline std::shared_ptr<Response> Client::send_with_content_provider(
3920 return send(req, *res) ? res : nullptr; 5047 return send(req, *res) ? res : nullptr;
3921} 5048}
3922 5049
3923inline bool Client::process_request(Stream &strm, const Request &req, 5050inline bool ClientImpl::process_request(Stream &strm, const Request &req,
3924 Response &res, bool last_connection, 5051 Response &res, bool close_connection) {
3925 bool &connection_close) {
3926 // Send request 5052 // Send request
3927 if (!write_request(strm, req, last_connection)) { return false; } 5053 if (!write_request(strm, req, close_connection)) { return false; }
3928 5054
3929 // Receive response and headers 5055 // Receive response and headers
3930 if (!read_response_line(strm, res) || 5056 if (!read_response_line(strm, res) ||
3931 !detail::read_headers(strm, res.headers)) { 5057 !detail::read_headers(strm, res.headers)) {
5058 error_ = Error::Read;
3932 return false; 5059 return false;
3933 } 5060 }
3934 5061
3935 if (res.get_header_value("Connection") == "close" ||
3936 res.version == "HTTP/1.0") {
3937 connection_close = true;
3938 }
3939
3940 if (req.response_handler) { 5062 if (req.response_handler) {
3941 if (!req.response_handler(res)) { return false; } 5063 if (!req.response_handler(res)) {
5064 error_ = Error::Canceled;
5065 return false;
5066 }
3942 } 5067 }
3943 5068
3944 // Body 5069 // Body
3945 if (req.method != "HEAD" && req.method != "CONNECT") { 5070 if (req.method != "HEAD" && req.method != "CONNECT") {
3946 ContentReceiver out = [&](const char *buf, size_t n) { 5071 auto out =
3947 if (res.body.size() + n > res.body.max_size()) { return false; } 5072 req.content_receiver
3948 res.body.append(buf, n); 5073 ? static_cast<ContentReceiver>([&](const char *buf, size_t n) {
3949 return true; 5074 auto ret = req.content_receiver(buf, n);
5075 if (!ret) { error_ = Error::Canceled; }
5076 return ret;
5077 })
5078 : static_cast<ContentReceiver>([&](const char *buf, size_t n) {
5079 if (res.body.size() + n > res.body.max_size()) { return false; }
5080 res.body.append(buf, n);
5081 return true;
5082 });
5083
5084 auto progress = [&](uint64_t current, uint64_t total) {
5085 if (!req.progress) { return true; }
5086 auto ret = req.progress(current, total);
5087 if (!ret) { error_ = Error::Canceled; }
5088 return ret;
3950 }; 5089 };
3951 5090
3952 if (req.content_receiver) {
3953 out = [&](const char *buf, size_t n) {
3954 return req.content_receiver(buf, n);
3955 };
3956 }
3957
3958 int dummy_status; 5091 int dummy_status;
3959 if (!detail::read_content(strm, res, std::numeric_limits<size_t>::max(), 5092 if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
3960 dummy_status, req.progress, out)) { 5093 dummy_status, progress, out, decompress_)) {
5094 if (error_ != Error::Canceled) { error_ = Error::Read; }
3961 return false; 5095 return false;
3962 } 5096 }
3963 } 5097 }
3964 5098
5099 if (res.get_header_value("Connection") == "close" ||
5100 (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
5101 stop_core();
5102 }
5103
3965 // Log 5104 // Log
3966 if (logger_) { logger_(req, res); } 5105 if (logger_) { logger_(req, res); }
3967 5106
3968 return true; 5107 return true;
3969} 5108}
3970 5109
3971inline bool Client::process_and_close_socket( 5110inline bool
3972 socket_t sock, size_t request_count, 5111ClientImpl::process_socket(Socket &socket,
3973 std::function<bool(Stream &strm, bool last_connection, 5112 std::function<bool(Stream &strm)> callback) {
3974 bool &connection_close)> 5113 return detail::process_client_socket(socket.sock, read_timeout_sec_,
3975 callback) { 5114 read_timeout_usec_, write_timeout_sec_,
3976 request_count = std::min(request_count, keep_alive_max_count_); 5115 write_timeout_usec_, callback);
3977 return detail::process_and_close_socket(true, sock, request_count,
3978 read_timeout_sec_, read_timeout_usec_,
3979 callback);
3980} 5116}
3981 5117
3982inline bool Client::is_ssl() const { return false; } 5118inline bool ClientImpl::is_ssl() const { return false; }
3983 5119
3984inline std::shared_ptr<Response> Client::Get(const char *path) { 5120inline Result ClientImpl::Get(const char *path) {
3985 return Get(path, Headers(), Progress()); 5121 return Get(path, Headers(), Progress());
3986} 5122}
3987 5123
3988inline std::shared_ptr<Response> Client::Get(const char *path, 5124inline Result ClientImpl::Get(const char *path, Progress progress) {
3989 Progress progress) {
3990 return Get(path, Headers(), std::move(progress)); 5125 return Get(path, Headers(), std::move(progress));
3991} 5126}
3992 5127
3993inline std::shared_ptr<Response> Client::Get(const char *path, 5128inline Result ClientImpl::Get(const char *path, const Headers &headers) {
3994 const Headers &headers) {
3995 return Get(path, headers, Progress()); 5129 return Get(path, headers, Progress());
3996} 5130}
3997 5131
3998inline std::shared_ptr<Response> 5132inline Result ClientImpl::Get(const char *path, const Headers &headers,
3999Client::Get(const char *path, const Headers &headers, Progress progress) { 5133 Progress progress) {
4000 Request req; 5134 Request req;
4001 req.method = "GET"; 5135 req.method = "GET";
4002 req.path = path; 5136 req.path = path;
4003 req.headers = headers; 5137 req.headers = default_headers_;
5138 req.headers.insert(headers.begin(), headers.end());
4004 req.progress = std::move(progress); 5139 req.progress = std::move(progress);
4005 5140
4006 auto res = std::make_shared<Response>(); 5141 auto res = std::make_shared<Response>();
4007 return send(req, *res) ? res : nullptr; 5142 auto ret = send(req, *res);
5143 return Result{ret ? res : nullptr, get_last_error()};
4008} 5144}
4009 5145
4010inline std::shared_ptr<Response> Client::Get(const char *path, 5146inline Result ClientImpl::Get(const char *path,
4011 ContentReceiver content_receiver) { 5147 ContentReceiver content_receiver) {
4012 return Get(path, Headers(), nullptr, std::move(content_receiver), Progress()); 5148 return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
4013} 5149}
4014 5150
4015inline std::shared_ptr<Response> Client::Get(const char *path, 5151inline Result ClientImpl::Get(const char *path,
4016 ContentReceiver content_receiver, 5152 ContentReceiver content_receiver,
4017 Progress progress) { 5153 Progress progress) {
4018 return Get(path, Headers(), nullptr, std::move(content_receiver), 5154 return Get(path, Headers(), nullptr, std::move(content_receiver),
4019 std::move(progress)); 5155 std::move(progress));
4020} 5156}
4021 5157
4022inline std::shared_ptr<Response> Client::Get(const char *path, 5158inline Result ClientImpl::Get(const char *path, const Headers &headers,
4023 const Headers &headers, 5159 ContentReceiver content_receiver) {
4024 ContentReceiver content_receiver) { 5160 return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
4025 return Get(path, headers, nullptr, std::move(content_receiver), Progress());
4026} 5161}
4027 5162
4028inline std::shared_ptr<Response> Client::Get(const char *path, 5163inline Result ClientImpl::Get(const char *path, const Headers &headers,
4029 const Headers &headers, 5164 ContentReceiver content_receiver,
4030 ContentReceiver content_receiver, 5165 Progress progress) {
4031 Progress progress) {
4032 return Get(path, headers, nullptr, std::move(content_receiver), 5166 return Get(path, headers, nullptr, std::move(content_receiver),
4033 std::move(progress)); 5167 std::move(progress));
4034} 5168}
4035 5169
4036inline std::shared_ptr<Response> Client::Get(const char *path, 5170inline Result ClientImpl::Get(const char *path,
4037 const Headers &headers, 5171 ResponseHandler response_handler,
4038 ResponseHandler response_handler, 5172 ContentReceiver content_receiver) {
4039 ContentReceiver content_receiver) { 5173 return Get(path, Headers(), std::move(response_handler), content_receiver,
5174 nullptr);
5175}
5176
5177inline Result ClientImpl::Get(const char *path, const Headers &headers,
5178 ResponseHandler response_handler,
5179 ContentReceiver content_receiver) {
4040 return Get(path, headers, std::move(response_handler), content_receiver, 5180 return Get(path, headers, std::move(response_handler), content_receiver,
4041 Progress()); 5181 nullptr);
4042} 5182}
4043 5183
4044inline std::shared_ptr<Response> Client::Get(const char *path, 5184inline Result ClientImpl::Get(const char *path,
4045 const Headers &headers, 5185 ResponseHandler response_handler,
4046 ResponseHandler response_handler, 5186 ContentReceiver content_receiver,
4047 ContentReceiver content_receiver, 5187 Progress progress) {
4048 Progress progress) { 5188 return Get(path, Headers(), std::move(response_handler), content_receiver,
5189 progress);
5190}
5191
5192inline Result ClientImpl::Get(const char *path, const Headers &headers,
5193 ResponseHandler response_handler,
5194 ContentReceiver content_receiver,
5195 Progress progress) {
4049 Request req; 5196 Request req;
4050 req.method = "GET"; 5197 req.method = "GET";
4051 req.path = path; 5198 req.path = path;
4052 req.headers = headers; 5199 req.headers = default_headers_;
5200 req.headers.insert(headers.begin(), headers.end());
4053 req.response_handler = std::move(response_handler); 5201 req.response_handler = std::move(response_handler);
4054 req.content_receiver = std::move(content_receiver); 5202 req.content_receiver = std::move(content_receiver);
4055 req.progress = std::move(progress); 5203 req.progress = std::move(progress);
4056 5204
4057 auto res = std::make_shared<Response>(); 5205 auto res = std::make_shared<Response>();
4058 return send(req, *res) ? res : nullptr; 5206 auto ret = send(req, *res);
5207 return Result{ret ? res : nullptr, get_last_error()};
4059} 5208}
4060 5209
4061inline std::shared_ptr<Response> Client::Head(const char *path) { 5210inline Result ClientImpl::Head(const char *path) {
4062 return Head(path, Headers()); 5211 return Head(path, Headers());
4063} 5212}
4064 5213
4065inline std::shared_ptr<Response> Client::Head(const char *path, 5214inline Result ClientImpl::Head(const char *path, const Headers &headers) {
4066 const Headers &headers) {
4067 Request req; 5215 Request req;
4068 req.method = "HEAD"; 5216 req.method = "HEAD";
4069 req.headers = headers; 5217 req.headers = default_headers_;
5218 req.headers.insert(headers.begin(), headers.end());
4070 req.path = path; 5219 req.path = path;
4071 5220
4072 auto res = std::make_shared<Response>(); 5221 auto res = std::make_shared<Response>();
5222 auto ret = send(req, *res);
5223 return Result{ret ? res : nullptr, get_last_error()};
5224}
4073 5225
4074 return send(req, *res) ? res : nullptr; 5226inline Result ClientImpl::Post(const char *path) {
5227 return Post(path, std::string(), nullptr);
4075} 5228}
4076 5229
4077inline std::shared_ptr<Response> Client::Post(const char *path, 5230inline Result ClientImpl::Post(const char *path, const std::string &body,
4078 const std::string &body, 5231 const char *content_type) {
4079 const char *content_type) {
4080 return Post(path, Headers(), body, content_type); 5232 return Post(path, Headers(), body, content_type);
4081} 5233}
4082 5234
4083inline std::shared_ptr<Response> Client::Post(const char *path, 5235inline Result ClientImpl::Post(const char *path, const Headers &headers,
4084 const Headers &headers, 5236 const std::string &body,
4085 const std::string &body, 5237 const char *content_type) {
4086 const char *content_type) { 5238 auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
4087 return send_with_content_provider("POST", path, headers, body, 0, nullptr, 5239 content_type);
4088 content_type); 5240 return Result{ret, get_last_error()};
4089} 5241}
4090 5242
4091inline std::shared_ptr<Response> Client::Post(const char *path, 5243inline Result ClientImpl::Post(const char *path, const Params &params) {
4092 const Params &params) {
4093 return Post(path, Headers(), params); 5244 return Post(path, Headers(), params);
4094} 5245}
4095 5246
4096inline std::shared_ptr<Response> Client::Post(const char *path, 5247inline Result ClientImpl::Post(const char *path, size_t content_length,
4097 size_t content_length, 5248 ContentProvider content_provider,
4098 ContentProvider content_provider, 5249 const char *content_type) {
4099 const char *content_type) {
4100 return Post(path, Headers(), content_length, content_provider, content_type); 5250 return Post(path, Headers(), content_length, content_provider, content_type);
4101} 5251}
4102 5252
4103inline std::shared_ptr<Response> 5253inline Result ClientImpl::Post(const char *path, const Headers &headers,
4104Client::Post(const char *path, const Headers &headers, size_t content_length, 5254 size_t content_length,
4105 ContentProvider content_provider, const char *content_type) { 5255 ContentProvider content_provider,
4106 return send_with_content_provider("POST", path, headers, std::string(), 5256 const char *content_type) {
4107 content_length, content_provider, 5257 auto ret = send_with_content_provider("POST", path, headers, std::string(),
4108 content_type); 5258 content_length, content_provider,
5259 content_type);
5260 return Result{ret, get_last_error()};
4109} 5261}
4110 5262
4111inline std::shared_ptr<Response> 5263inline Result ClientImpl::Post(const char *path, const Headers &headers,
4112Client::Post(const char *path, const Headers &headers, const Params &params) { 5264 const Params &params) {
4113 std::string query; 5265 auto query = detail::params_to_query_str(params);
4114 for (auto it = params.begin(); it != params.end(); ++it) {
4115 if (it != params.begin()) { query += "&"; }
4116 query += it->first;
4117 query += "=";
4118 query += detail::encode_url(it->second);
4119 }
4120
4121 return Post(path, headers, query, "application/x-www-form-urlencoded"); 5266 return Post(path, headers, query, "application/x-www-form-urlencoded");
4122} 5267}
4123 5268
4124inline std::shared_ptr<Response> 5269inline Result ClientImpl::Post(const char *path,
4125Client::Post(const char *path, const MultipartFormDataItems &items) { 5270 const MultipartFormDataItems &items) {
4126 return Post(path, Headers(), items); 5271 return Post(path, Headers(), items);
4127} 5272}
4128 5273
4129inline std::shared_ptr<Response> 5274inline Result ClientImpl::Post(const char *path, const Headers &headers,
4130Client::Post(const char *path, const Headers &headers, 5275 const MultipartFormDataItems &items) {
4131 const MultipartFormDataItems &items) {
4132 auto boundary = detail::make_multipart_data_boundary(); 5276 auto boundary = detail::make_multipart_data_boundary();
4133 5277
4134 std::string body; 5278 std::string body;
@@ -4153,182 +5297,229 @@ Client::Post(const char *path, const Headers &headers,
4153 return Post(path, headers, body, content_type.c_str()); 5297 return Post(path, headers, body, content_type.c_str());
4154} 5298}
4155 5299
4156inline std::shared_ptr<Response> Client::Put(const char *path, 5300inline Result ClientImpl::Put(const char *path) {
4157 const std::string &body, 5301 return Put(path, std::string(), nullptr);
4158 const char *content_type) { 5302}
5303
5304inline Result ClientImpl::Put(const char *path, const std::string &body,
5305 const char *content_type) {
4159 return Put(path, Headers(), body, content_type); 5306 return Put(path, Headers(), body, content_type);
4160} 5307}
4161 5308
4162inline std::shared_ptr<Response> Client::Put(const char *path, 5309inline Result ClientImpl::Put(const char *path, const Headers &headers,
4163 const Headers &headers, 5310 const std::string &body,
4164 const std::string &body, 5311 const char *content_type) {
4165 const char *content_type) { 5312 auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
4166 return send_with_content_provider("PUT", path, headers, body, 0, nullptr, 5313 content_type);
4167 content_type); 5314 return Result{ret, get_last_error()};
4168} 5315}
4169 5316
4170inline std::shared_ptr<Response> Client::Put(const char *path, 5317inline Result ClientImpl::Put(const char *path, size_t content_length,
4171 size_t content_length, 5318 ContentProvider content_provider,
4172 ContentProvider content_provider, 5319 const char *content_type) {
4173 const char *content_type) {
4174 return Put(path, Headers(), content_length, content_provider, content_type); 5320 return Put(path, Headers(), content_length, content_provider, content_type);
4175} 5321}
4176 5322
4177inline std::shared_ptr<Response> 5323inline Result ClientImpl::Put(const char *path, const Headers &headers,
4178Client::Put(const char *path, const Headers &headers, size_t content_length, 5324 size_t content_length,
4179 ContentProvider content_provider, const char *content_type) { 5325 ContentProvider content_provider,
4180 return send_with_content_provider("PUT", path, headers, std::string(), 5326 const char *content_type) {
4181 content_length, content_provider, 5327 auto ret = send_with_content_provider("PUT", path, headers, std::string(),
4182 content_type); 5328 content_length, content_provider,
5329 content_type);
5330 return Result{ret, get_last_error()};
4183} 5331}
4184 5332
4185inline std::shared_ptr<Response> Client::Put(const char *path, 5333inline Result ClientImpl::Put(const char *path, const Params &params) {
4186 const Params &params) {
4187 return Put(path, Headers(), params); 5334 return Put(path, Headers(), params);
4188} 5335}
4189 5336
4190inline std::shared_ptr<Response> 5337inline Result ClientImpl::Put(const char *path, const Headers &headers,
4191Client::Put(const char *path, const Headers &headers, const Params &params) { 5338 const Params &params) {
4192 std::string query; 5339 auto query = detail::params_to_query_str(params);
4193 for (auto it = params.begin(); it != params.end(); ++it) {
4194 if (it != params.begin()) { query += "&"; }
4195 query += it->first;
4196 query += "=";
4197 query += detail::encode_url(it->second);
4198 }
4199
4200 return Put(path, headers, query, "application/x-www-form-urlencoded"); 5340 return Put(path, headers, query, "application/x-www-form-urlencoded");
4201} 5341}
4202 5342
4203inline std::shared_ptr<Response> Client::Patch(const char *path, 5343inline Result ClientImpl::Patch(const char *path, const std::string &body,
4204 const std::string &body, 5344 const char *content_type) {
4205 const char *content_type) {
4206 return Patch(path, Headers(), body, content_type); 5345 return Patch(path, Headers(), body, content_type);
4207} 5346}
4208 5347
4209inline std::shared_ptr<Response> Client::Patch(const char *path, 5348inline Result ClientImpl::Patch(const char *path, const Headers &headers,
4210 const Headers &headers, 5349 const std::string &body,
4211 const std::string &body, 5350 const char *content_type) {
4212 const char *content_type) { 5351 auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
4213 return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, 5352 nullptr, content_type);
4214 content_type); 5353 return Result{ret, get_last_error()};
4215} 5354}
4216 5355
4217inline std::shared_ptr<Response> Client::Patch(const char *path, 5356inline Result ClientImpl::Patch(const char *path, size_t content_length,
4218 size_t content_length, 5357 ContentProvider content_provider,
4219 ContentProvider content_provider, 5358 const char *content_type) {
4220 const char *content_type) {
4221 return Patch(path, Headers(), content_length, content_provider, content_type); 5359 return Patch(path, Headers(), content_length, content_provider, content_type);
4222} 5360}
4223 5361
4224inline std::shared_ptr<Response> 5362inline Result ClientImpl::Patch(const char *path, const Headers &headers,
4225Client::Patch(const char *path, const Headers &headers, size_t content_length, 5363 size_t content_length,
4226 ContentProvider content_provider, const char *content_type) { 5364 ContentProvider content_provider,
4227 return send_with_content_provider("PATCH", path, headers, std::string(), 5365 const char *content_type) {
4228 content_length, content_provider, 5366 auto ret = send_with_content_provider("PATCH", path, headers, std::string(),
4229 content_type); 5367 content_length, content_provider,
5368 content_type);
5369 return Result{ret, get_last_error()};
4230} 5370}
4231 5371
4232inline std::shared_ptr<Response> Client::Delete(const char *path) { 5372inline Result ClientImpl::Delete(const char *path) {
4233 return Delete(path, Headers(), std::string(), nullptr); 5373 return Delete(path, Headers(), std::string(), nullptr);
4234} 5374}
4235 5375
4236inline std::shared_ptr<Response> Client::Delete(const char *path, 5376inline Result ClientImpl::Delete(const char *path, const std::string &body,
4237 const std::string &body, 5377 const char *content_type) {
4238 const char *content_type) {
4239 return Delete(path, Headers(), body, content_type); 5378 return Delete(path, Headers(), body, content_type);
4240} 5379}
4241 5380
4242inline std::shared_ptr<Response> Client::Delete(const char *path, 5381inline Result ClientImpl::Delete(const char *path, const Headers &headers) {
4243 const Headers &headers) {
4244 return Delete(path, headers, std::string(), nullptr); 5382 return Delete(path, headers, std::string(), nullptr);
4245} 5383}
4246 5384
4247inline std::shared_ptr<Response> Client::Delete(const char *path, 5385inline Result ClientImpl::Delete(const char *path, const Headers &headers,
4248 const Headers &headers, 5386 const std::string &body,
4249 const std::string &body, 5387 const char *content_type) {
4250 const char *content_type) {
4251 Request req; 5388 Request req;
4252 req.method = "DELETE"; 5389 req.method = "DELETE";
4253 req.headers = headers; 5390 req.headers = default_headers_;
5391 req.headers.insert(headers.begin(), headers.end());
4254 req.path = path; 5392 req.path = path;
4255 5393
4256 if (content_type) { req.headers.emplace("Content-Type", content_type); } 5394 if (content_type) { req.headers.emplace("Content-Type", content_type); }
4257 req.body = body; 5395 req.body = body;
4258 5396
4259 auto res = std::make_shared<Response>(); 5397 auto res = std::make_shared<Response>();
4260 5398 auto ret = send(req, *res);
4261 return send(req, *res) ? res : nullptr; 5399 return Result{ret ? res : nullptr, get_last_error()};
4262} 5400}
4263 5401
4264inline std::shared_ptr<Response> Client::Options(const char *path) { 5402inline Result ClientImpl::Options(const char *path) {
4265 return Options(path, Headers()); 5403 return Options(path, Headers());
4266} 5404}
4267 5405
4268inline std::shared_ptr<Response> Client::Options(const char *path, 5406inline Result ClientImpl::Options(const char *path, const Headers &headers) {
4269 const Headers &headers) {
4270 Request req; 5407 Request req;
4271 req.method = "OPTIONS"; 5408 req.method = "OPTIONS";
5409 req.headers = default_headers_;
5410 req.headers.insert(headers.begin(), headers.end());
4272 req.path = path; 5411 req.path = path;
4273 req.headers = headers;
4274 5412
4275 auto res = std::make_shared<Response>(); 5413 auto res = std::make_shared<Response>();
5414 auto ret = send(req, *res);
5415 return Result{ret ? res : nullptr, get_last_error()};
5416}
4276 5417
4277 return send(req, *res) ? res : nullptr; 5418inline size_t ClientImpl::is_socket_open() const {
5419 std::lock_guard<std::mutex> guard(socket_mutex_);
5420 return socket_.is_open();
4278} 5421}
4279 5422
4280inline void Client::set_timeout_sec(time_t timeout_sec) { 5423inline void ClientImpl::stop() {
4281 timeout_sec_ = timeout_sec; 5424 stop_core();
5425 error_ = Error::Canceled;
4282} 5426}
4283 5427
4284inline void Client::set_read_timeout(time_t sec, time_t usec) { 5428inline void ClientImpl::stop_core() {
5429 std::lock_guard<std::mutex> guard(socket_mutex_);
5430 if (socket_.is_open()) {
5431 detail::shutdown_socket(socket_.sock);
5432 std::this_thread::sleep_for(std::chrono::milliseconds(1));
5433 close_socket(socket_, true);
5434 std::this_thread::sleep_for(std::chrono::milliseconds(1));
5435 }
5436}
5437
5438inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
5439 connection_timeout_sec_ = sec;
5440 connection_timeout_usec_ = usec;
5441}
5442
5443inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
4285 read_timeout_sec_ = sec; 5444 read_timeout_sec_ = sec;
4286 read_timeout_usec_ = usec; 5445 read_timeout_usec_ = usec;
4287} 5446}
4288 5447
4289inline void Client::set_keep_alive_max_count(size_t count) { 5448inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
4290 keep_alive_max_count_ = count; 5449 write_timeout_sec_ = sec;
5450 write_timeout_usec_ = usec;
4291} 5451}
4292 5452
4293inline void Client::set_basic_auth(const char *username, const char *password) { 5453inline void ClientImpl::set_basic_auth(const char *username,
5454 const char *password) {
4294 basic_auth_username_ = username; 5455 basic_auth_username_ = username;
4295 basic_auth_password_ = password; 5456 basic_auth_password_ = password;
4296} 5457}
4297 5458
5459inline void ClientImpl::set_bearer_token_auth(const char *token) {
5460 bearer_token_auth_token_ = token;
5461}
5462
4298#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 5463#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4299inline void Client::set_digest_auth(const char *username, 5464inline void ClientImpl::set_digest_auth(const char *username,
4300 const char *password) { 5465 const char *password) {
4301 digest_auth_username_ = username; 5466 digest_auth_username_ = username;
4302 digest_auth_password_ = password; 5467 digest_auth_password_ = password;
4303} 5468}
4304#endif 5469#endif
4305 5470
4306inline void Client::set_follow_location(bool on) { follow_location_ = on; } 5471inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
4307 5472
4308inline void Client::set_compress(bool on) { compress_ = on; } 5473inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
4309 5474
4310inline void Client::set_interface(const char *intf) { interface_ = intf; } 5475inline void ClientImpl::set_default_headers(Headers headers) {
5476 default_headers_ = std::move(headers);
5477}
4311 5478
4312inline void Client::set_proxy(const char *host, int port) { 5479inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
5480
5481inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
5482 socket_options_ = socket_options;
5483}
5484
5485inline void ClientImpl::set_compress(bool on) { compress_ = on; }
5486
5487inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
5488
5489inline void ClientImpl::set_interface(const char *intf) { interface_ = intf; }
5490
5491inline void ClientImpl::set_proxy(const char *host, int port) {
4313 proxy_host_ = host; 5492 proxy_host_ = host;
4314 proxy_port_ = port; 5493 proxy_port_ = port;
4315} 5494}
4316 5495
4317inline void Client::set_proxy_basic_auth(const char *username, 5496inline void ClientImpl::set_proxy_basic_auth(const char *username,
4318 const char *password) { 5497 const char *password) {
4319 proxy_basic_auth_username_ = username; 5498 proxy_basic_auth_username_ = username;
4320 proxy_basic_auth_password_ = password; 5499 proxy_basic_auth_password_ = password;
4321} 5500}
4322 5501
5502inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) {
5503 proxy_bearer_token_auth_token_ = token;
5504}
5505
4323#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 5506#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4324inline void Client::set_proxy_digest_auth(const char *username, 5507inline void ClientImpl::set_proxy_digest_auth(const char *username,
4325 const char *password) { 5508 const char *password) {
4326 proxy_digest_auth_username_ = username; 5509 proxy_digest_auth_username_ = username;
4327 proxy_digest_auth_password_ = password; 5510 proxy_digest_auth_password_ = password;
4328} 5511}
4329#endif 5512#endif
4330 5513
4331inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); } 5514#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5515inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
5516 server_certificate_verification_ = enabled;
5517}
5518#endif
5519
5520inline void ClientImpl::set_logger(Logger logger) {
5521 logger_ = std::move(logger);
5522}
4332 5523
4333/* 5524/*
4334 * SSL Implementation 5525 * SSL Implementation
@@ -4336,72 +5527,66 @@ inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); }
4336#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 5527#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
4337namespace detail { 5528namespace detail {
4338 5529
4339template <typename U, typename V, typename T> 5530template <typename U, typename V>
4340inline bool process_and_close_socket_ssl( 5531inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
4341 bool is_client_request, socket_t sock, size_t keep_alive_max_count, 5532 U SSL_connect_or_accept, V setup) {
4342 time_t read_timeout_sec, time_t read_timeout_usec, SSL_CTX *ctx,
4343 std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup, T callback) {
4344 assert(keep_alive_max_count > 0);
4345
4346 SSL *ssl = nullptr; 5533 SSL *ssl = nullptr;
4347 { 5534 {
4348 std::lock_guard<std::mutex> guard(ctx_mutex); 5535 std::lock_guard<std::mutex> guard(ctx_mutex);
4349 ssl = SSL_new(ctx); 5536 ssl = SSL_new(ctx);
4350 } 5537 }
4351 5538
4352 if (!ssl) { 5539 if (ssl) {
4353 close_socket(sock); 5540 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
4354 return false; 5541 SSL_set_bio(ssl, bio, bio);
4355 }
4356
4357 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
4358 SSL_set_bio(ssl, bio, bio);
4359 5542
4360 if (!setup(ssl)) { 5543 if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
4361 SSL_shutdown(ssl); 5544 SSL_shutdown(ssl);
4362 { 5545 {
4363 std::lock_guard<std::mutex> guard(ctx_mutex); 5546 std::lock_guard<std::mutex> guard(ctx_mutex);
4364 SSL_free(ssl); 5547 SSL_free(ssl);
5548 }
5549 return nullptr;
4365 } 5550 }
4366
4367 close_socket(sock);
4368 return false;
4369 } 5551 }
4370 5552
4371 auto ret = false; 5553 return ssl;
5554}
4372 5555
4373 if (SSL_connect_or_accept(ssl) == 1) { 5556inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
4374 if (keep_alive_max_count > 1) { 5557 bool process_socket_ret) {
4375 auto count = keep_alive_max_count; 5558 if (process_socket_ret) {
4376 while (count > 0 && 5559 SSL_shutdown(ssl); // shutdown only if not already closed by remote
4377 (is_client_request ||
4378 detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
4379 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
4380 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
4381 auto last_connection = count == 1;
4382 auto connection_close = false;
4383
4384 ret = callback(ssl, strm, last_connection, connection_close);
4385 if (!ret || connection_close) { break; }
4386
4387 count--;
4388 }
4389 } else {
4390 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
4391 auto dummy_connection_close = false;
4392 ret = callback(ssl, strm, true, dummy_connection_close);
4393 }
4394 } 5560 }
4395 5561
4396 SSL_shutdown(ssl); 5562 std::lock_guard<std::mutex> guard(ctx_mutex);
4397 { 5563 SSL_free(ssl);
4398 std::lock_guard<std::mutex> guard(ctx_mutex); 5564}
4399 SSL_free(ssl);
4400 }
4401 5565
4402 close_socket(sock); 5566template <typename T>
5567inline bool
5568process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count,
5569 time_t keep_alive_timeout_sec,
5570 time_t read_timeout_sec, time_t read_timeout_usec,
5571 time_t write_timeout_sec, time_t write_timeout_usec,
5572 T callback) {
5573 return process_server_socket_core(
5574 sock, keep_alive_max_count, keep_alive_timeout_sec,
5575 [&](bool close_connection, bool &connection_closed) {
5576 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
5577 write_timeout_sec, write_timeout_usec);
5578 return callback(strm, close_connection, connection_closed);
5579 });
5580}
4403 5581
4404 return ret; 5582template <typename T>
5583inline bool
5584process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,
5585 time_t read_timeout_usec, time_t write_timeout_sec,
5586 time_t write_timeout_usec, T callback) {
5587 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
5588 write_timeout_sec, write_timeout_usec);
5589 return callback(strm);
4405} 5590}
4406 5591
4407#if OPENSSL_VERSION_NUMBER < 0x10100000L 5592#if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -4420,11 +5605,11 @@ public:
4420private: 5605private:
4421 static void locking_callback(int mode, int type, const char * /*file*/, 5606 static void locking_callback(int mode, int type, const char * /*file*/,
4422 int /*line*/) { 5607 int /*line*/) {
4423 auto &locks = *openSSL_locks_; 5608 auto &lk = (*openSSL_locks_)[static_cast<size_t>(type)];
4424 if (mode & CRYPTO_LOCK) { 5609 if (mode & CRYPTO_LOCK) {
4425 locks[type].lock(); 5610 lk.lock();
4426 } else { 5611 } else {
4427 locks[type].unlock(); 5612 lk.unlock();
4428 } 5613 }
4429 } 5614 }
4430}; 5615};
@@ -4458,9 +5643,30 @@ private:
4458// SSL socket stream implementation 5643// SSL socket stream implementation
4459inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, 5644inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
4460 time_t read_timeout_sec, 5645 time_t read_timeout_sec,
4461 time_t read_timeout_usec) 5646 time_t read_timeout_usec,
5647 time_t write_timeout_sec,
5648 time_t write_timeout_usec)
4462 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), 5649 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
4463 read_timeout_usec_(read_timeout_usec) {} 5650 read_timeout_usec_(read_timeout_usec),
5651 write_timeout_sec_(write_timeout_sec),
5652 write_timeout_usec_(write_timeout_usec) {
5653 {
5654 timeval tv;
5655 tv.tv_sec = static_cast<long>(read_timeout_sec);
5656 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
5657
5658 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&tv),
5659 sizeof(tv));
5660 }
5661 {
5662 timeval tv;
5663 tv.tv_sec = static_cast<long>(write_timeout_sec);
5664 tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
5665
5666 setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&tv),
5667 sizeof(tv));
5668 }
5669}
4464 5670
4465inline SSLSocketStream::~SSLSocketStream() {} 5671inline SSLSocketStream::~SSLSocketStream() {}
4466 5672
@@ -4469,24 +5675,25 @@ inline bool SSLSocketStream::is_readable() const {
4469} 5675}
4470 5676
4471inline bool SSLSocketStream::is_writable() const { 5677inline bool SSLSocketStream::is_writable() const {
4472 return detail::select_write(sock_, 0, 0) > 0; 5678 return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) >
5679 0;
4473} 5680}
4474 5681
4475inline int SSLSocketStream::read(char *ptr, size_t size) { 5682inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
4476 if (SSL_pending(ssl_) > 0 || 5683 if (SSL_pending(ssl_) > 0 || is_readable()) {
4477 select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
4478 return SSL_read(ssl_, ptr, static_cast<int>(size)); 5684 return SSL_read(ssl_, ptr, static_cast<int>(size));
4479 } 5685 }
4480 return -1; 5686 return -1;
4481} 5687}
4482 5688
4483inline int SSLSocketStream::write(const char *ptr, size_t size) { 5689inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
4484 if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); } 5690 if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); }
4485 return -1; 5691 return -1;
4486} 5692}
4487 5693
4488inline std::string SSLSocketStream::get_remote_addr() const { 5694inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
4489 return detail::get_remote_addr(sock_); 5695 int &port) const {
5696 detail::get_remote_ip_and_port(sock_, ip, port);
4490} 5697}
4491 5698
4492static SSLInit sslinit_; 5699static SSLInit sslinit_;
@@ -4532,6 +5739,33 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
4532 } 5739 }
4533} 5740}
4534 5741
5742inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
5743 X509_STORE *client_ca_cert_store) {
5744 ctx_ = SSL_CTX_new(SSLv23_server_method());
5745
5746 if (ctx_) {
5747 SSL_CTX_set_options(ctx_,
5748 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
5749 SSL_OP_NO_COMPRESSION |
5750 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
5751
5752 if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
5753 SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
5754 SSL_CTX_free(ctx_);
5755 ctx_ = nullptr;
5756 } else if (client_ca_cert_store) {
5757
5758 SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
5759
5760 SSL_CTX_set_verify(
5761 ctx_,
5762 SSL_VERIFY_PEER |
5763 SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
5764 nullptr);
5765 }
5766 }
5767}
5768
4535inline SSLServer::~SSLServer() { 5769inline SSLServer::~SSLServer() {
4536 if (ctx_) { SSL_CTX_free(ctx_); } 5770 if (ctx_) { SSL_CTX_free(ctx_); }
4537} 5771}
@@ -4539,21 +5773,39 @@ inline SSLServer::~SSLServer() {
4539inline bool SSLServer::is_valid() const { return ctx_; } 5773inline bool SSLServer::is_valid() const { return ctx_; }
4540 5774
4541inline bool SSLServer::process_and_close_socket(socket_t sock) { 5775inline bool SSLServer::process_and_close_socket(socket_t sock) {
4542 return detail::process_and_close_socket_ssl( 5776 auto ssl = detail::ssl_new(sock, ctx_, ctx_mutex_, SSL_accept,
4543 false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, 5777 [](SSL * /*ssl*/) { return true; });
4544 ctx_, ctx_mutex_, SSL_accept, [](SSL * /*ssl*/) { return true; }, 5778
4545 [this](SSL *ssl, Stream &strm, bool last_connection, 5779 if (ssl) {
4546 bool &connection_close) { 5780 auto ret = detail::process_server_socket_ssl(
4547 return process_request(strm, last_connection, connection_close, 5781 ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
4548 [&](Request &req) { req.ssl = ssl; }); 5782 read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
4549 }); 5783 write_timeout_usec_,
5784 [this, ssl](Stream &strm, bool close_connection,
5785 bool &connection_closed) {
5786 return process_request(strm, close_connection, connection_closed,
5787 [&](Request &req) { req.ssl = ssl; });
5788 });
5789
5790 detail::ssl_delete(ctx_mutex_, ssl, ret);
5791 return ret;
5792 }
5793
5794 detail::close_socket(sock);
5795 return false;
4550} 5796}
4551 5797
4552// SSL HTTP client implementation 5798// SSL HTTP client implementation
5799inline SSLClient::SSLClient(const std::string &host)
5800 : SSLClient(host, 443, std::string(), std::string()) {}
5801
5802inline SSLClient::SSLClient(const std::string &host, int port)
5803 : SSLClient(host, port, std::string(), std::string()) {}
5804
4553inline SSLClient::SSLClient(const std::string &host, int port, 5805inline SSLClient::SSLClient(const std::string &host, int port,
4554 const std::string &client_cert_path, 5806 const std::string &client_cert_path,
4555 const std::string &client_key_path) 5807 const std::string &client_key_path)
4556 : Client(host, port, client_cert_path, client_key_path) { 5808 : ClientImpl(host, port, client_cert_path, client_key_path) {
4557 ctx_ = SSL_CTX_new(SSLv23_client_method()); 5809 ctx_ = SSL_CTX_new(SSLv23_client_method());
4558 5810
4559 detail::split(&host_[0], &host_[host_.size()], '.', 5811 detail::split(&host_[0], &host_[host_.size()], '.',
@@ -4571,6 +5823,24 @@ inline SSLClient::SSLClient(const std::string &host, int port,
4571 } 5823 }
4572} 5824}
4573 5825
5826inline SSLClient::SSLClient(const std::string &host, int port,
5827 X509 *client_cert, EVP_PKEY *client_key)
5828 : ClientImpl(host, port) {
5829 ctx_ = SSL_CTX_new(SSLv23_client_method());
5830
5831 detail::split(&host_[0], &host_[host_.size()], '.',
5832 [&](const char *b, const char *e) {
5833 host_components_.emplace_back(std::string(b, e));
5834 });
5835 if (client_cert != nullptr && client_key != nullptr) {
5836 if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
5837 SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
5838 SSL_CTX_free(ctx_);
5839 ctx_ = nullptr;
5840 }
5841 }
5842}
5843
4574inline SSLClient::~SSLClient() { 5844inline SSLClient::~SSLClient() {
4575 if (ctx_) { SSL_CTX_free(ctx_); } 5845 if (ctx_) { SSL_CTX_free(ctx_); }
4576} 5846}
@@ -4583,67 +5853,178 @@ inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
4583 if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } 5853 if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
4584} 5854}
4585 5855
4586inline void SSLClient::enable_server_certificate_verification(bool enabled) { 5856inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
4587 server_certificate_verification_ = enabled; 5857 if (ca_cert_store) {
5858 if (ctx_) {
5859 if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
5860 // Free memory allocated for old cert and use new store `ca_cert_store`
5861 SSL_CTX_set_cert_store(ctx_, ca_cert_store);
5862 }
5863 } else {
5864 X509_STORE_free(ca_cert_store);
5865 }
5866 }
4588} 5867}
4589 5868
4590inline long SSLClient::get_openssl_verify_result() const { 5869inline long SSLClient::get_openssl_verify_result() const {
4591 return verify_result_; 5870 return verify_result_;
4592} 5871}
4593 5872
4594inline SSL_CTX *SSLClient::ssl_context() const noexcept { return ctx_; } 5873inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
5874
5875inline bool SSLClient::create_and_connect_socket(Socket &socket) {
5876 return is_valid() && ClientImpl::create_and_connect_socket(socket);
5877}
5878
5879inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
5880 bool &success) {
5881 success = true;
5882 Response res2;
5883
5884 if (!detail::process_client_socket(
5885 socket.sock, read_timeout_sec_, read_timeout_usec_,
5886 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
5887 Request req2;
5888 req2.method = "CONNECT";
5889 req2.path = host_and_port_;
5890 return process_request(strm, req2, res2, false);
5891 })) {
5892 close_socket(socket, true);
5893 success = false;
5894 return false;
5895 }
5896
5897 if (res2.status == 407) {
5898 if (!proxy_digest_auth_username_.empty() &&
5899 !proxy_digest_auth_password_.empty()) {
5900 std::map<std::string, std::string> auth;
5901 if (detail::parse_www_authenticate(res2, auth, true)) {
5902 Response res3;
5903 if (!detail::process_client_socket(
5904 socket.sock, read_timeout_sec_, read_timeout_usec_,
5905 write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
5906 Request req3;
5907 req3.method = "CONNECT";
5908 req3.path = host_and_port_;
5909 req3.headers.insert(detail::make_digest_authentication_header(
5910 req3, auth, 1, detail::random_string(10),
5911 proxy_digest_auth_username_, proxy_digest_auth_password_,
5912 true));
5913 return process_request(strm, req3, res3, false);
5914 })) {
5915 close_socket(socket, true);
5916 success = false;
5917 return false;
5918 }
5919 }
5920 } else {
5921 res = res2;
5922 return false;
5923 }
5924 }
5925
5926 return true;
5927}
5928
5929inline bool SSLClient::load_certs() {
5930 bool ret = true;
5931
5932 std::call_once(initialize_cert_, [&]() {
5933 std::lock_guard<std::mutex> guard(ctx_mutex_);
5934 if (!ca_cert_file_path_.empty()) {
5935 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
5936 nullptr)) {
5937 ret = false;
5938 }
5939 } else if (!ca_cert_dir_path_.empty()) {
5940 if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
5941 ca_cert_dir_path_.c_str())) {
5942 ret = false;
5943 }
5944 } else {
5945#ifdef _WIN32
5946 detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
5947#else
5948 SSL_CTX_set_default_verify_paths(ctx_);
5949#endif
5950 }
5951 });
5952
5953 return ret;
5954}
5955
5956inline bool SSLClient::initialize_ssl(Socket &socket) {
5957 auto ssl = detail::ssl_new(
5958 socket.sock, ctx_, ctx_mutex_,
5959 [&](SSL *ssl) {
5960 if (server_certificate_verification_) {
5961 if (!load_certs()) {
5962 error_ = Error::SSLLoadingCerts;
5963 return false;
5964 }
5965 SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
5966 }
4595 5967
4596inline bool SSLClient::process_and_close_socket( 5968 if (SSL_connect(ssl) != 1) {
4597 socket_t sock, size_t request_count, 5969 error_ = Error::SSLConnection;
4598 std::function<bool(Stream &strm, bool last_connection, 5970 return false;
4599 bool &connection_close)> 5971 }
4600 callback) {
4601 5972
4602 request_count = std::min(request_count, keep_alive_max_count_); 5973 if (server_certificate_verification_) {
5974 verify_result_ = SSL_get_verify_result(ssl);
4603 5975
4604 return is_valid() && 5976 if (verify_result_ != X509_V_OK) {
4605 detail::process_and_close_socket_ssl( 5977 error_ = Error::SSLServerVerification;
4606 true, sock, request_count, read_timeout_sec_, read_timeout_usec_, 5978 return false;
4607 ctx_, ctx_mutex_, 5979 }
4608 [&](SSL *ssl) { 5980
4609 if (ca_cert_file_path_.empty()) { 5981 auto server_cert = SSL_get_peer_certificate(ssl);
4610 SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
4611 } else {
4612 if (!SSL_CTX_load_verify_locations(
4613 ctx_, ca_cert_file_path_.c_str(), nullptr)) {
4614 return false;
4615 }
4616 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
4617 }
4618 5982
4619 if (SSL_connect(ssl) != 1) { return false; } 5983 if (server_cert == nullptr) {
5984 error_ = Error::SSLServerVerification;
5985 return false;
5986 }
4620 5987
4621 if (server_certificate_verification_) { 5988 if (!verify_host(server_cert)) {
4622 verify_result_ = SSL_get_verify_result(ssl); 5989 X509_free(server_cert);
5990 error_ = Error::SSLServerVerification;
5991 return false;
5992 }
5993 X509_free(server_cert);
5994 }
4623 5995
4624 if (verify_result_ != X509_V_OK) { return false; } 5996 return true;
5997 },
5998 [&](SSL *ssl) {
5999 SSL_set_tlsext_host_name(ssl, host_.c_str());
6000 return true;
6001 });
4625 6002
4626 auto server_cert = SSL_get_peer_certificate(ssl); 6003 if (ssl) {
6004 socket.ssl = ssl;
6005 return true;
6006 }
4627 6007
4628 if (server_cert == nullptr) { return false; } 6008 close_socket(socket, false);
6009 return false;
6010}
4629 6011
4630 if (!verify_host(server_cert)) { 6012inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) {
4631 X509_free(server_cert); 6013 detail::close_socket(socket.sock);
4632 return false; 6014 socket_.sock = INVALID_SOCKET;
4633 } 6015 if (socket.ssl) {
4634 X509_free(server_cert); 6016 detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret);
4635 } 6017 socket_.ssl = nullptr;
6018 }
6019}
4636 6020
4637 return true; 6021inline bool
4638 }, 6022SSLClient::process_socket(Socket &socket,
4639 [&](SSL *ssl) { 6023 std::function<bool(Stream &strm)> callback) {
4640 SSL_set_tlsext_host_name(ssl, host_.c_str()); 6024 assert(socket.ssl);
4641 return true; 6025 return detail::process_client_socket_ssl(
4642 }, 6026 socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
4643 [&](SSL * /*ssl*/, Stream &strm, bool last_connection, 6027 write_timeout_sec_, write_timeout_usec_, callback);
4644 bool &connection_close) {
4645 return callback(strm, last_connection, connection_close);
4646 });
4647} 6028}
4648 6029
4649inline bool SSLClient::is_ssl() const { return true; } 6030inline bool SSLClient::is_ssl() const { return true; }
@@ -4703,7 +6084,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
4703 6084
4704 auto count = sk_GENERAL_NAME_num(alt_names); 6085 auto count = sk_GENERAL_NAME_num(alt_names);
4705 6086
4706 for (auto i = 0; i < count && !dsn_matched; i++) { 6087 for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
4707 auto val = sk_GENERAL_NAME_value(alt_names, i); 6088 auto val = sk_GENERAL_NAME_value(alt_names, i);
4708 if (val->type == type) { 6089 if (val->type == type) {
4709 auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); 6090 auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
@@ -4728,7 +6109,6 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
4728 } 6109 }
4729 6110
4730 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); 6111 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
4731
4732 return ret; 6112 return ret;
4733} 6113}
4734 6114
@@ -4740,7 +6120,9 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
4740 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, 6120 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
4741 name, sizeof(name)); 6121 name, sizeof(name));
4742 6122
4743 if (name_len != -1) { return check_host_name(name, name_len); } 6123 if (name_len != -1) {
6124 return check_host_name(name, static_cast<size_t>(name_len));
6125 }
4744 } 6126 }
4745 6127
4746 return false; 6128 return false;
@@ -4775,6 +6157,327 @@ inline bool SSLClient::check_host_name(const char *pattern,
4775} 6157}
4776#endif 6158#endif
4777 6159
6160// Universal client implementation
6161inline Client::Client(const char *scheme_host_port)
6162 : Client(scheme_host_port, std::string(), std::string()) {}
6163
6164inline Client::Client(const char *scheme_host_port,
6165 const std::string &client_cert_path,
6166 const std::string &client_key_path) {
6167 const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)");
6168
6169 std::cmatch m;
6170 if (std::regex_match(scheme_host_port, m, re)) {
6171 auto scheme = m[1].str();
6172
6173#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6174 if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
6175#else
6176 if (!scheme.empty() && scheme != "http") {
6177#endif
6178 return;
6179 }
6180
6181 auto is_ssl = scheme == "https";
6182
6183 auto host = m[2].str();
6184
6185 auto port_str = m[3].str();
6186 auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
6187
6188 if (is_ssl) {
6189#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6190 cli_ = std::make_shared<SSLClient>(host.c_str(), port, client_cert_path,
6191 client_key_path);
6192 is_ssl_ = is_ssl;
6193#endif
6194 } else {
6195 cli_ = std::make_shared<ClientImpl>(host.c_str(), port, client_cert_path,
6196 client_key_path);
6197 }
6198 } else {
6199 cli_ = std::make_shared<ClientImpl>(scheme_host_port, 80, client_cert_path,
6200 client_key_path);
6201 }
6202}
6203
6204inline Client::Client(const std::string &host, int port)
6205 : cli_(std::make_shared<ClientImpl>(host, port)) {}
6206
6207inline Client::Client(const std::string &host, int port,
6208 const std::string &client_cert_path,
6209 const std::string &client_key_path)
6210 : cli_(std::make_shared<ClientImpl>(host, port, client_cert_path,
6211 client_key_path)) {}
6212
6213inline Client::~Client() {}
6214
6215inline bool Client::is_valid() const {
6216 return cli_ != nullptr && cli_->is_valid();
6217}
6218
6219inline Result Client::Get(const char *path) { return cli_->Get(path); }
6220inline Result Client::Get(const char *path, const Headers &headers) {
6221 return cli_->Get(path, headers);
6222}
6223inline Result Client::Get(const char *path, Progress progress) {
6224 return cli_->Get(path, progress);
6225}
6226inline Result Client::Get(const char *path, const Headers &headers,
6227 Progress progress) {
6228 return cli_->Get(path, headers, progress);
6229}
6230inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
6231 return cli_->Get(path, std::move(content_receiver));
6232}
6233inline Result Client::Get(const char *path, const Headers &headers,
6234 ContentReceiver content_receiver) {
6235 return cli_->Get(path, headers, std::move(content_receiver));
6236}
6237inline Result Client::Get(const char *path, ContentReceiver content_receiver,
6238 Progress progress) {
6239 return cli_->Get(path, std::move(content_receiver), std::move(progress));
6240}
6241inline Result Client::Get(const char *path, const Headers &headers,
6242 ContentReceiver content_receiver, Progress progress) {
6243 return cli_->Get(path, headers, std::move(content_receiver),
6244 std::move(progress));
6245}
6246inline Result Client::Get(const char *path, ResponseHandler response_handler,
6247 ContentReceiver content_receiver) {
6248 return cli_->Get(path, std::move(response_handler),
6249 std::move(content_receiver));
6250}
6251inline Result Client::Get(const char *path, const Headers &headers,
6252 ResponseHandler response_handler,
6253 ContentReceiver content_receiver) {
6254 return cli_->Get(path, headers, std::move(response_handler),
6255 std::move(content_receiver));
6256}
6257inline Result Client::Get(const char *path, ResponseHandler response_handler,
6258 ContentReceiver content_receiver, Progress progress) {
6259 return cli_->Get(path, std::move(response_handler),
6260 std::move(content_receiver), std::move(progress));
6261}
6262inline Result Client::Get(const char *path, const Headers &headers,
6263 ResponseHandler response_handler,
6264 ContentReceiver content_receiver, Progress progress) {
6265 return cli_->Get(path, headers, response_handler, content_receiver, progress);
6266}
6267
6268inline Result Client::Head(const char *path) { return cli_->Head(path); }
6269inline Result Client::Head(const char *path, const Headers &headers) {
6270 return cli_->Head(path, headers);
6271}
6272
6273inline Result Client::Post(const char *path) { return cli_->Post(path); }
6274inline Result Client::Post(const char *path, const std::string &body,
6275 const char *content_type) {
6276 return cli_->Post(path, body, content_type);
6277}
6278inline Result Client::Post(const char *path, const Headers &headers,
6279 const std::string &body, const char *content_type) {
6280 return cli_->Post(path, headers, body, content_type);
6281}
6282inline Result Client::Post(const char *path, size_t content_length,
6283 ContentProvider content_provider,
6284 const char *content_type) {
6285 return cli_->Post(path, content_length, content_provider, content_type);
6286}
6287inline Result Client::Post(const char *path, const Headers &headers,
6288 size_t content_length,
6289 ContentProvider content_provider,
6290 const char *content_type) {
6291 return cli_->Post(path, headers, content_length, content_provider,
6292 content_type);
6293}
6294inline Result Client::Post(const char *path, const Params &params) {
6295 return cli_->Post(path, params);
6296}
6297inline Result Client::Post(const char *path, const Headers &headers,
6298 const Params &params) {
6299 return cli_->Post(path, headers, params);
6300}
6301inline Result Client::Post(const char *path,
6302 const MultipartFormDataItems &items) {
6303 return cli_->Post(path, items);
6304}
6305inline Result Client::Post(const char *path, const Headers &headers,
6306 const MultipartFormDataItems &items) {
6307 return cli_->Post(path, headers, items);
6308}
6309inline Result Client::Put(const char *path) { return cli_->Put(path); }
6310inline Result Client::Put(const char *path, const std::string &body,
6311 const char *content_type) {
6312 return cli_->Put(path, body, content_type);
6313}
6314inline Result Client::Put(const char *path, const Headers &headers,
6315 const std::string &body, const char *content_type) {
6316 return cli_->Put(path, headers, body, content_type);
6317}
6318inline Result Client::Put(const char *path, size_t content_length,
6319 ContentProvider content_provider,
6320 const char *content_type) {
6321 return cli_->Put(path, content_length, content_provider, content_type);
6322}
6323inline Result Client::Put(const char *path, const Headers &headers,
6324 size_t content_length,
6325 ContentProvider content_provider,
6326 const char *content_type) {
6327 return cli_->Put(path, headers, content_length, content_provider,
6328 content_type);
6329}
6330inline Result Client::Put(const char *path, const Params &params) {
6331 return cli_->Put(path, params);
6332}
6333inline Result Client::Put(const char *path, const Headers &headers,
6334 const Params &params) {
6335 return cli_->Put(path, headers, params);
6336}
6337inline Result Client::Patch(const char *path, const std::string &body,
6338 const char *content_type) {
6339 return cli_->Patch(path, body, content_type);
6340}
6341inline Result Client::Patch(const char *path, const Headers &headers,
6342 const std::string &body, const char *content_type) {
6343 return cli_->Patch(path, headers, body, content_type);
6344}
6345inline Result Client::Patch(const char *path, size_t content_length,
6346 ContentProvider content_provider,
6347 const char *content_type) {
6348 return cli_->Patch(path, content_length, content_provider, content_type);
6349}
6350inline Result Client::Patch(const char *path, const Headers &headers,
6351 size_t content_length,
6352 ContentProvider content_provider,
6353 const char *content_type) {
6354 return cli_->Patch(path, headers, content_length, content_provider,
6355 content_type);
6356}
6357inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
6358inline Result Client::Delete(const char *path, const std::string &body,
6359 const char *content_type) {
6360 return cli_->Delete(path, body, content_type);
6361}
6362inline Result Client::Delete(const char *path, const Headers &headers) {
6363 return cli_->Delete(path, headers);
6364}
6365inline Result Client::Delete(const char *path, const Headers &headers,
6366 const std::string &body,
6367 const char *content_type) {
6368 return cli_->Delete(path, headers, body, content_type);
6369}
6370inline Result Client::Options(const char *path) { return cli_->Options(path); }
6371inline Result Client::Options(const char *path, const Headers &headers) {
6372 return cli_->Options(path, headers);
6373}
6374
6375inline bool Client::send(const Request &req, Response &res) {
6376 return cli_->send(req, res);
6377}
6378
6379inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
6380
6381inline void Client::stop() { cli_->stop(); }
6382
6383inline void Client::set_default_headers(Headers headers) {
6384 cli_->set_default_headers(std::move(headers));
6385}
6386
6387inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
6388inline void Client::set_socket_options(SocketOptions socket_options) {
6389 cli_->set_socket_options(socket_options);
6390}
6391
6392inline void Client::set_connection_timeout(time_t sec, time_t usec) {
6393 cli_->set_connection_timeout(sec, usec);
6394}
6395inline void Client::set_read_timeout(time_t sec, time_t usec) {
6396 cli_->set_read_timeout(sec, usec);
6397}
6398inline void Client::set_write_timeout(time_t sec, time_t usec) {
6399 cli_->set_write_timeout(sec, usec);
6400}
6401
6402inline void Client::set_basic_auth(const char *username, const char *password) {
6403 cli_->set_basic_auth(username, password);
6404}
6405inline void Client::set_bearer_token_auth(const char *token) {
6406 cli_->set_bearer_token_auth(token);
6407}
6408#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6409inline void Client::set_digest_auth(const char *username,
6410 const char *password) {
6411 cli_->set_digest_auth(username, password);
6412}
6413#endif
6414
6415inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
6416inline void Client::set_follow_location(bool on) {
6417 cli_->set_follow_location(on);
6418}
6419
6420inline void Client::set_compress(bool on) { cli_->set_compress(on); }
6421
6422inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
6423
6424inline void Client::set_interface(const char *intf) {
6425 cli_->set_interface(intf);
6426}
6427
6428inline void Client::set_proxy(const char *host, int port) {
6429 cli_->set_proxy(host, port);
6430}
6431inline void Client::set_proxy_basic_auth(const char *username,
6432 const char *password) {
6433 cli_->set_proxy_basic_auth(username, password);
6434}
6435inline void Client::set_proxy_bearer_token_auth(const char *token) {
6436 cli_->set_proxy_bearer_token_auth(token);
6437}
6438#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6439inline void Client::set_proxy_digest_auth(const char *username,
6440 const char *password) {
6441 cli_->set_proxy_digest_auth(username, password);
6442}
6443#endif
6444
6445#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6446inline void Client::enable_server_certificate_verification(bool enabled) {
6447 cli_->enable_server_certificate_verification(enabled);
6448}
6449#endif
6450
6451inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
6452
6453#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6454inline void Client::set_ca_cert_path(const char *ca_cert_file_path,
6455 const char *ca_cert_dir_path) {
6456 if (is_ssl_) {
6457 static_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path,
6458 ca_cert_dir_path);
6459 }
6460}
6461
6462inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
6463 if (is_ssl_) {
6464 static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
6465 }
6466}
6467
6468inline long Client::get_openssl_verify_result() const {
6469 if (is_ssl_) {
6470 return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
6471 }
6472 return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
6473}
6474
6475inline SSL_CTX *Client::ssl_context() const {
6476 if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
6477 return nullptr;
6478}
6479#endif
6480
4778// ---------------------------------------------------------------------------- 6481// ----------------------------------------------------------------------------
4779 6482
4780} // namespace httplib 6483} // namespace httplib