summaryrefslogtreecommitdiff
path: root/externals/httplib/httplib.h
diff options
context:
space:
mode:
authorGravatar Brian Clinkenbeard2020-02-17 20:13:24 -0800
committerGravatar Brian Clinkenbeard2020-02-17 20:13:24 -0800
commit293d4d553a8736a76a73c14d6c254b6d13898730 (patch)
tree39f72143be17e471c20db8f12641bb478dc220e1 /externals/httplib/httplib.h
parentMerge pull request #3412 from Morph1984/aspect-ratio (diff)
downloadyuzu-293d4d553a8736a76a73c14d6c254b6d13898730.tar.gz
yuzu-293d4d553a8736a76a73c14d6c254b6d13898730.tar.xz
yuzu-293d4d553a8736a76a73c14d6c254b6d13898730.zip
update httplib to 0.2.6
Diffstat (limited to '')
-rw-r--r--externals/httplib/httplib.h981
1 files changed, 645 insertions, 336 deletions
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index fa2edcc94..002b874b9 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -11,6 +11,7 @@
11/* 11/*
12 * Configuration 12 * Configuration
13 */ 13 */
14
14#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 15#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
15#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 16#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
16#endif 17#endif
@@ -51,6 +52,10 @@
51#define CPPHTTPLIB_THREAD_POOL_COUNT 8 52#define CPPHTTPLIB_THREAD_POOL_COUNT 8
52#endif 53#endif
53 54
55/*
56 * Headers
57 */
58
54#ifdef _WIN32 59#ifdef _WIN32
55#ifndef _CRT_SECURE_NO_WARNINGS 60#ifndef _CRT_SECURE_NO_WARNINGS
56#define _CRT_SECURE_NO_WARNINGS 61#define _CRT_SECURE_NO_WARNINGS
@@ -62,9 +67,9 @@
62 67
63#if defined(_MSC_VER) 68#if defined(_MSC_VER)
64#ifdef _WIN64 69#ifdef _WIN64
65typedef __int64 ssize_t; 70using ssize_t = __int64;
66#else 71#else
67typedef int ssize_t; 72using ssize_t = int;
68#endif 73#endif
69 74
70#if _MSC_VER < 1900 75#if _MSC_VER < 1900
@@ -100,7 +105,7 @@ typedef int ssize_t;
100#define strcasecmp _stricmp 105#define strcasecmp _stricmp
101#endif // strcasecmp 106#endif // strcasecmp
102 107
103typedef SOCKET socket_t; 108using socket_t = SOCKET;
104#ifdef CPPHTTPLIB_USE_POLL 109#ifdef CPPHTTPLIB_USE_POLL
105#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) 110#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
106#endif 111#endif
@@ -115,16 +120,16 @@ typedef SOCKET socket_t;
115#include <poll.h> 120#include <poll.h>
116#endif 121#endif
117#include <pthread.h> 122#include <pthread.h>
118#include <signal.h> 123#include <csignal>
119#include <sys/select.h> 124#include <sys/select.h>
120#include <sys/socket.h> 125#include <sys/socket.h>
121#include <unistd.h> 126#include <unistd.h>
122 127
123typedef int socket_t; 128using socket_t = int;
124#define INVALID_SOCKET (-1) 129#define INVALID_SOCKET (-1)
125#endif //_WIN32 130#endif //_WIN32
126 131
127#include <assert.h> 132#include <cassert>
128#include <atomic> 133#include <atomic>
129#include <condition_variable> 134#include <condition_variable>
130#include <errno.h> 135#include <errno.h>
@@ -140,6 +145,7 @@ typedef int socket_t;
140#include <string> 145#include <string>
141#include <sys/stat.h> 146#include <sys/stat.h>
142#include <thread> 147#include <thread>
148#include <array>
143 149
144#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 150#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
145#include <openssl/err.h> 151#include <openssl/err.h>
@@ -162,6 +168,9 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
162#include <zlib.h> 168#include <zlib.h>
163#endif 169#endif
164 170
171/*
172 * Declaration
173 */
165namespace httplib { 174namespace httplib {
166 175
167namespace detail { 176namespace detail {
@@ -178,27 +187,27 @@ struct ci {
178 187
179enum class HttpVersion { v1_0 = 0, v1_1 }; 188enum class HttpVersion { v1_0 = 0, v1_1 };
180 189
181typedef std::multimap<std::string, std::string, detail::ci> Headers; 190using Headers = std::multimap<std::string, std::string, detail::ci>;
191
192using Params = std::multimap<std::string, std::string>;
193using Match = std::smatch;
194
195using DataSink = std::function<void(const char *data, size_t data_len)>;
182 196
183typedef std::multimap<std::string, std::string> Params; 197using Done = std::function<void()>;
184typedef std::smatch Match;
185 198
186typedef std::function<void(const char *data, size_t data_len)> DataSink; 199using ContentProvider = std::function<void(size_t offset, size_t length, DataSink sink)>;
187 200
188typedef std::function<void()> Done; 201using ContentProviderWithCloser = std::function<void(size_t offset, size_t length, DataSink sink, Done done)>;
189 202
190typedef std::function<void(size_t offset, size_t length, DataSink sink, 203using ContentReceiver = std::function<bool(const char *data, size_t data_length)>;
191 Done done)>
192 ContentProvider;
193 204
194typedef std::function<bool(const char *data, size_t data_length, size_t offset, 205using ContentReader = std::function<bool(ContentReceiver receiver)>;
195 uint64_t content_length)>
196 ContentReceiver;
197 206
198typedef std::function<bool(uint64_t current, uint64_t total)> Progress; 207using Progress = std::function<bool(uint64_t current, uint64_t total)>;
199 208
200struct Response; 209struct Response;
201typedef std::function<bool(const Response &response)> ResponseHandler; 210using ResponseHandler = std::function<bool(const Response &response)>;
202 211
203struct MultipartFile { 212struct MultipartFile {
204 std::string filename; 213 std::string filename;
@@ -206,7 +215,7 @@ struct MultipartFile {
206 size_t offset = 0; 215 size_t offset = 0;
207 size_t length = 0; 216 size_t length = 0;
208}; 217};
209typedef std::multimap<std::string, MultipartFile> MultipartFiles; 218using MultipartFiles = std::multimap<std::string, MultipartFile>;
210 219
211struct MultipartFormData { 220struct MultipartFormData {
212 std::string name; 221 std::string name;
@@ -214,10 +223,10 @@ struct MultipartFormData {
214 std::string filename; 223 std::string filename;
215 std::string content_type; 224 std::string content_type;
216}; 225};
217typedef std::vector<MultipartFormData> MultipartFormDataItems; 226using MultipartFormDataItems = std::vector<MultipartFormData>;
218 227
219typedef std::pair<ssize_t, ssize_t> Range; 228using Range = std::pair<ssize_t, ssize_t>;
220typedef std::vector<Range> Ranges; 229using Ranges = std::vector<Range>;
221 230
222struct Request { 231struct Request {
223 std::string method; 232 std::string method;
@@ -255,6 +264,10 @@ struct Request {
255 264
256 bool has_file(const char *key) const; 265 bool has_file(const char *key) const;
257 MultipartFile get_file_value(const char *key) const; 266 MultipartFile get_file_value(const char *key) const;
267
268 // private members...
269 size_t content_length;
270 ContentProvider content_provider;
258}; 271};
259 272
260struct Response { 273struct Response {
@@ -269,7 +282,7 @@ struct Response {
269 void set_header(const char *key, const char *val); 282 void set_header(const char *key, const char *val);
270 void set_header(const char *key, const std::string &val); 283 void set_header(const char *key, const std::string &val);
271 284
272 void set_redirect(const char *uri); 285 void set_redirect(const char *url);
273 void set_content(const char *s, size_t n, const char *content_type); 286 void set_content(const char *s, size_t n, const char *content_type);
274 void set_content(const std::string &s, const char *content_type); 287 void set_content(const std::string &s, const char *content_type);
275 288
@@ -282,7 +295,7 @@ struct Response {
282 std::function<void(size_t offset, DataSink sink, Done done)> provider, 295 std::function<void(size_t offset, DataSink sink, Done done)> provider,
283 std::function<void()> resource_releaser = [] {}); 296 std::function<void()> resource_releaser = [] {});
284 297
285 Response() : status(-1), content_provider_resource_length(0) {} 298 Response() : status(-1), content_length(0) {}
286 299
287 ~Response() { 300 ~Response() {
288 if (content_provider_resource_releaser) { 301 if (content_provider_resource_releaser) {
@@ -290,14 +303,15 @@ struct Response {
290 } 303 }
291 } 304 }
292 305
293 size_t content_provider_resource_length; 306 // private members...
294 ContentProvider content_provider; 307 size_t content_length;
308 ContentProviderWithCloser content_provider;
295 std::function<void()> content_provider_resource_releaser; 309 std::function<void()> content_provider_resource_releaser;
296}; 310};
297 311
298class Stream { 312class Stream {
299public: 313public:
300 virtual ~Stream() {} 314 virtual ~Stream() = default;
301 virtual int read(char *ptr, size_t size) = 0; 315 virtual int read(char *ptr, size_t size) = 0;
302 virtual int write(const char *ptr, size_t size1) = 0; 316 virtual int write(const char *ptr, size_t size1) = 0;
303 virtual int write(const char *ptr) = 0; 317 virtual int write(const char *ptr) = 0;
@@ -310,29 +324,32 @@ public:
310 324
311class SocketStream : public Stream { 325class SocketStream : public Stream {
312public: 326public:
313 SocketStream(socket_t sock); 327 SocketStream(socket_t sock, time_t read_timeout_sec,
314 virtual ~SocketStream(); 328 time_t read_timeout_usec);
329 ~SocketStream() override;
315 330
316 virtual int read(char *ptr, size_t size); 331 int read(char *ptr, size_t size) override;
317 virtual int write(const char *ptr, size_t size); 332 int write(const char *ptr, size_t size) override;
318 virtual int write(const char *ptr); 333 int write(const char *ptr) override;
319 virtual int write(const std::string &s); 334 int write(const std::string &s) override;
320 virtual std::string get_remote_addr() const; 335 std::string get_remote_addr() const override;
321 336
322private: 337private:
323 socket_t sock_; 338 socket_t sock_;
339 time_t read_timeout_sec_;
340 time_t read_timeout_usec_;
324}; 341};
325 342
326class BufferStream : public Stream { 343class BufferStream : public Stream {
327public: 344public:
328 BufferStream() {} 345 BufferStream() = default;
329 virtual ~BufferStream() {} 346 ~BufferStream() override = default;
330 347
331 virtual int read(char *ptr, size_t size); 348 int read(char *ptr, size_t size) override;
332 virtual int write(const char *ptr, size_t size); 349 int write(const char *ptr, size_t size) override;
333 virtual int write(const char *ptr); 350 int write(const char *ptr) override;
334 virtual int write(const std::string &s); 351 int write(const std::string &s) override;
335 virtual std::string get_remote_addr() const; 352 std::string get_remote_addr() const override;
336 353
337 const std::string &get_buffer() const; 354 const std::string &get_buffer() const;
338 355
@@ -342,8 +359,8 @@ private:
342 359
343class TaskQueue { 360class TaskQueue {
344public: 361public:
345 TaskQueue() {} 362 TaskQueue() = default;
346 virtual ~TaskQueue() {} 363 virtual ~TaskQueue() = default;
347 virtual void enqueue(std::function<void()> fn) = 0; 364 virtual void enqueue(std::function<void()> fn) = 0;
348 virtual void shutdown() = 0; 365 virtual void shutdown() = 0;
349}; 366};
@@ -351,24 +368,23 @@ public:
351#if CPPHTTPLIB_THREAD_POOL_COUNT > 0 368#if CPPHTTPLIB_THREAD_POOL_COUNT > 0
352class ThreadPool : public TaskQueue { 369class ThreadPool : public TaskQueue {
353public: 370public:
354 ThreadPool(size_t n) : shutdown_(false) { 371 explicit ThreadPool(size_t n) : shutdown_(false) {
355 while (n) { 372 while (n) {
356 auto t = std::make_shared<std::thread>(worker(*this)); 373 threads_.emplace_back(worker(*this));
357 threads_.push_back(t);
358 n--; 374 n--;
359 } 375 }
360 } 376 }
361 377
362 ThreadPool(const ThreadPool &) = delete; 378 ThreadPool(const ThreadPool &) = delete;
363 virtual ~ThreadPool() {} 379 ~ThreadPool() override = default;
364 380
365 virtual void enqueue(std::function<void()> fn) override { 381 void enqueue(std::function<void()> fn) override {
366 std::unique_lock<std::mutex> lock(mutex_); 382 std::unique_lock<std::mutex> lock(mutex_);
367 jobs_.push_back(fn); 383 jobs_.push_back(fn);
368 cond_.notify_one(); 384 cond_.notify_one();
369 } 385 }
370 386
371 virtual void shutdown() override { 387 void shutdown() override {
372 // Stop all worker threads... 388 // Stop all worker threads...
373 { 389 {
374 std::unique_lock<std::mutex> lock(mutex_); 390 std::unique_lock<std::mutex> lock(mutex_);
@@ -378,14 +394,14 @@ public:
378 cond_.notify_all(); 394 cond_.notify_all();
379 395
380 // Join... 396 // Join...
381 for (auto t : threads_) { 397 for (auto& t : threads_) {
382 t->join(); 398 t.join();
383 } 399 }
384 } 400 }
385 401
386private: 402private:
387 struct worker { 403 struct worker {
388 worker(ThreadPool &pool) : pool_(pool) {} 404 explicit worker(ThreadPool &pool) : pool_(pool) {}
389 405
390 void operator()() { 406 void operator()() {
391 for (;;) { 407 for (;;) {
@@ -411,7 +427,7 @@ private:
411 }; 427 };
412 friend struct worker; 428 friend struct worker;
413 429
414 std::vector<std::shared_ptr<std::thread>> threads_; 430 std::vector<std::thread> threads_;
415 std::list<std::function<void()>> jobs_; 431 std::list<std::function<void()>> jobs_;
416 432
417 bool shutdown_; 433 bool shutdown_;
@@ -419,7 +435,7 @@ private:
419 std::condition_variable cond_; 435 std::condition_variable cond_;
420 std::mutex mutex_; 436 std::mutex mutex_;
421}; 437};
422#else 438#elif CPPHTTPLIB_THREAD_POOL_COUNT == 0
423class Threads : public TaskQueue { 439class Threads : public TaskQueue {
424public: 440public:
425 Threads() : running_threads_(0) {} 441 Threads() : running_threads_(0) {}
@@ -453,12 +469,27 @@ private:
453 std::mutex running_threads_mutex_; 469 std::mutex running_threads_mutex_;
454 int running_threads_; 470 int running_threads_;
455}; 471};
472#else
473class NoThread : public TaskQueue {
474public:
475 NoThread() {}
476 virtual ~NoThread() {}
477
478 virtual void enqueue(std::function<void()> fn) override {
479 fn();
480 }
481
482 virtual void shutdown() override {
483 }
484};
456#endif 485#endif
457 486
458class Server { 487class Server {
459public: 488public:
460 typedef std::function<void(const Request &, Response &)> Handler; 489 using Handler = std::function<void(const Request &, Response &)>;
461 typedef std::function<void(const Request &, const Response &)> Logger; 490 using HandlerWithContentReader = std::function<void(const Request &, Response &,
491 const ContentReader &content_reader)>;
492 using Logger = std::function<void(const Request &, const Response &)>;
462 493
463 Server(); 494 Server();
464 495
@@ -468,9 +499,11 @@ public:
468 499
469 Server &Get(const char *pattern, Handler handler); 500 Server &Get(const char *pattern, Handler handler);
470 Server &Post(const char *pattern, Handler handler); 501 Server &Post(const char *pattern, Handler handler);
471 502 Server &Post(const char *pattern, HandlerWithContentReader handler);
472 Server &Put(const char *pattern, Handler handler); 503 Server &Put(const char *pattern, Handler handler);
504 Server &Put(const char *pattern, HandlerWithContentReader handler);
473 Server &Patch(const char *pattern, Handler handler); 505 Server &Patch(const char *pattern, Handler handler);
506 Server &Patch(const char *pattern, HandlerWithContentReader handler);
474 Server &Delete(const char *pattern, Handler handler); 507 Server &Delete(const char *pattern, Handler handler);
475 Server &Options(const char *pattern, Handler handler); 508 Server &Options(const char *pattern, Handler handler);
476 509
@@ -481,8 +514,10 @@ public:
481 void set_logger(Logger logger); 514 void set_logger(Logger logger);
482 515
483 void set_keep_alive_max_count(size_t count); 516 void set_keep_alive_max_count(size_t count);
517 void set_read_timeout(time_t sec, time_t usec);
484 void set_payload_max_length(size_t length); 518 void set_payload_max_length(size_t length);
485 519
520 bool bind_to_port(const char *host, int port, int socket_flags = 0);
486 int bind_to_any_port(const char *host, int socket_flags = 0); 521 int bind_to_any_port(const char *host, int socket_flags = 0);
487 bool listen_after_bind(); 522 bool listen_after_bind();
488 523
@@ -496,22 +531,28 @@ public:
496protected: 531protected:
497 bool process_request(Stream &strm, bool last_connection, 532 bool process_request(Stream &strm, bool last_connection,
498 bool &connection_close, 533 bool &connection_close,
499 std::function<void(Request &)> setup_request); 534 const std::function<void(Request &)>& setup_request);
500 535
501 size_t keep_alive_max_count_; 536 size_t keep_alive_max_count_;
537 time_t read_timeout_sec_;
538 time_t read_timeout_usec_;
502 size_t payload_max_length_; 539 size_t payload_max_length_;
503 540
504private: 541private:
505 typedef std::vector<std::pair<std::regex, Handler>> Handlers; 542 using Handlers = std::vector<std::pair<std::regex, Handler>>;
543 using HandersForContentReader = std::vector<std::pair<std::regex, HandlerWithContentReader>>;
506 544
507 socket_t create_server_socket(const char *host, int port, 545 socket_t create_server_socket(const char *host, int port,
508 int socket_flags) const; 546 int socket_flags) const;
509 int bind_internal(const char *host, int port, int socket_flags); 547 int bind_internal(const char *host, int port, int socket_flags);
510 bool listen_internal(); 548 bool listen_internal();
511 549
512 bool routing(Request &req, Response &res); 550 bool routing(Request &req, Response &res, Stream &strm, bool last_connection);
513 bool handle_file_request(Request &req, Response &res); 551 bool handle_file_request(Request &req, Response &res);
514 bool dispatch_request(Request &req, Response &res, Handlers &handlers); 552 bool dispatch_request(Request &req, Response &res, Handlers &handlers);
553 bool dispatch_request_for_content_reader(Request &req, Response &res,
554 ContentReader content_reader,
555 HandersForContentReader &handlers);
515 556
516 bool parse_request_line(const char *s, Request &req); 557 bool parse_request_line(const char *s, Request &req);
517 bool write_response(Stream &strm, bool last_connection, const Request &req, 558 bool write_response(Stream &strm, bool last_connection, const Request &req,
@@ -519,6 +560,11 @@ private:
519 bool write_content_with_provider(Stream &strm, const Request &req, 560 bool write_content_with_provider(Stream &strm, const Request &req,
520 Response &res, const std::string &boundary, 561 Response &res, const std::string &boundary,
521 const std::string &content_type); 562 const std::string &content_type);
563 bool read_content(Stream &strm, bool last_connection, Request &req,
564 Response &res);
565 bool read_content_with_content_receiver(Stream &strm, bool last_connection,
566 Request &req, Response &res,
567 ContentReceiver reveiver);
522 568
523 virtual bool process_and_close_socket(socket_t sock); 569 virtual bool process_and_close_socket(socket_t sock);
524 570
@@ -528,8 +574,11 @@ private:
528 Handler file_request_handler_; 574 Handler file_request_handler_;
529 Handlers get_handlers_; 575 Handlers get_handlers_;
530 Handlers post_handlers_; 576 Handlers post_handlers_;
577 HandersForContentReader post_handlers_for_content_reader;
531 Handlers put_handlers_; 578 Handlers put_handlers_;
579 HandersForContentReader put_handlers_for_content_reader;
532 Handlers patch_handlers_; 580 Handlers patch_handlers_;
581 HandersForContentReader patch_handlers_for_content_reader;
533 Handlers delete_handlers_; 582 Handlers delete_handlers_;
534 Handlers options_handlers_; 583 Handlers options_handlers_;
535 Handler error_handler_; 584 Handler error_handler_;
@@ -538,7 +587,7 @@ private:
538 587
539class Client { 588class Client {
540public: 589public:
541 Client(const char *host, int port = 80, time_t timeout_sec = 300); 590 explicit Client(const char *host, int port = 80, time_t timeout_sec = 300);
542 591
543 virtual ~Client(); 592 virtual ~Client();
544 593
@@ -580,36 +629,78 @@ public:
580 std::shared_ptr<Response> Head(const char *path, const Headers &headers); 629 std::shared_ptr<Response> Head(const char *path, const Headers &headers);
581 630
582 std::shared_ptr<Response> Post(const char *path, const std::string &body, 631 std::shared_ptr<Response> Post(const char *path, const std::string &body,
583 const char *content_type); 632 const char *content_type,
633 bool compress = false);
584 634
585 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 635 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
586 const std::string &body, 636 const std::string &body,
587 const char *content_type); 637 const char *content_type,
638 bool compress = false);
588 639
589 std::shared_ptr<Response> Post(const char *path, const Params &params); 640 std::shared_ptr<Response> Post(const char *path, size_t content_length,
641 ContentProvider content_provider,
642 const char *content_type,
643 bool compress = false);
590 644
591 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 645 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
592 const Params &params); 646 size_t content_length,
647 ContentProvider content_provider,
648 const char *content_type,
649 bool compress = false);
650
651 std::shared_ptr<Response> Post(const char *path, const Params &params,
652 bool compress = false);
653
654 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
655 const Params &params, bool compress = false);
593 656
594 std::shared_ptr<Response> Post(const char *path, 657 std::shared_ptr<Response> Post(const char *path,
595 const MultipartFormDataItems &items); 658 const MultipartFormDataItems &items,
659 bool compress = false);
596 660
597 std::shared_ptr<Response> Post(const char *path, const Headers &headers, 661 std::shared_ptr<Response> Post(const char *path, const Headers &headers,
598 const MultipartFormDataItems &items); 662 const MultipartFormDataItems &items,
663 bool compress = false);
599 664
600 std::shared_ptr<Response> Put(const char *path, const std::string &body, 665 std::shared_ptr<Response> Put(const char *path, const std::string &body,
601 const char *content_type); 666 const char *content_type,
667 bool compress = false);
602 668
603 std::shared_ptr<Response> Put(const char *path, const Headers &headers, 669 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
604 const std::string &body, 670 const std::string &body,
605 const char *content_type); 671 const char *content_type,
672 bool compress = false);
673
674 std::shared_ptr<Response> Put(const char *path, size_t content_length,
675 ContentProvider content_provider,
676 const char *content_type,
677 bool compress = false);
678
679 std::shared_ptr<Response> Put(const char *path, const Headers &headers,
680 size_t content_length,
681 ContentProvider content_provider,
682 const char *content_type,
683 bool compress = false);
606 684
607 std::shared_ptr<Response> Patch(const char *path, const std::string &body, 685 std::shared_ptr<Response> Patch(const char *path, const std::string &body,
608 const char *content_type); 686 const char *content_type,
687 bool compress = false);
609 688
610 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, 689 std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
611 const std::string &body, 690 const std::string &body,
612 const char *content_type); 691 const char *content_type,
692 bool compress = false);
693
694 std::shared_ptr<Response> Patch(const char *path, size_t content_length,
695 ContentProvider content_provider,
696 const char *content_type,
697 bool compress = false);
698
699 std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
700 size_t content_length,
701 ContentProvider content_provider,
702 const char *content_type,
703 bool compress = false);
613 704
614 std::shared_ptr<Response> Delete(const char *path); 705 std::shared_ptr<Response> Delete(const char *path);
615 706
@@ -632,6 +723,7 @@ public:
632 std::vector<Response> &responses); 723 std::vector<Response> &responses);
633 724
634 void set_keep_alive_max_count(size_t count); 725 void set_keep_alive_max_count(size_t count);
726 void set_read_timeout(time_t sec, time_t usec);
635 727
636 void follow_location(bool on); 728 void follow_location(bool on);
637 729
@@ -644,6 +736,8 @@ protected:
644 time_t timeout_sec_; 736 time_t timeout_sec_;
645 const std::string host_and_port_; 737 const std::string host_and_port_;
646 size_t keep_alive_max_count_; 738 size_t keep_alive_max_count_;
739 time_t read_timeout_sec_;
740 time_t read_timeout_usec_;
647 size_t follow_location_; 741 size_t follow_location_;
648 742
649private: 743private:
@@ -652,6 +746,13 @@ private:
652 void write_request(Stream &strm, const Request &req, bool last_connection); 746 void write_request(Stream &strm, const Request &req, bool last_connection);
653 bool redirect(const Request &req, Response &res); 747 bool redirect(const Request &req, Response &res);
654 748
749 std::shared_ptr<Response>
750 send_with_content_provider(const char *method, const char *path,
751 const Headers &headers, const std::string &body,
752 size_t content_length,
753 ContentProvider content_provider,
754 const char *content_type, bool compress);
755
655 virtual bool process_and_close_socket( 756 virtual bool process_and_close_socket(
656 socket_t sock, size_t request_count, 757 socket_t sock, size_t request_count,
657 std::function<bool(Stream &strm, bool last_connection, 758 std::function<bool(Stream &strm, bool last_connection,
@@ -694,7 +795,8 @@ inline void Post(std::vector<Request> &requests, const char *path,
694#ifdef CPPHTTPLIB_OPENSSL_SUPPORT 795#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
695class SSLSocketStream : public Stream { 796class SSLSocketStream : public Stream {
696public: 797public:
697 SSLSocketStream(socket_t sock, SSL *ssl); 798 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
799 time_t read_timeout_usec);
698 virtual ~SSLSocketStream(); 800 virtual ~SSLSocketStream();
699 801
700 virtual int read(char *ptr, size_t size); 802 virtual int read(char *ptr, size_t size);
@@ -706,6 +808,8 @@ public:
706private: 808private:
707 socket_t sock_; 809 socket_t sock_;
708 SSL *ssl_; 810 SSL *ssl_;
811 time_t read_timeout_sec_;
812 time_t read_timeout_usec_;
709}; 813};
710 814
711class SSLServer : public Server { 815class SSLServer : public Server {
@@ -741,7 +845,7 @@ public:
741 845
742 long get_openssl_verify_result() const; 846 long get_openssl_verify_result() const;
743 847
744 SSL_CTX* ssl_context() const noexcept; 848 SSL_CTX *ssl_context() const noexcept;
745 849
746private: 850private:
747 virtual bool process_and_close_socket( 851 virtual bool process_and_close_socket(
@@ -769,6 +873,7 @@ private:
769/* 873/*
770 * Implementation 874 * Implementation
771 */ 875 */
876
772namespace detail { 877namespace detail {
773 878
774inline bool is_hex(char c, int &v) { 879inline bool is_hex(char c, int &v) {
@@ -932,8 +1037,8 @@ inline void read_file(const std::string &path, std::string &out) {
932 1037
933inline std::string file_extension(const std::string &path) { 1038inline std::string file_extension(const std::string &path) {
934 std::smatch m; 1039 std::smatch m;
935 auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); 1040 auto re = std::regex("\\.([a-zA-Z0-9]+)$");
936 if (std::regex_search(path, m, pat)) { return m[1].str(); } 1041 if (std::regex_search(path, m, re)) { return m[1].str(); }
937 return std::string(); 1042 return std::string();
938} 1043}
939 1044
@@ -1019,7 +1124,7 @@ private:
1019 Stream &strm_; 1124 Stream &strm_;
1020 char *fixed_buffer_; 1125 char *fixed_buffer_;
1021 const size_t fixed_buffer_size_; 1126 const size_t fixed_buffer_size_;
1022 size_t fixed_buffer_used_size_; 1127 size_t fixed_buffer_used_size_ = 0;
1023 std::string glowable_buffer_; 1128 std::string glowable_buffer_;
1024}; 1129};
1025 1130
@@ -1065,7 +1170,8 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1065 pfd_read.revents & (POLLIN | POLLOUT)) { 1170 pfd_read.revents & (POLLIN | POLLOUT)) {
1066 int error = 0; 1171 int error = 0;
1067 socklen_t len = sizeof(error); 1172 socklen_t len = sizeof(error);
1068 return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) >= 0 && 1173 return getsockopt(sock, SOL_SOCKET, SO_ERROR,
1174 reinterpret_cast<char *>(&error), &len) >= 0 &&
1069 !error; 1175 !error;
1070 } 1176 }
1071 return false; 1177 return false;
@@ -1085,7 +1191,7 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1085 (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { 1191 (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
1086 int error = 0; 1192 int error = 0;
1087 socklen_t len = sizeof(error); 1193 socklen_t len = sizeof(error);
1088 return getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) >= 0 && 1194 return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) >= 0 &&
1089 !error; 1195 !error;
1090 } 1196 }
1091 return false; 1197 return false;
@@ -1094,7 +1200,9 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1094 1200
1095template <typename T> 1201template <typename T>
1096inline bool process_and_close_socket(bool is_client_request, socket_t sock, 1202inline bool process_and_close_socket(bool is_client_request, socket_t sock,
1097 size_t keep_alive_max_count, T callback) { 1203 size_t keep_alive_max_count,
1204 time_t read_timeout_sec,
1205 time_t read_timeout_usec, T callback) {
1098 assert(keep_alive_max_count > 0); 1206 assert(keep_alive_max_count > 0);
1099 1207
1100 bool ret = false; 1208 bool ret = false;
@@ -1105,7 +1213,7 @@ inline bool process_and_close_socket(bool is_client_request, socket_t sock,
1105 (is_client_request || 1213 (is_client_request ||
1106 detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, 1214 detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
1107 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { 1215 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
1108 SocketStream strm(sock); 1216 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1109 auto last_connection = count == 1; 1217 auto last_connection = count == 1;
1110 auto connection_close = false; 1218 auto connection_close = false;
1111 1219
@@ -1115,7 +1223,7 @@ inline bool process_and_close_socket(bool is_client_request, socket_t sock,
1115 count--; 1223 count--;
1116 } 1224 }
1117 } else { 1225 } else {
1118 SocketStream strm(sock); 1226 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1119 auto dummy_connection_close = false; 1227 auto dummy_connection_close = false;
1120 ret = callback(strm, true, dummy_connection_close); 1228 ret = callback(strm, true, dummy_connection_close);
1121 } 1229 }
@@ -1176,9 +1284,11 @@ socket_t create_socket(const char *host, int port, Fn fn,
1176 1284
1177 // Make 'reuse address' option available 1285 // Make 'reuse address' option available
1178 int yes = 1; 1286 int yes = 1;
1179 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&yes), sizeof(yes)); 1287 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
1288 sizeof(yes));
1180#ifdef SO_REUSEPORT 1289#ifdef SO_REUSEPORT
1181 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char*>(&yes), sizeof(yes)); 1290 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes),
1291 sizeof(yes));
1182#endif 1292#endif
1183 1293
1184 // bind or connect 1294 // bind or connect
@@ -1218,11 +1328,11 @@ inline std::string get_remote_addr(socket_t sock) {
1218 socklen_t len = sizeof(addr); 1328 socklen_t len = sizeof(addr);
1219 1329
1220 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) { 1330 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) {
1221 char ipstr[NI_MAXHOST]; 1331 std::array<char, NI_MAXHOST> ipstr{};
1222 1332
1223 if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, ipstr, sizeof(ipstr), 1333 if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, ipstr.data(), ipstr.size(),
1224 nullptr, 0, NI_NUMERICHOST)) { 1334 nullptr, 0, NI_NUMERICHOST)) {
1225 return ipstr; 1335 return ipstr.data();
1226 } 1336 }
1227 } 1337 }
1228 1338
@@ -1233,7 +1343,7 @@ inline const char *find_content_type(const std::string &path) {
1233 auto ext = file_extension(path); 1343 auto ext = file_extension(path);
1234 if (ext == "txt") { 1344 if (ext == "txt") {
1235 return "text/plain"; 1345 return "text/plain";
1236 } else if (ext == "html") { 1346 } else if (ext == "html" || ext == "htm") {
1237 return "text/html"; 1347 return "text/html";
1238 } else if (ext == "css") { 1348 } else if (ext == "css") {
1239 return "text/css"; 1349 return "text/css";
@@ -1302,18 +1412,18 @@ inline bool compress(std::string &content) {
1302 if (ret != Z_OK) { return false; } 1412 if (ret != Z_OK) { return false; }
1303 1413
1304 strm.avail_in = content.size(); 1414 strm.avail_in = content.size();
1305 strm.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(content.data())); 1415 strm.next_in =
1416 const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
1306 1417
1307 std::string compressed; 1418 std::string compressed;
1308 1419
1309 const auto bufsiz = 16384; 1420 std::array<char, 16384> buff{};
1310 char buff[bufsiz];
1311 do { 1421 do {
1312 strm.avail_out = bufsiz; 1422 strm.avail_out = buff.size();
1313 strm.next_out = reinterpret_cast<Bytef*>(buff); 1423 strm.next_out = reinterpret_cast<Bytef*>(buff.data());
1314 ret = deflate(&strm, Z_FINISH); 1424 ret = deflate(&strm, Z_FINISH);
1315 assert(ret != Z_STREAM_ERROR); 1425 assert(ret != Z_STREAM_ERROR);
1316 compressed.append(buff, bufsiz - strm.avail_out); 1426 compressed.append(buff.data(), buff.size() - strm.avail_out);
1317 } while (strm.avail_out == 0); 1427 } while (strm.avail_out == 0);
1318 1428
1319 assert(ret == Z_STREAM_END); 1429 assert(ret == Z_STREAM_END);
@@ -1347,13 +1457,12 @@ public:
1347 int ret = Z_OK; 1457 int ret = Z_OK;
1348 1458
1349 strm.avail_in = data_length; 1459 strm.avail_in = data_length;
1350 strm.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef *>(data)); 1460 strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
1351 1461
1352 const auto bufsiz = 16384; 1462 std::array<char, 16384> buff{};
1353 char buff[bufsiz];
1354 do { 1463 do {
1355 strm.avail_out = bufsiz; 1464 strm.avail_out = buff.size();
1356 strm.next_out = reinterpret_cast<Bytef*>(buff); 1465 strm.next_out = reinterpret_cast<Bytef*>(buff.data());
1357 1466
1358 ret = inflate(&strm, Z_NO_FLUSH); 1467 ret = inflate(&strm, Z_NO_FLUSH);
1359 assert(ret != Z_STREAM_ERROR); 1468 assert(ret != Z_STREAM_ERROR);
@@ -1363,10 +1472,10 @@ public:
1363 case Z_MEM_ERROR: inflateEnd(&strm); return false; 1472 case Z_MEM_ERROR: inflateEnd(&strm); return false;
1364 } 1473 }
1365 1474
1366 if (!callback(buff, bufsiz - strm.avail_out)) { return false; } 1475 if (!callback(buff.data(), buff.size() - strm.avail_out)) { return false; }
1367 } while (strm.avail_out == 0); 1476 } while (strm.avail_out == 0);
1368 1477
1369 return ret == Z_STREAM_END; 1478 return ret == Z_OK || ret == Z_STREAM_END;
1370 } 1479 }
1371 1480
1372private: 1481private:
@@ -1402,13 +1511,13 @@ inline bool read_headers(Stream &strm, Headers &headers) {
1402 const auto bufsiz = 2048; 1511 const auto bufsiz = 2048;
1403 char buf[bufsiz]; 1512 char buf[bufsiz];
1404 1513
1405 stream_line_reader reader(strm, buf, bufsiz); 1514 stream_line_reader line_reader(strm, buf, bufsiz);
1406 1515
1407 for (;;) { 1516 for (;;) {
1408 if (!reader.getline()) { return false; } 1517 if (!line_reader.getline()) { return false; }
1409 if (!strcmp(reader.ptr(), "\r\n")) { break; } 1518 if (!strcmp(line_reader.ptr(), "\r\n")) { break; }
1410 std::cmatch m; 1519 std::cmatch m;
1411 if (std::regex_match(reader.ptr(), m, re)) { 1520 if (std::regex_match(line_reader.ptr(), m, re)) {
1412 auto key = std::string(m[1]); 1521 auto key = std::string(m[1]);
1413 auto val = std::string(m[2]); 1522 auto val = std::string(m[2]);
1414 headers.emplace(key, val); 1523 headers.emplace(key, val);
@@ -1418,12 +1527,8 @@ inline bool read_headers(Stream &strm, Headers &headers) {
1418 return true; 1527 return true;
1419} 1528}
1420 1529
1421typedef std::function<bool(const char *data, size_t data_length)>
1422 ContentReceiverCore;
1423
1424inline bool read_content_with_length(Stream &strm, uint64_t len, 1530inline bool read_content_with_length(Stream &strm, uint64_t len,
1425 Progress progress, 1531 Progress progress, ContentReceiver out) {
1426 ContentReceiverCore out) {
1427 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 1532 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1428 1533
1429 uint64_t r = 0; 1534 uint64_t r = 0;
@@ -1455,7 +1560,7 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
1455 } 1560 }
1456} 1561}
1457 1562
1458inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) { 1563inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
1459 char buf[CPPHTTPLIB_RECV_BUFSIZ]; 1564 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1460 for (;;) { 1565 for (;;) {
1461 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); 1566 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
@@ -1470,33 +1575,34 @@ inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) {
1470 return true; 1575 return true;
1471} 1576}
1472 1577
1473inline bool read_content_chunked(Stream &strm, ContentReceiverCore out) { 1578inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
1474 const auto bufsiz = 16; 1579 const auto bufsiz = 16;
1475 char buf[bufsiz]; 1580 char buf[bufsiz];
1476 1581
1477 stream_line_reader reader(strm, buf, bufsiz); 1582 stream_line_reader line_reader(strm, buf, bufsiz);
1478 1583
1479 if (!reader.getline()) { return false; } 1584 if (!line_reader.getline()) { return false; }
1480 1585
1481 auto chunk_len = std::stoi(reader.ptr(), 0, 16); 1586 auto chunk_len = std::stoi(line_reader.ptr(), 0, 16);
1482 1587
1483 while (chunk_len > 0) { 1588 while (chunk_len > 0) {
1484 if (!read_content_with_length(strm, chunk_len, nullptr, out)) { 1589 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
1485 return false; 1590 return false;
1486 } 1591 }
1487 1592
1488 if (!reader.getline()) { return false; } 1593 if (!line_reader.getline()) { return false; }
1489 1594
1490 if (strcmp(reader.ptr(), "\r\n")) { break; } 1595 if (strcmp(line_reader.ptr(), "\r\n")) { break; }
1491 1596
1492 if (!reader.getline()) { return false; } 1597 if (!line_reader.getline()) { return false; }
1493 1598
1494 chunk_len = std::stoi(reader.ptr(), 0, 16); 1599 chunk_len = std::stoi(line_reader.ptr(), 0, 16);
1495 } 1600 }
1496 1601
1497 if (chunk_len == 0) { 1602 if (chunk_len == 0) {
1498 // Reader terminator after chunks 1603 // Reader terminator after chunks
1499 if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) return false; 1604 if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n"))
1605 return false;
1500 } 1606 }
1501 1607
1502 return true; 1608 return true;
@@ -1509,9 +1615,9 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
1509 1615
1510template <typename T> 1616template <typename T>
1511bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, 1617bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
1512 Progress progress, ContentReceiverCore receiver) { 1618 Progress progress, ContentReceiver receiver) {
1513 1619
1514 ContentReceiverCore out = [&](const char *buf, size_t n) { 1620 ContentReceiver out = [&](const char *buf, size_t n) {
1515 return receiver(buf, n); 1621 return receiver(buf, n);
1516 }; 1622 };
1517 1623
@@ -1580,7 +1686,8 @@ inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
1580 return write_len; 1686 return write_len;
1581} 1687}
1582 1688
1583inline ssize_t write_content(Stream &strm, ContentProvider content_provider, 1689inline ssize_t write_content(Stream &strm,
1690 ContentProviderWithCloser content_provider,
1584 size_t offset, size_t length) { 1691 size_t offset, size_t length) {
1585 size_t begin_offset = offset; 1692 size_t begin_offset = offset;
1586 size_t end_offset = offset + length; 1693 size_t end_offset = offset + length;
@@ -1598,8 +1705,9 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
1598 return static_cast<ssize_t>(offset - begin_offset); 1705 return static_cast<ssize_t>(offset - begin_offset);
1599} 1706}
1600 1707
1601inline ssize_t write_content_chunked(Stream &strm, 1708inline ssize_t
1602 ContentProvider content_provider) { 1709write_content_chunked(Stream &strm,
1710 ContentProviderWithCloser content_provider) {
1603 size_t offset = 0; 1711 size_t offset = 0;
1604 auto data_available = true; 1712 auto data_available = true;
1605 ssize_t total_written_length = 0; 1713 ssize_t total_written_length = 0;
@@ -1742,7 +1850,7 @@ inline bool parse_multipart_formdata(const std::string &boundary,
1742 static std::string dash = "--"; 1850 static std::string dash = "--";
1743 static std::string crlf = "\r\n"; 1851 static std::string crlf = "\r\n";
1744 1852
1745 static std::regex re_content_type("Content-Type: (.*?)", 1853 static std::regex re_content_type("Content-Type: (.*?)$",
1746 std::regex_constants::icase); 1854 std::regex_constants::icase);
1747 1855
1748 static std::regex re_content_disposition( 1856 static std::regex re_content_disposition(
@@ -1811,32 +1919,33 @@ inline bool parse_multipart_formdata(const std::string &boundary,
1811 1919
1812inline bool parse_range_header(const std::string &s, Ranges &ranges) { 1920inline bool parse_range_header(const std::string &s, Ranges &ranges) {
1813 try { 1921 try {
1814 static auto re = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); 1922 static auto re_first_range =
1923 std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
1815 std::smatch m; 1924 std::smatch m;
1816 if (std::regex_match(s, m, re)) { 1925 if (std::regex_match(s, m, re_first_range)) {
1817 auto pos = m.position(1); 1926 auto pos = m.position(1);
1818 auto len = m.length(1); 1927 auto len = m.length(1);
1819 detail::split(&s[pos], &s[pos + len], ',', 1928 detail::split(
1820 [&](const char *b, const char *e) { 1929 &s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
1821 static auto re = std::regex(R"(\s*(\d*)-(\d*))"); 1930 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
1822 std::cmatch m; 1931 std::cmatch m;
1823 if (std::regex_match(b, e, m, re)) { 1932 if (std::regex_match(b, e, m, re_another_range)) {
1824 ssize_t first = -1; 1933 ssize_t first = -1;
1825 if (!m.str(1).empty()) { 1934 if (!m.str(1).empty()) {
1826 first = static_cast<ssize_t>(std::stoll(m.str(1))); 1935 first = static_cast<ssize_t>(std::stoll(m.str(1)));
1827 } 1936 }
1828 1937
1829 ssize_t last = -1; 1938 ssize_t last = -1;
1830 if (!m.str(2).empty()) { 1939 if (!m.str(2).empty()) {
1831 last = static_cast<ssize_t>(std::stoll(m.str(2))); 1940 last = static_cast<ssize_t>(std::stoll(m.str(2)));
1832 } 1941 }
1833 1942
1834 if (first != -1 && last != -1 && first > last) { 1943 if (first != -1 && last != -1 && first > last) {
1835 throw std::runtime_error("invalid range error"); 1944 throw std::runtime_error("invalid range error");
1836 } 1945 }
1837 ranges.emplace_back(std::make_pair(first, last)); 1946 ranges.emplace_back(std::make_pair(first, last));
1838 } 1947 }
1839 }); 1948 });
1840 return true; 1949 return true;
1841 } 1950 }
1842 return false; 1951 return false;
@@ -1988,7 +2097,7 @@ get_range_offset_and_length(const Request &req, const Response &res,
1988 size_t index) { 2097 size_t index) {
1989 auto r = req.ranges[index]; 2098 auto r = req.ranges[index];
1990 2099
1991 if (r.second == -1) { r.second = res.content_provider_resource_length - 1; } 2100 if (r.second == -1) { r.second = res.content_length - 1; }
1992 2101
1993 return std::make_pair(r.first, r.second - r.first + 1); 2102 return std::make_pair(r.first, r.second - r.first + 1);
1994} 2103}
@@ -2123,7 +2232,7 @@ inline void Response::set_content_provider(
2123 std::function<void(size_t offset, size_t length, DataSink sink)> provider, 2232 std::function<void(size_t offset, size_t length, DataSink sink)> provider,
2124 std::function<void()> resource_releaser) { 2233 std::function<void()> resource_releaser) {
2125 assert(length > 0); 2234 assert(length > 0);
2126 content_provider_resource_length = length; 2235 content_length = length;
2127 content_provider = [provider](size_t offset, size_t length, DataSink sink, 2236 content_provider = [provider](size_t offset, size_t length, DataSink sink,
2128 Done) { provider(offset, length, sink); }; 2237 Done) { provider(offset, length, sink); };
2129 content_provider_resource_releaser = resource_releaser; 2238 content_provider_resource_releaser = resource_releaser;
@@ -2132,7 +2241,7 @@ inline void Response::set_content_provider(
2132inline void Response::set_chunked_content_provider( 2241inline void Response::set_chunked_content_provider(
2133 std::function<void(size_t offset, DataSink sink, Done done)> provider, 2242 std::function<void(size_t offset, DataSink sink, Done done)> provider,
2134 std::function<void()> resource_releaser) { 2243 std::function<void()> resource_releaser) {
2135 content_provider_resource_length = 0; 2244 content_length = 0;
2136 content_provider = [provider](size_t offset, size_t, DataSink sink, 2245 content_provider = [provider](size_t offset, size_t, DataSink sink,
2137 Done done) { provider(offset, sink, done); }; 2246 Done done) { provider(offset, sink, done); };
2138 content_provider_resource_releaser = resource_releaser; 2247 content_provider_resource_releaser = resource_releaser;
@@ -2141,18 +2250,17 @@ inline void Response::set_chunked_content_provider(
2141// Rstream implementation 2250// Rstream implementation
2142template <typename... Args> 2251template <typename... Args>
2143inline int Stream::write_format(const char *fmt, const Args &... args) { 2252inline int Stream::write_format(const char *fmt, const Args &... args) {
2144 const auto bufsiz = 2048; 2253 std::array<char, 2048> buf;
2145 char buf[bufsiz];
2146 2254
2147#if defined(_MSC_VER) && _MSC_VER < 1900 2255#if defined(_MSC_VER) && _MSC_VER < 1900
2148 auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); 2256 auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
2149#else 2257#else
2150 auto n = snprintf(buf, bufsiz - 1, fmt, args...); 2258 auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...);
2151#endif 2259#endif
2152 if (n <= 0) { return n; } 2260 if (n <= 0) { return n; }
2153 2261
2154 if (n >= bufsiz - 1) { 2262 if (n >= static_cast<int>(buf.size()) - 1) {
2155 std::vector<char> glowable_buf(bufsiz); 2263 std::vector<char> glowable_buf(buf.size());
2156 2264
2157 while (n >= static_cast<int>(glowable_buf.size() - 1)) { 2265 while (n >= static_cast<int>(glowable_buf.size() - 1)) {
2158 glowable_buf.resize(glowable_buf.size() * 2); 2266 glowable_buf.resize(glowable_buf.size() * 2);
@@ -2165,18 +2273,20 @@ inline int Stream::write_format(const char *fmt, const Args &... args) {
2165 } 2273 }
2166 return write(&glowable_buf[0], n); 2274 return write(&glowable_buf[0], n);
2167 } else { 2275 } else {
2168 return write(buf, n); 2276 return write(buf.data(), n);
2169 } 2277 }
2170} 2278}
2171 2279
2172// Socket stream implementation 2280// Socket stream implementation
2173inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} 2281inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
2282 time_t read_timeout_usec)
2283 : sock_(sock), read_timeout_sec_(read_timeout_sec),
2284 read_timeout_usec_(read_timeout_usec) {}
2174 2285
2175inline SocketStream::~SocketStream() {} 2286inline SocketStream::~SocketStream() {}
2176 2287
2177inline int SocketStream::read(char *ptr, size_t size) { 2288inline int SocketStream::read(char *ptr, size_t size) {
2178 if (detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, 2289 if (detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
2179 CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) {
2180 return recv(sock_, ptr, static_cast<int>(size), 0); 2290 return recv(sock_, ptr, static_cast<int>(size), 0);
2181 } 2291 }
2182 return -1; 2292 return -1;
@@ -2227,6 +2337,8 @@ inline const std::string &BufferStream::get_buffer() const { return buffer; }
2227// HTTP server implementation 2337// HTTP server implementation
2228inline Server::Server() 2338inline Server::Server()
2229 : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), 2339 : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
2340 read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
2341 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
2230 payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false), 2342 payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false),
2231 svr_sock_(INVALID_SOCKET) { 2343 svr_sock_(INVALID_SOCKET) {
2232#ifndef _WIN32 2344#ifndef _WIN32
@@ -2235,8 +2347,10 @@ inline Server::Server()
2235 new_task_queue = [] { 2347 new_task_queue = [] {
2236#if CPPHTTPLIB_THREAD_POOL_COUNT > 0 2348#if CPPHTTPLIB_THREAD_POOL_COUNT > 0
2237 return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); 2349 return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT);
2238#else 2350#elif CPPHTTPLIB_THREAD_POOL_COUNT == 0
2239 return new Threads(); 2351 return new Threads();
2352#else
2353 return new NoThread();
2240#endif 2354#endif
2241 }; 2355 };
2242} 2356}
@@ -2253,16 +2367,37 @@ inline Server &Server::Post(const char *pattern, Handler handler) {
2253 return *this; 2367 return *this;
2254} 2368}
2255 2369
2370inline Server &Server::Post(const char *pattern,
2371 HandlerWithContentReader handler) {
2372 post_handlers_for_content_reader.push_back(
2373 std::make_pair(std::regex(pattern), handler));
2374 return *this;
2375}
2376
2256inline Server &Server::Put(const char *pattern, Handler handler) { 2377inline Server &Server::Put(const char *pattern, Handler handler) {
2257 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 2378 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2258 return *this; 2379 return *this;
2259} 2380}
2260 2381
2382inline Server &Server::Put(const char *pattern,
2383 HandlerWithContentReader handler) {
2384 put_handlers_for_content_reader.push_back(
2385 std::make_pair(std::regex(pattern), handler));
2386 return *this;
2387}
2388
2261inline Server &Server::Patch(const char *pattern, Handler handler) { 2389inline Server &Server::Patch(const char *pattern, Handler handler) {
2262 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 2390 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2263 return *this; 2391 return *this;
2264} 2392}
2265 2393
2394inline Server &Server::Patch(const char *pattern,
2395 HandlerWithContentReader handler) {
2396 patch_handlers_for_content_reader.push_back(
2397 std::make_pair(std::regex(pattern), handler));
2398 return *this;
2399}
2400
2266inline Server &Server::Delete(const char *pattern, Handler handler) { 2401inline Server &Server::Delete(const char *pattern, Handler handler) {
2267 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); 2402 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2268 return *this; 2403 return *this;
@@ -2282,23 +2417,32 @@ inline bool Server::set_base_dir(const char *path) {
2282} 2417}
2283 2418
2284inline void Server::set_file_request_handler(Handler handler) { 2419inline void Server::set_file_request_handler(Handler handler) {
2285 file_request_handler_ = handler; 2420 file_request_handler_ = std::move(handler);
2286} 2421}
2287 2422
2288inline void Server::set_error_handler(Handler handler) { 2423inline void Server::set_error_handler(Handler handler) {
2289 error_handler_ = handler; 2424 error_handler_ = std::move(handler);
2290} 2425}
2291 2426
2292inline void Server::set_logger(Logger logger) { logger_ = logger; } 2427inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
2293 2428
2294inline void Server::set_keep_alive_max_count(size_t count) { 2429inline void Server::set_keep_alive_max_count(size_t count) {
2295 keep_alive_max_count_ = count; 2430 keep_alive_max_count_ = count;
2296} 2431}
2297 2432
2433inline void Server::set_read_timeout(time_t sec, time_t usec) {
2434 read_timeout_sec_ = sec;
2435 read_timeout_usec_ = usec;
2436}
2437
2298inline void Server::set_payload_max_length(size_t length) { 2438inline void Server::set_payload_max_length(size_t length) {
2299 payload_max_length_ = length; 2439 payload_max_length_ = length;
2300} 2440}
2301 2441
2442inline bool Server::bind_to_port(const char *host, int port, int socket_flags) {
2443 if (bind_internal(host, port, socket_flags) < 0) return false;
2444 return true;
2445}
2302inline int Server::bind_to_any_port(const char *host, int socket_flags) { 2446inline int Server::bind_to_any_port(const char *host, int socket_flags) {
2303 return bind_internal(host, 0, socket_flags); 2447 return bind_internal(host, 0, socket_flags);
2304} 2448}
@@ -2306,8 +2450,7 @@ inline int Server::bind_to_any_port(const char *host, int socket_flags) {
2306inline bool Server::listen_after_bind() { return listen_internal(); } 2450inline bool Server::listen_after_bind() { return listen_internal(); }
2307 2451
2308inline bool Server::listen(const char *host, int port, int socket_flags) { 2452inline bool Server::listen(const char *host, int port, int socket_flags) {
2309 if (bind_internal(host, port, socket_flags) < 0) return false; 2453 return bind_to_port(host, port, socket_flags) && listen_internal();
2310 return listen_internal();
2311} 2454}
2312 2455
2313inline bool Server::is_running() const { return is_running_; } 2456inline bool Server::is_running() const { return is_running_; }
@@ -2322,8 +2465,9 @@ inline void Server::stop() {
2322} 2465}
2323 2466
2324inline bool Server::parse_request_line(const char *s, Request &req) { 2467inline bool Server::parse_request_line(const char *s, Request &req) {
2325 static std::regex re("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " 2468 static std::regex re(
2326 "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); 2469 "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
2470 "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n");
2327 2471
2328 std::cmatch m; 2472 std::cmatch m;
2329 if (std::regex_match(s, m, re)) { 2473 if (std::regex_match(s, m, re)) {
@@ -2388,17 +2532,17 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
2388 } 2532 }
2389 2533
2390 if (res.body.empty()) { 2534 if (res.body.empty()) {
2391 if (res.content_provider_resource_length > 0) { 2535 if (res.content_length > 0) {
2392 size_t length = 0; 2536 size_t length = 0;
2393 if (req.ranges.empty()) { 2537 if (req.ranges.empty()) {
2394 length = res.content_provider_resource_length; 2538 length = res.content_length;
2395 } else if (req.ranges.size() == 1) { 2539 } else if (req.ranges.size() == 1) {
2396 auto offsets = detail::get_range_offset_and_length( 2540 auto offsets =
2397 req, res.content_provider_resource_length, 0); 2541 detail::get_range_offset_and_length(req, res.content_length, 0);
2398 auto offset = offsets.first; 2542 auto offset = offsets.first;
2399 length = offsets.second; 2543 length = offsets.second;
2400 auto content_range = detail::make_content_range_header_field( 2544 auto content_range = detail::make_content_range_header_field(
2401 offset, length, res.content_provider_resource_length); 2545 offset, length, res.content_length);
2402 res.set_header("Content-Range", content_range); 2546 res.set_header("Content-Range", content_range);
2403 } else { 2547 } else {
2404 length = detail::get_multipart_ranges_data_length(req, res, boundary, 2548 length = detail::get_multipart_ranges_data_length(req, res, boundary,
@@ -2468,15 +2612,15 @@ inline bool
2468Server::write_content_with_provider(Stream &strm, const Request &req, 2612Server::write_content_with_provider(Stream &strm, const Request &req,
2469 Response &res, const std::string &boundary, 2613 Response &res, const std::string &boundary,
2470 const std::string &content_type) { 2614 const std::string &content_type) {
2471 if (res.content_provider_resource_length) { 2615 if (res.content_length) {
2472 if (req.ranges.empty()) { 2616 if (req.ranges.empty()) {
2473 if (detail::write_content(strm, res.content_provider, 0, 2617 if (detail::write_content(strm, res.content_provider, 0,
2474 res.content_provider_resource_length) < 0) { 2618 res.content_length) < 0) {
2475 return false; 2619 return false;
2476 } 2620 }
2477 } else if (req.ranges.size() == 1) { 2621 } else if (req.ranges.size() == 1) {
2478 auto offsets = detail::get_range_offset_and_length( 2622 auto offsets =
2479 req, res.content_provider_resource_length, 0); 2623 detail::get_range_offset_and_length(req, res.content_length, 0);
2480 auto offset = offsets.first; 2624 auto offset = offsets.first;
2481 auto length = offsets.second; 2625 auto length = offsets.second;
2482 if (detail::write_content(strm, res.content_provider, offset, length) < 2626 if (detail::write_content(strm, res.content_provider, offset, length) <
@@ -2497,6 +2641,48 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
2497 return true; 2641 return true;
2498} 2642}
2499 2643
2644inline bool Server::read_content(Stream &strm, bool last_connection,
2645 Request &req, Response &res) {
2646 if (!detail::read_content(strm, req, payload_max_length_, res.status,
2647 Progress(), [&](const char *buf, size_t n) {
2648 if (req.body.size() + n > req.body.max_size()) {
2649 return false;
2650 }
2651 req.body.append(buf, n);
2652 return true;
2653 })) {
2654 return write_response(strm, last_connection, req, res);
2655 }
2656
2657 const auto &content_type = req.get_header_value("Content-Type");
2658
2659 if (!content_type.find("application/x-www-form-urlencoded")) {
2660 detail::parse_query_text(req.body, req.params);
2661 } else if (!content_type.find("multipart/form-data")) {
2662 std::string boundary;
2663 if (!detail::parse_multipart_boundary(content_type, boundary) ||
2664 !detail::parse_multipart_formdata(boundary, req.body, req.files)) {
2665 res.status = 400;
2666 return write_response(strm, last_connection, req, res);
2667 }
2668 }
2669
2670 return true;
2671}
2672
2673inline bool
2674Server::read_content_with_content_receiver(Stream &strm, bool last_connection,
2675 Request &req, Response &res,
2676 ContentReceiver receiver) {
2677 if (!detail::read_content(
2678 strm, req, payload_max_length_, res.status, Progress(),
2679 [&](const char *buf, size_t n) { return receiver(buf, n); })) {
2680 return write_response(strm, last_connection, req, res);
2681 }
2682
2683 return true;
2684}
2685
2500inline bool Server::handle_file_request(Request &req, Response &res) { 2686inline bool Server::handle_file_request(Request &req, Response &res) {
2501 if (!base_dir_.empty() && detail::is_valid_path(req.path)) { 2687 if (!base_dir_.empty() && detail::is_valid_path(req.path)) {
2502 std::string path = base_dir_ + req.path; 2688 std::string path = base_dir_ + req.path;
@@ -2548,8 +2734,7 @@ inline int Server::bind_internal(const char *host, int port, int socket_flags) {
2548 if (address.ss_family == AF_INET) { 2734 if (address.ss_family == AF_INET) {
2549 return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port); 2735 return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port);
2550 } else if (address.ss_family == AF_INET6) { 2736 } else if (address.ss_family == AF_INET6) {
2551 return ntohs( 2737 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
2552 reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
2553 } else { 2738 } else {
2554 return -1; 2739 return -1;
2555 } 2740 }
@@ -2605,9 +2790,42 @@ inline bool Server::listen_internal() {
2605 return ret; 2790 return ret;
2606} 2791}
2607 2792
2608inline bool Server::routing(Request &req, Response &res) { 2793inline bool Server::routing(Request &req, Response &res, Stream &strm, bool last_connection) {
2794 // File handler
2609 if (req.method == "GET" && handle_file_request(req, res)) { return true; } 2795 if (req.method == "GET" && handle_file_request(req, res)) { return true; }
2610 2796
2797 // Content reader handler
2798 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") {
2799 ContentReader content_reader = [&](ContentReceiver receiver) {
2800 return read_content_with_content_receiver(strm, last_connection, req, res, receiver);
2801 };
2802
2803 if (req.method == "POST") {
2804 if (dispatch_request_for_content_reader(req, res, content_reader,
2805 post_handlers_for_content_reader)) {
2806 return true;
2807 }
2808 } else if (req.method == "PUT") {
2809 if (dispatch_request_for_content_reader(req, res, content_reader,
2810 put_handlers_for_content_reader)) {
2811 return true;
2812 }
2813 } else if (req.method == "PATCH") {
2814 if (dispatch_request_for_content_reader(
2815 req, res, content_reader, patch_handlers_for_content_reader)) {
2816 return true;
2817 }
2818 }
2819 }
2820
2821 // Read content into `req.body`
2822 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI") {
2823 if (!read_content(strm, last_connection, req, res)) {
2824 return false;
2825 }
2826 }
2827
2828 // Regular handler
2611 if (req.method == "GET" || req.method == "HEAD") { 2829 if (req.method == "GET" || req.method == "HEAD") {
2612 return dispatch_request(req, res, get_handlers_); 2830 return dispatch_request(req, res, get_handlers_);
2613 } else if (req.method == "POST") { 2831 } else if (req.method == "POST") {
@@ -2641,16 +2859,31 @@ inline bool Server::dispatch_request(Request &req, Response &res,
2641} 2859}
2642 2860
2643inline bool 2861inline bool
2862Server::dispatch_request_for_content_reader(Request &req, Response &res,
2863 ContentReader content_reader,
2864 HandersForContentReader &handlers) {
2865 for (const auto &x : handlers) {
2866 const auto &pattern = x.first;
2867 const auto &handler = x.second;
2868
2869 if (std::regex_match(req.path, req.matches, pattern)) {
2870 handler(req, res, content_reader);
2871 return true;
2872 }
2873 }
2874 return false;
2875}
2876
2877inline bool
2644Server::process_request(Stream &strm, bool last_connection, 2878Server::process_request(Stream &strm, bool last_connection,
2645 bool &connection_close, 2879 bool &connection_close,
2646 std::function<void(Request &)> setup_request) { 2880 const std::function<void(Request &)>& setup_request) {
2647 const auto bufsiz = 2048; 2881 std::array<char, 2048> buf{};
2648 char buf[bufsiz];
2649 2882
2650 detail::stream_line_reader reader(strm, buf, bufsiz); 2883 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
2651 2884
2652 // Connection has been closed on client 2885 // Connection has been closed on client
2653 if (!reader.getline()) { return false; } 2886 if (!line_reader.getline()) { return false; }
2654 2887
2655 Request req; 2888 Request req;
2656 Response res; 2889 Response res;
@@ -2658,7 +2891,7 @@ Server::process_request(Stream &strm, bool last_connection,
2658 res.version = "HTTP/1.1"; 2891 res.version = "HTTP/1.1";
2659 2892
2660 // Check if the request URI doesn't exceed the limit 2893 // Check if the request URI doesn't exceed the limit
2661 if (reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { 2894 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
2662 Headers dummy; 2895 Headers dummy;
2663 detail::read_headers(strm, dummy); 2896 detail::read_headers(strm, dummy);
2664 res.status = 414; 2897 res.status = 414;
@@ -2666,7 +2899,7 @@ Server::process_request(Stream &strm, bool last_connection,
2666 } 2899 }
2667 2900
2668 // Request line and headers 2901 // Request line and headers
2669 if (!parse_request_line(reader.ptr(), req) || 2902 if (!parse_request_line(line_reader.ptr(), req) ||
2670 !detail::read_headers(strm, req.headers)) { 2903 !detail::read_headers(strm, req.headers)) {
2671 res.status = 400; 2904 res.status = 400;
2672 return write_response(strm, last_connection, req, res); 2905 return write_response(strm, last_connection, req, res);
@@ -2683,33 +2916,6 @@ Server::process_request(Stream &strm, bool last_connection,
2683 2916
2684 req.set_header("REMOTE_ADDR", strm.get_remote_addr()); 2917 req.set_header("REMOTE_ADDR", strm.get_remote_addr());
2685 2918
2686 // Body
2687 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI") {
2688 if (!detail::read_content(strm, req, payload_max_length_, res.status,
2689 Progress(), [&](const char *buf, size_t n) {
2690 if (req.body.size() + n > req.body.max_size()) {
2691 return false;
2692 }
2693 req.body.append(buf, n);
2694 return true;
2695 })) {
2696 return write_response(strm, last_connection, req, res);
2697 }
2698
2699 const auto &content_type = req.get_header_value("Content-Type");
2700
2701 if (!content_type.find("application/x-www-form-urlencoded")) {
2702 detail::parse_query_text(req.body, req.params);
2703 } else if (!content_type.find("multipart/form-data")) {
2704 std::string boundary;
2705 if (!detail::parse_multipart_boundary(content_type, boundary) ||
2706 !detail::parse_multipart_formdata(boundary, req.body, req.files)) {
2707 res.status = 400;
2708 return write_response(strm, last_connection, req, res);
2709 }
2710 }
2711 }
2712
2713 if (req.has_header("Range")) { 2919 if (req.has_header("Range")) {
2714 const auto &range_header_value = req.get_header_value("Range"); 2920 const auto &range_header_value = req.get_header_value("Range");
2715 if (!detail::parse_range_header(range_header_value, req.ranges)) { 2921 if (!detail::parse_range_header(range_header_value, req.ranges)) {
@@ -2719,7 +2925,8 @@ Server::process_request(Stream &strm, bool last_connection,
2719 2925
2720 if (setup_request) { setup_request(req); } 2926 if (setup_request) { setup_request(req); }
2721 2927
2722 if (routing(req, res)) { 2928 // Rounting
2929 if (routing(req, res, strm, last_connection)) {
2723 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } 2930 if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
2724 } else { 2931 } else {
2725 if (res.status == -1) { res.status = 404; } 2932 if (res.status == -1) { res.status = 404; }
@@ -2732,7 +2939,7 @@ inline bool Server::is_valid() const { return true; }
2732 2939
2733inline bool Server::process_and_close_socket(socket_t sock) { 2940inline bool Server::process_and_close_socket(socket_t sock) {
2734 return detail::process_and_close_socket( 2941 return detail::process_and_close_socket(
2735 false, sock, keep_alive_max_count_, 2942 false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
2736 [this](Stream &strm, bool last_connection, bool &connection_close) { 2943 [this](Stream &strm, bool last_connection, bool &connection_close) {
2737 return process_request(strm, last_connection, connection_close, 2944 return process_request(strm, last_connection, connection_close,
2738 nullptr); 2945 nullptr);
@@ -2744,6 +2951,8 @@ inline Client::Client(const char *host, int port, time_t timeout_sec)
2744 : host_(host), port_(port), timeout_sec_(timeout_sec), 2951 : host_(host), port_(port), timeout_sec_(timeout_sec),
2745 host_and_port_(host_ + ":" + std::to_string(port_)), 2952 host_and_port_(host_ + ":" + std::to_string(port_)),
2746 keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), 2953 keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
2954 read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
2955 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
2747 follow_location_(false) {} 2956 follow_location_(false) {}
2748 2957
2749inline Client::~Client() {} 2958inline Client::~Client() {}
@@ -2770,17 +2979,16 @@ inline socket_t Client::create_client_socket() const {
2770} 2979}
2771 2980
2772inline bool Client::read_response_line(Stream &strm, Response &res) { 2981inline bool Client::read_response_line(Stream &strm, Response &res) {
2773 const auto bufsiz = 2048; 2982 std::array<char, 2048> buf;
2774 char buf[bufsiz];
2775 2983
2776 detail::stream_line_reader reader(strm, buf, bufsiz); 2984 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
2777 2985
2778 if (!reader.getline()) { return false; } 2986 if (!line_reader.getline()) { return false; }
2779 2987
2780 const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); 2988 const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n");
2781 2989
2782 std::cmatch m; 2990 std::cmatch m;
2783 if (std::regex_match(reader.ptr(), m, re)) { 2991 if (std::regex_match(line_reader.ptr(), m, re)) {
2784 res.version = std::string(m[1]); 2992 res.version = std::string(m[1]);
2785 res.status = std::stoi(std::string(m[2])); 2993 res.status = std::stoi(std::string(m[2]));
2786 } 2994 }
@@ -2816,7 +3024,8 @@ inline bool Client::send(const std::vector<Request> &requests,
2816 3024
2817 if (!process_and_close_socket( 3025 if (!process_and_close_socket(
2818 sock, requests.size() - i, 3026 sock, requests.size() - i,
2819 [&](Stream &strm, bool last_connection, bool &connection_close) -> bool { 3027 [&](Stream &strm, bool last_connection,
3028 bool &connection_close) -> bool {
2820 auto &req = requests[i]; 3029 auto &req = requests[i];
2821 auto res = Response(); 3030 auto res = Response();
2822 i++; 3031 i++;
@@ -2917,7 +3126,10 @@ inline void Client::write_request(Stream &strm, const Request &req,
2917 } 3126 }
2918 3127
2919 if (req.body.empty()) { 3128 if (req.body.empty()) {
2920 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { 3129 if (req.content_provider) {
3130 auto length = std::to_string(req.content_length);
3131 headers.emplace("Content-Length", length);
3132 } else {
2921 headers.emplace("Content-Length", "0"); 3133 headers.emplace("Content-Length", "0");
2922 } 3134 }
2923 } else { 3135 } else {
@@ -2933,12 +3145,74 @@ inline void Client::write_request(Stream &strm, const Request &req,
2933 3145
2934 detail::write_headers(bstrm, req, headers); 3146 detail::write_headers(bstrm, req, headers);
2935 3147
2936 // Body
2937 if (!req.body.empty()) { bstrm.write(req.body); }
2938
2939 // Flush buffer 3148 // Flush buffer
2940 auto &data = bstrm.get_buffer(); 3149 auto &data = bstrm.get_buffer();
2941 strm.write(data.data(), data.size()); 3150 strm.write(data.data(), data.size());
3151
3152 // Body
3153 if (req.body.empty()) {
3154 if (req.content_provider) {
3155 size_t offset = 0;
3156 size_t end_offset = req.content_length;
3157 while (offset < end_offset) {
3158 req.content_provider(offset, end_offset - offset,
3159 [&](const char *d, size_t l) {
3160 auto written_length = strm.write(d, l);
3161 offset += written_length;
3162 });
3163 }
3164 }
3165 } else {
3166 strm.write(req.body);
3167 }
3168}
3169
3170inline std::shared_ptr<Response> Client::send_with_content_provider(
3171 const char *method, const char *path, const Headers &headers,
3172 const std::string &body, size_t content_length,
3173 ContentProvider content_provider, const char *content_type, bool compress) {
3174#ifndef CPPHTTPLIB_ZLIB_SUPPORT
3175 (void)compress;
3176#endif
3177
3178 Request req;
3179 req.method = method;
3180 req.headers = headers;
3181 req.path = path;
3182
3183 req.headers.emplace("Content-Type", content_type);
3184
3185#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3186 if (compress) {
3187 if (content_provider) {
3188 size_t offset = 0;
3189 while (offset < content_length) {
3190 content_provider(offset, content_length - offset,
3191 [&](const char *data, size_t data_len) {
3192 req.body.append(data, data_len);
3193 offset += data_len;
3194 });
3195 }
3196 } else {
3197 req.body = body;
3198 }
3199
3200 if (!detail::compress(req.body)) { return nullptr; }
3201 req.headers.emplace("Content-Encoding", "gzip");
3202 } else
3203#endif
3204 {
3205 if (content_provider) {
3206 req.content_length = content_length;
3207 req.content_provider = content_provider;
3208 } else {
3209 req.body = body;
3210 }
3211 }
3212
3213 auto res = std::make_shared<Response>();
3214
3215 return send(req, *res) ? res : nullptr;
2942} 3216}
2943 3217
2944inline bool Client::process_request(Stream &strm, const Request &req, 3218inline bool Client::process_request(Stream &strm, const Request &req,
@@ -2964,20 +3238,15 @@ inline bool Client::process_request(Stream &strm, const Request &req,
2964 3238
2965 // Body 3239 // Body
2966 if (req.method != "HEAD") { 3240 if (req.method != "HEAD") {
2967 detail::ContentReceiverCore out = [&](const char *buf, size_t n) { 3241 ContentReceiver out = [&](const char *buf, size_t n) {
2968 if (res.body.size() + n > res.body.max_size()) { return false; } 3242 if (res.body.size() + n > res.body.max_size()) { return false; }
2969 res.body.append(buf, n); 3243 res.body.append(buf, n);
2970 return true; 3244 return true;
2971 }; 3245 };
2972 3246
2973 if (req.content_receiver) { 3247 if (req.content_receiver) {
2974 auto offset = std::make_shared<size_t>(); 3248 out = [&](const char *buf, size_t n) {
2975 auto length = get_header_value_uint64(res.headers, "Content-Length", 0); 3249 return req.content_receiver(buf, n);
2976 auto receiver = req.content_receiver;
2977 out = [offset, length, receiver](const char *buf, size_t n) {
2978 auto ret = receiver(buf, n, *offset, length);
2979 (*offset) += n;
2980 return ret;
2981 }; 3250 };
2982 } 3251 }
2983 3252
@@ -2997,7 +3266,9 @@ inline bool Client::process_and_close_socket(
2997 bool &connection_close)> 3266 bool &connection_close)>
2998 callback) { 3267 callback) {
2999 request_count = std::min(request_count, keep_alive_max_count_); 3268 request_count = std::min(request_count, keep_alive_max_count_);
3000 return detail::process_and_close_socket(true, sock, request_count, callback); 3269 return detail::process_and_close_socket(true, sock, request_count,
3270 read_timeout_sec_, read_timeout_usec_,
3271 callback);
3001} 3272}
3002 3273
3003inline bool Client::is_ssl() const { return false; } 3274inline bool Client::is_ssl() const { return false; }
@@ -3009,7 +3280,7 @@ inline std::shared_ptr<Response> Client::Get(const char *path) {
3009 3280
3010inline std::shared_ptr<Response> Client::Get(const char *path, 3281inline std::shared_ptr<Response> Client::Get(const char *path,
3011 Progress progress) { 3282 Progress progress) {
3012 return Get(path, Headers(), progress); 3283 return Get(path, Headers(), std::move(progress));
3013} 3284}
3014 3285
3015inline std::shared_ptr<Response> Client::Get(const char *path, 3286inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3024,7 +3295,7 @@ Client::Get(const char *path, const Headers &headers, Progress progress) {
3024 req.method = "GET"; 3295 req.method = "GET";
3025 req.path = path; 3296 req.path = path;
3026 req.headers = headers; 3297 req.headers = headers;
3027 req.progress = progress; 3298 req.progress = std::move(progress);
3028 3299
3029 auto res = std::make_shared<Response>(); 3300 auto res = std::make_shared<Response>();
3030 return send(req, *res) ? res : nullptr; 3301 return send(req, *res) ? res : nullptr;
@@ -3033,27 +3304,27 @@ Client::Get(const char *path, const Headers &headers, Progress progress) {
3033inline std::shared_ptr<Response> Client::Get(const char *path, 3304inline std::shared_ptr<Response> Client::Get(const char *path,
3034 ContentReceiver content_receiver) { 3305 ContentReceiver content_receiver) {
3035 Progress dummy; 3306 Progress dummy;
3036 return Get(path, Headers(), nullptr, content_receiver, dummy); 3307 return Get(path, Headers(), nullptr, std::move(content_receiver), dummy);
3037} 3308}
3038 3309
3039inline std::shared_ptr<Response> Client::Get(const char *path, 3310inline std::shared_ptr<Response> Client::Get(const char *path,
3040 ContentReceiver content_receiver, 3311 ContentReceiver content_receiver,
3041 Progress progress) { 3312 Progress progress) {
3042 return Get(path, Headers(), nullptr, content_receiver, progress); 3313 return Get(path, Headers(), nullptr, std::move(content_receiver), progress);
3043} 3314}
3044 3315
3045inline std::shared_ptr<Response> Client::Get(const char *path, 3316inline std::shared_ptr<Response> Client::Get(const char *path,
3046 const Headers &headers, 3317 const Headers &headers,
3047 ContentReceiver content_receiver) { 3318 ContentReceiver content_receiver) {
3048 Progress dummy; 3319 Progress dummy;
3049 return Get(path, headers, nullptr, content_receiver, dummy); 3320 return Get(path, headers, nullptr, std::move(content_receiver), dummy);
3050} 3321}
3051 3322
3052inline std::shared_ptr<Response> Client::Get(const char *path, 3323inline std::shared_ptr<Response> Client::Get(const char *path,
3053 const Headers &headers, 3324 const Headers &headers,
3054 ContentReceiver content_receiver, 3325 ContentReceiver content_receiver,
3055 Progress progress) { 3326 Progress progress) {
3056 return Get(path, headers, nullptr, content_receiver, progress); 3327 return Get(path, headers, nullptr, std::move(content_receiver), progress);
3057} 3328}
3058 3329
3059inline std::shared_ptr<Response> Client::Get(const char *path, 3330inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3061,7 +3332,7 @@ inline std::shared_ptr<Response> Client::Get(const char *path,
3061 ResponseHandler response_handler, 3332 ResponseHandler response_handler,
3062 ContentReceiver content_receiver) { 3333 ContentReceiver content_receiver) {
3063 Progress dummy; 3334 Progress dummy;
3064 return Get(path, headers, response_handler, content_receiver, dummy); 3335 return Get(path, headers, std::move(response_handler), content_receiver, dummy);
3065} 3336}
3066 3337
3067inline std::shared_ptr<Response> Client::Get(const char *path, 3338inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3073,9 +3344,9 @@ inline std::shared_ptr<Response> Client::Get(const char *path,
3073 req.method = "GET"; 3344 req.method = "GET";
3074 req.path = path; 3345 req.path = path;
3075 req.headers = headers; 3346 req.headers = headers;
3076 req.response_handler = response_handler; 3347 req.response_handler = std::move(response_handler);
3077 req.content_receiver = content_receiver; 3348 req.content_receiver = std::move(content_receiver);
3078 req.progress = progress; 3349 req.progress = std::move(progress);
3079 3350
3080 auto res = std::make_shared<Response>(); 3351 auto res = std::make_shared<Response>();
3081 return send(req, *res) ? res : nullptr; 3352 return send(req, *res) ? res : nullptr;
@@ -3099,34 +3370,45 @@ inline std::shared_ptr<Response> Client::Head(const char *path,
3099 3370
3100inline std::shared_ptr<Response> Client::Post(const char *path, 3371inline std::shared_ptr<Response> Client::Post(const char *path,
3101 const std::string &body, 3372 const std::string &body,
3102 const char *content_type) { 3373 const char *content_type,
3103 return Post(path, Headers(), body, content_type); 3374 bool compress) {
3375 return Post(path, Headers(), body, content_type, compress);
3104} 3376}
3105 3377
3106inline std::shared_ptr<Response> Client::Post(const char *path, 3378inline std::shared_ptr<Response>
3107 const Headers &headers, 3379Client::Post(const char *path, const Headers &headers, const std::string &body,
3108 const std::string &body, 3380 const char *content_type, bool compress) {
3109 const char *content_type) { 3381 return send_with_content_provider("POST", path, headers, body, 0, nullptr,
3110 Request req; 3382 content_type, compress);
3111 req.method = "POST"; 3383}
3112 req.headers = headers;
3113 req.path = path;
3114
3115 req.headers.emplace("Content-Type", content_type);
3116 req.body = body;
3117
3118 auto res = std::make_shared<Response>();
3119 3384
3120 return send(req, *res) ? res : nullptr; 3385inline std::shared_ptr<Response>
3386Client::Post(const char *path, const Params &params, bool compress) {
3387 return Post(path, Headers(), params, compress);
3121} 3388}
3122 3389
3123inline std::shared_ptr<Response> Client::Post(const char *path, 3390inline std::shared_ptr<Response> Client::Post(const char *path,
3124 const Params &params) { 3391 size_t content_length,
3125 return Post(path, Headers(), params); 3392 ContentProvider content_provider,
3393 const char *content_type,
3394 bool compress) {
3395 return Post(path, Headers(), content_length, content_provider, content_type,
3396 compress);
3126} 3397}
3127 3398
3128inline std::shared_ptr<Response> 3399inline std::shared_ptr<Response>
3129Client::Post(const char *path, const Headers &headers, const Params &params) { 3400Client::Post(const char *path, const Headers &headers, size_t content_length,
3401 ContentProvider content_provider, const char *content_type,
3402 bool compress) {
3403 return send_with_content_provider("POST", path, headers, std::string(),
3404 content_length, content_provider,
3405 content_type, compress);
3406}
3407
3408inline std::shared_ptr<Response> Client::Post(const char *path,
3409 const Headers &headers,
3410 const Params &params,
3411 bool compress) {
3130 std::string query; 3412 std::string query;
3131 for (auto it = params.begin(); it != params.end(); ++it) { 3413 for (auto it = params.begin(); it != params.end(); ++it) {
3132 if (it != params.begin()) { query += "&"; } 3414 if (it != params.begin()) { query += "&"; }
@@ -3135,92 +3417,105 @@ Client::Post(const char *path, const Headers &headers, const Params &params) {
3135 query += detail::encode_url(it->second); 3417 query += detail::encode_url(it->second);
3136 } 3418 }
3137 3419
3138 return Post(path, headers, query, "application/x-www-form-urlencoded"); 3420 return Post(path, headers, query, "application/x-www-form-urlencoded",
3421 compress);
3139} 3422}
3140 3423
3141inline std::shared_ptr<Response> 3424inline std::shared_ptr<Response>
3142Client::Post(const char *path, const MultipartFormDataItems &items) { 3425Client::Post(const char *path, const MultipartFormDataItems &items,
3143 return Post(path, Headers(), items); 3426 bool compress) {
3427 return Post(path, Headers(), items, compress);
3144} 3428}
3145 3429
3146inline std::shared_ptr<Response> 3430inline std::shared_ptr<Response>
3147Client::Post(const char *path, const Headers &headers, 3431Client::Post(const char *path, const Headers &headers,
3148 const MultipartFormDataItems &items) { 3432 const MultipartFormDataItems &items, bool compress) {
3149 Request req;
3150 req.method = "POST";
3151 req.headers = headers;
3152 req.path = path;
3153
3154 auto boundary = detail::make_multipart_data_boundary(); 3433 auto boundary = detail::make_multipart_data_boundary();
3155 3434
3156 req.headers.emplace("Content-Type", 3435 std::string body;
3157 "multipart/form-data; boundary=" + boundary);
3158 3436
3159 for (const auto &item : items) { 3437 for (const auto &item : items) {
3160 req.body += "--" + boundary + "\r\n"; 3438 body += "--" + boundary + "\r\n";
3161 req.body += "Content-Disposition: form-data; name=\"" + item.name + "\""; 3439 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
3162 if (!item.filename.empty()) { 3440 if (!item.filename.empty()) {
3163 req.body += "; filename=\"" + item.filename + "\""; 3441 body += "; filename=\"" + item.filename + "\"";
3164 } 3442 }
3165 req.body += "\r\n"; 3443 body += "\r\n";
3166 if (!item.content_type.empty()) { 3444 if (!item.content_type.empty()) {
3167 req.body += "Content-Type: " + item.content_type + "\r\n"; 3445 body += "Content-Type: " + item.content_type + "\r\n";
3168 } 3446 }
3169 req.body += "\r\n"; 3447 body += "\r\n";
3170 req.body += item.content + "\r\n"; 3448 body += item.content + "\r\n";
3171 } 3449 }
3172 3450
3173 req.body += "--" + boundary + "--\r\n"; 3451 body += "--" + boundary + "--\r\n";
3174
3175 auto res = std::make_shared<Response>();
3176 3452
3177 return send(req, *res) ? res : nullptr; 3453 std::string content_type = "multipart/form-data; boundary=" + boundary;
3454 return Post(path, headers, body, content_type.c_str(), compress);
3178} 3455}
3179 3456
3180inline std::shared_ptr<Response> Client::Put(const char *path, 3457inline std::shared_ptr<Response> Client::Put(const char *path,
3181 const std::string &body, 3458 const std::string &body,
3182 const char *content_type) { 3459 const char *content_type,
3183 return Put(path, Headers(), body, content_type); 3460 bool compress) {
3461 return Put(path, Headers(), body, content_type, compress);
3184} 3462}
3185 3463
3186inline std::shared_ptr<Response> Client::Put(const char *path, 3464inline std::shared_ptr<Response>
3187 const Headers &headers, 3465Client::Put(const char *path, const Headers &headers, const std::string &body,
3188 const std::string &body, 3466 const char *content_type, bool compress) {
3189 const char *content_type) { 3467 return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
3190 Request req; 3468 content_type, compress);
3191 req.method = "PUT"; 3469}
3192 req.headers = headers;
3193 req.path = path;
3194
3195 req.headers.emplace("Content-Type", content_type);
3196 req.body = body;
3197
3198 auto res = std::make_shared<Response>();
3199 3470
3200 return send(req, *res) ? res : nullptr; 3471inline std::shared_ptr<Response> Client::Put(const char *path,
3472 size_t content_length,
3473 ContentProvider content_provider,
3474 const char *content_type,
3475 bool compress) {
3476 return Put(path, Headers(), content_length, content_provider, content_type,
3477 compress);
3201} 3478}
3202 3479
3203inline std::shared_ptr<Response> Client::Patch(const char *path, 3480inline std::shared_ptr<Response>
3204 const std::string &body, 3481Client::Put(const char *path, const Headers &headers, size_t content_length,
3205 const char *content_type) { 3482 ContentProvider content_provider, const char *content_type,
3206 return Patch(path, Headers(), body, content_type); 3483 bool compress) {
3484 return send_with_content_provider("PUT", path, headers, std::string(),
3485 content_length, content_provider,
3486 content_type, compress);
3207} 3487}
3208 3488
3209inline std::shared_ptr<Response> Client::Patch(const char *path, 3489inline std::shared_ptr<Response> Client::Patch(const char *path,
3210 const Headers &headers,
3211 const std::string &body, 3490 const std::string &body,
3212 const char *content_type) { 3491 const char *content_type,
3213 Request req; 3492 bool compress) {
3214 req.method = "PATCH"; 3493 return Patch(path, Headers(), body, content_type, compress);
3215 req.headers = headers; 3494}
3216 req.path = path;
3217 3495
3218 req.headers.emplace("Content-Type", content_type); 3496inline std::shared_ptr<Response>
3219 req.body = body; 3497Client::Patch(const char *path, const Headers &headers, const std::string &body,
3498 const char *content_type, bool compress) {
3499 return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
3500 content_type, compress);
3501}
3220 3502
3221 auto res = std::make_shared<Response>(); 3503inline std::shared_ptr<Response> Client::Patch(const char *path,
3504 size_t content_length,
3505 ContentProvider content_provider,
3506 const char *content_type,
3507 bool compress) {
3508 return Patch(path, Headers(), content_length, content_provider, content_type,
3509 compress);
3510}
3222 3511
3223 return send(req, *res) ? res : nullptr; 3512inline std::shared_ptr<Response>
3513Client::Patch(const char *path, const Headers &headers, size_t content_length,
3514 ContentProvider content_provider, const char *content_type,
3515 bool compress) {
3516 return send_with_content_provider("PATCH", path, headers, std::string(),
3517 content_length, content_provider,
3518 content_type, compress);
3224} 3519}
3225 3520
3226inline std::shared_ptr<Response> Client::Delete(const char *path) { 3521inline std::shared_ptr<Response> Client::Delete(const char *path) {
@@ -3275,6 +3570,11 @@ inline void Client::set_keep_alive_max_count(size_t count) {
3275 keep_alive_max_count_ = count; 3570 keep_alive_max_count_ = count;
3276} 3571}
3277 3572
3573inline void Client::set_read_timeout(time_t sec, time_t usec) {
3574 read_timeout_sec_ = sec;
3575 read_timeout_usec_ = usec;
3576}
3577
3278inline void Client::follow_location(bool on) { follow_location_ = on; } 3578inline void Client::follow_location(bool on) { follow_location_ = on; }
3279 3579
3280/* 3580/*
@@ -3284,11 +3584,10 @@ inline void Client::follow_location(bool on) { follow_location_ = on; }
3284namespace detail { 3584namespace detail {
3285 3585
3286template <typename U, typename V, typename T> 3586template <typename U, typename V, typename T>
3287inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, 3587inline bool process_and_close_socket_ssl(
3288 size_t keep_alive_max_count, 3588 bool is_client_request, socket_t sock, size_t keep_alive_max_count,
3289 SSL_CTX *ctx, std::mutex &ctx_mutex, 3589 time_t read_timeout_sec, time_t read_timeout_usec, SSL_CTX *ctx,
3290 U SSL_connect_or_accept, V setup, 3590 std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup, T callback) {
3291 T callback) {
3292 assert(keep_alive_max_count > 0); 3591 assert(keep_alive_max_count > 0);
3293 3592
3294 SSL *ssl = nullptr; 3593 SSL *ssl = nullptr;
@@ -3325,7 +3624,7 @@ inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock,
3325 (is_client_request || 3624 (is_client_request ||
3326 detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, 3625 detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
3327 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { 3626 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
3328 SSLSocketStream strm(sock, ssl); 3627 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
3329 auto last_connection = count == 1; 3628 auto last_connection = count == 1;
3330 auto connection_close = false; 3629 auto connection_close = false;
3331 3630
@@ -3335,7 +3634,7 @@ inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock,
3335 count--; 3634 count--;
3336 } 3635 }
3337 } else { 3636 } else {
3338 SSLSocketStream strm(sock, ssl); 3637 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
3339 auto dummy_connection_close = false; 3638 auto dummy_connection_close = false;
3340 ret = callback(ssl, strm, true, dummy_connection_close); 3639 ret = callback(ssl, strm, true, dummy_connection_close);
3341 } 3640 }
@@ -3382,11 +3681,20 @@ private:
3382class SSLInit { 3681class SSLInit {
3383public: 3682public:
3384 SSLInit() { 3683 SSLInit() {
3684#if OPENSSL_VERSION_NUMBER < 0x1010001fL
3385 SSL_load_error_strings(); 3685 SSL_load_error_strings();
3386 SSL_library_init(); 3686 SSL_library_init();
3687#else
3688 OPENSSL_init_ssl(
3689 OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
3690#endif
3387 } 3691 }
3388 3692
3389 ~SSLInit() { ERR_free_strings(); } 3693 ~SSLInit() {
3694#if OPENSSL_VERSION_NUMBER < 0x1010001fL
3695 ERR_free_strings();
3696#endif
3697 }
3390 3698
3391private: 3699private:
3392#if OPENSSL_VERSION_NUMBER < 0x10100000L 3700#if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -3399,15 +3707,17 @@ static SSLInit sslinit_;
3399} // namespace detail 3707} // namespace detail
3400 3708
3401// SSL socket stream implementation 3709// SSL socket stream implementation
3402inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) 3710inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
3403 : sock_(sock), ssl_(ssl) {} 3711 time_t read_timeout_sec,
3712 time_t read_timeout_usec)
3713 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
3714 read_timeout_usec_(read_timeout_usec) {}
3404 3715
3405inline SSLSocketStream::~SSLSocketStream() {} 3716inline SSLSocketStream::~SSLSocketStream() {}
3406 3717
3407inline int SSLSocketStream::read(char *ptr, size_t size) { 3718inline int SSLSocketStream::read(char *ptr, size_t size) {
3408 if (SSL_pending(ssl_) > 0 || 3719 if (SSL_pending(ssl_) > 0 ||
3409 detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, 3720 detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
3410 CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) {
3411 return SSL_read(ssl_, ptr, static_cast<int>(size)); 3721 return SSL_read(ssl_, ptr, static_cast<int>(size));
3412 } 3722 }
3413 return -1; 3723 return -1;
@@ -3476,8 +3786,8 @@ inline bool SSLServer::is_valid() const { return ctx_; }
3476 3786
3477inline bool SSLServer::process_and_close_socket(socket_t sock) { 3787inline bool SSLServer::process_and_close_socket(socket_t sock) {
3478 return detail::process_and_close_socket_ssl( 3788 return detail::process_and_close_socket_ssl(
3479 false, sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, 3789 false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
3480 [](SSL * /*ssl*/) { return true; }, 3790 ctx_, ctx_mutex_, SSL_accept, [](SSL * /*ssl*/) { return true; },
3481 [this](SSL *ssl, Stream &strm, bool last_connection, 3791 [this](SSL *ssl, Stream &strm, bool last_connection,
3482 bool &connection_close) { 3792 bool &connection_close) {
3483 return process_request(strm, last_connection, connection_close, 3793 return process_request(strm, last_connection, connection_close,
@@ -3527,9 +3837,7 @@ inline long SSLClient::get_openssl_verify_result() const {
3527 return verify_result_; 3837 return verify_result_;
3528} 3838}
3529 3839
3530inline SSL_CTX* SSLClient::ssl_context() const noexcept { 3840inline SSL_CTX *SSLClient::ssl_context() const noexcept { return ctx_; }
3531 return ctx_;
3532}
3533 3841
3534inline bool SSLClient::process_and_close_socket( 3842inline bool SSLClient::process_and_close_socket(
3535 socket_t sock, size_t request_count, 3843 socket_t sock, size_t request_count,
@@ -3541,7 +3849,8 @@ inline bool SSLClient::process_and_close_socket(
3541 3849
3542 return is_valid() && 3850 return is_valid() &&
3543 detail::process_and_close_socket_ssl( 3851 detail::process_and_close_socket_ssl(
3544 true, sock, request_count, ctx_, ctx_mutex_, 3852 true, sock, request_count, read_timeout_sec_, read_timeout_usec_,
3853 ctx_, ctx_mutex_,
3545 [&](SSL *ssl) { 3854 [&](SSL *ssl) {
3546 if (ca_cert_file_path_.empty()) { 3855 if (ca_cert_file_path_.empty()) {
3547 SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); 3856 SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);