diff options
Diffstat (limited to '')
| -rw-r--r-- | externals/httplib/httplib.h | 981 |
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 |
| 65 | typedef __int64 ssize_t; | 70 | using ssize_t = __int64; |
| 66 | #else | 71 | #else |
| 67 | typedef int ssize_t; | 72 | using 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 | ||
| 103 | typedef SOCKET socket_t; | 108 | using 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 | ||
| 123 | typedef int socket_t; | 128 | using 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 | */ | ||
| 165 | namespace httplib { | 174 | namespace httplib { |
| 166 | 175 | ||
| 167 | namespace detail { | 176 | namespace detail { |
| @@ -178,27 +187,27 @@ struct ci { | |||
| 178 | 187 | ||
| 179 | enum class HttpVersion { v1_0 = 0, v1_1 }; | 188 | enum class HttpVersion { v1_0 = 0, v1_1 }; |
| 180 | 189 | ||
| 181 | typedef std::multimap<std::string, std::string, detail::ci> Headers; | 190 | using Headers = std::multimap<std::string, std::string, detail::ci>; |
| 191 | |||
| 192 | using Params = std::multimap<std::string, std::string>; | ||
| 193 | using Match = std::smatch; | ||
| 194 | |||
| 195 | using DataSink = std::function<void(const char *data, size_t data_len)>; | ||
| 182 | 196 | ||
| 183 | typedef std::multimap<std::string, std::string> Params; | 197 | using Done = std::function<void()>; |
| 184 | typedef std::smatch Match; | ||
| 185 | 198 | ||
| 186 | typedef std::function<void(const char *data, size_t data_len)> DataSink; | 199 | using ContentProvider = std::function<void(size_t offset, size_t length, DataSink sink)>; |
| 187 | 200 | ||
| 188 | typedef std::function<void()> Done; | 201 | using ContentProviderWithCloser = std::function<void(size_t offset, size_t length, DataSink sink, Done done)>; |
| 189 | 202 | ||
| 190 | typedef std::function<void(size_t offset, size_t length, DataSink sink, | 203 | using ContentReceiver = std::function<bool(const char *data, size_t data_length)>; |
| 191 | Done done)> | ||
| 192 | ContentProvider; | ||
| 193 | 204 | ||
| 194 | typedef std::function<bool(const char *data, size_t data_length, size_t offset, | 205 | using ContentReader = std::function<bool(ContentReceiver receiver)>; |
| 195 | uint64_t content_length)> | ||
| 196 | ContentReceiver; | ||
| 197 | 206 | ||
| 198 | typedef std::function<bool(uint64_t current, uint64_t total)> Progress; | 207 | using Progress = std::function<bool(uint64_t current, uint64_t total)>; |
| 199 | 208 | ||
| 200 | struct Response; | 209 | struct Response; |
| 201 | typedef std::function<bool(const Response &response)> ResponseHandler; | 210 | using ResponseHandler = std::function<bool(const Response &response)>; |
| 202 | 211 | ||
| 203 | struct MultipartFile { | 212 | struct 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 | }; |
| 209 | typedef std::multimap<std::string, MultipartFile> MultipartFiles; | 218 | using MultipartFiles = std::multimap<std::string, MultipartFile>; |
| 210 | 219 | ||
| 211 | struct MultipartFormData { | 220 | struct 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 | }; |
| 217 | typedef std::vector<MultipartFormData> MultipartFormDataItems; | 226 | using MultipartFormDataItems = std::vector<MultipartFormData>; |
| 218 | 227 | ||
| 219 | typedef std::pair<ssize_t, ssize_t> Range; | 228 | using Range = std::pair<ssize_t, ssize_t>; |
| 220 | typedef std::vector<Range> Ranges; | 229 | using Ranges = std::vector<Range>; |
| 221 | 230 | ||
| 222 | struct Request { | 231 | struct 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 | ||
| 260 | struct Response { | 273 | struct 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 | ||
| 298 | class Stream { | 312 | class Stream { |
| 299 | public: | 313 | public: |
| 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 | ||
| 311 | class SocketStream : public Stream { | 325 | class SocketStream : public Stream { |
| 312 | public: | 326 | public: |
| 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 | ||
| 322 | private: | 337 | private: |
| 323 | socket_t sock_; | 338 | socket_t sock_; |
| 339 | time_t read_timeout_sec_; | ||
| 340 | time_t read_timeout_usec_; | ||
| 324 | }; | 341 | }; |
| 325 | 342 | ||
| 326 | class BufferStream : public Stream { | 343 | class BufferStream : public Stream { |
| 327 | public: | 344 | public: |
| 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 | ||
| 343 | class TaskQueue { | 360 | class TaskQueue { |
| 344 | public: | 361 | public: |
| 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 |
| 352 | class ThreadPool : public TaskQueue { | 369 | class ThreadPool : public TaskQueue { |
| 353 | public: | 370 | public: |
| 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 | ||
| 386 | private: | 402 | private: |
| 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 |
| 423 | class Threads : public TaskQueue { | 439 | class Threads : public TaskQueue { |
| 424 | public: | 440 | public: |
| 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 | ||
| 473 | class NoThread : public TaskQueue { | ||
| 474 | public: | ||
| 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 | ||
| 458 | class Server { | 487 | class Server { |
| 459 | public: | 488 | public: |
| 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: | |||
| 496 | protected: | 531 | protected: |
| 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 | ||
| 504 | private: | 541 | private: |
| 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 | ||
| 539 | class Client { | 588 | class Client { |
| 540 | public: | 589 | public: |
| 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 ¶ms); | 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 ¶ms); | 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 ¶ms, | ||
| 652 | bool compress = false); | ||
| 653 | |||
| 654 | std::shared_ptr<Response> Post(const char *path, const Headers &headers, | ||
| 655 | const Params ¶ms, 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 | ||
| 649 | private: | 743 | private: |
| @@ -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 |
| 695 | class SSLSocketStream : public Stream { | 796 | class SSLSocketStream : public Stream { |
| 696 | public: | 797 | public: |
| 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: | |||
| 706 | private: | 808 | private: |
| 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 | ||
| 711 | class SSLServer : public Server { | 815 | class 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 | ||
| 746 | private: | 850 | private: |
| 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 | |||
| 772 | namespace detail { | 877 | namespace detail { |
| 773 | 878 | ||
| 774 | inline bool is_hex(char c, int &v) { | 879 | inline bool is_hex(char c, int &v) { |
| @@ -932,8 +1037,8 @@ inline void read_file(const std::string &path, std::string &out) { | |||
| 932 | 1037 | ||
| 933 | inline std::string file_extension(const std::string &path) { | 1038 | inline 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 | ||
| 1095 | template <typename T> | 1201 | template <typename T> |
| 1096 | inline bool process_and_close_socket(bool is_client_request, socket_t sock, | 1202 | inline 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 | ||
| 1372 | private: | 1481 | private: |
| @@ -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 | ||
| 1421 | typedef std::function<bool(const char *data, size_t data_length)> | ||
| 1422 | ContentReceiverCore; | ||
| 1423 | |||
| 1424 | inline bool read_content_with_length(Stream &strm, uint64_t len, | 1530 | inline 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 | ||
| 1458 | inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) { | 1563 | inline 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 | ||
| 1473 | inline bool read_content_chunked(Stream &strm, ContentReceiverCore out) { | 1578 | inline 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 | ||
| 1510 | template <typename T> | 1616 | template <typename T> |
| 1511 | bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, | 1617 | bool 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 | ||
| 1583 | inline ssize_t write_content(Stream &strm, ContentProvider content_provider, | 1689 | inline 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 | ||
| 1601 | inline ssize_t write_content_chunked(Stream &strm, | 1708 | inline ssize_t |
| 1602 | ContentProvider content_provider) { | 1709 | write_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 | ||
| 1812 | inline bool parse_range_header(const std::string &s, Ranges &ranges) { | 1920 | inline 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( | |||
| 2132 | inline void Response::set_chunked_content_provider( | 2241 | inline 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 |
| 2142 | template <typename... Args> | 2251 | template <typename... Args> |
| 2143 | inline int Stream::write_format(const char *fmt, const Args &... args) { | 2252 | inline 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 |
| 2173 | inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} | 2281 | inline 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 | ||
| 2175 | inline SocketStream::~SocketStream() {} | 2286 | inline SocketStream::~SocketStream() {} |
| 2176 | 2287 | ||
| 2177 | inline int SocketStream::read(char *ptr, size_t size) { | 2288 | inline 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 |
| 2228 | inline Server::Server() | 2338 | inline 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 | ||
| 2370 | inline 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 | |||
| 2256 | inline Server &Server::Put(const char *pattern, Handler handler) { | 2377 | inline 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 | ||
| 2382 | inline 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 | |||
| 2261 | inline Server &Server::Patch(const char *pattern, Handler handler) { | 2389 | inline 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 | ||
| 2394 | inline 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 | |||
| 2266 | inline Server &Server::Delete(const char *pattern, Handler handler) { | 2401 | inline 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 | ||
| 2284 | inline void Server::set_file_request_handler(Handler handler) { | 2419 | inline void Server::set_file_request_handler(Handler handler) { |
| 2285 | file_request_handler_ = handler; | 2420 | file_request_handler_ = std::move(handler); |
| 2286 | } | 2421 | } |
| 2287 | 2422 | ||
| 2288 | inline void Server::set_error_handler(Handler handler) { | 2423 | inline void Server::set_error_handler(Handler handler) { |
| 2289 | error_handler_ = handler; | 2424 | error_handler_ = std::move(handler); |
| 2290 | } | 2425 | } |
| 2291 | 2426 | ||
| 2292 | inline void Server::set_logger(Logger logger) { logger_ = logger; } | 2427 | inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } |
| 2293 | 2428 | ||
| 2294 | inline void Server::set_keep_alive_max_count(size_t count) { | 2429 | inline 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 | ||
| 2433 | inline void Server::set_read_timeout(time_t sec, time_t usec) { | ||
| 2434 | read_timeout_sec_ = sec; | ||
| 2435 | read_timeout_usec_ = usec; | ||
| 2436 | } | ||
| 2437 | |||
| 2298 | inline void Server::set_payload_max_length(size_t length) { | 2438 | inline void Server::set_payload_max_length(size_t length) { |
| 2299 | payload_max_length_ = length; | 2439 | payload_max_length_ = length; |
| 2300 | } | 2440 | } |
| 2301 | 2441 | ||
| 2442 | inline 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 | } | ||
| 2302 | inline int Server::bind_to_any_port(const char *host, int socket_flags) { | 2446 | inline 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) { | |||
| 2306 | inline bool Server::listen_after_bind() { return listen_internal(); } | 2450 | inline bool Server::listen_after_bind() { return listen_internal(); } |
| 2307 | 2451 | ||
| 2308 | inline bool Server::listen(const char *host, int port, int socket_flags) { | 2452 | inline 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 | ||
| 2313 | inline bool Server::is_running() const { return is_running_; } | 2456 | inline bool Server::is_running() const { return is_running_; } |
| @@ -2322,8 +2465,9 @@ inline void Server::stop() { | |||
| 2322 | } | 2465 | } |
| 2323 | 2466 | ||
| 2324 | inline bool Server::parse_request_line(const char *s, Request &req) { | 2467 | inline 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 | |||
| 2468 | Server::write_content_with_provider(Stream &strm, const Request &req, | 2612 | Server::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 | ||
| 2644 | inline 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 | |||
| 2673 | inline bool | ||
| 2674 | Server::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 | |||
| 2500 | inline bool Server::handle_file_request(Request &req, Response &res) { | 2686 | inline 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 | ||
| 2608 | inline bool Server::routing(Request &req, Response &res) { | 2793 | inline 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 | ||
| 2643 | inline bool | 2861 | inline bool |
| 2862 | Server::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 | |||
| 2877 | inline bool | ||
| 2644 | Server::process_request(Stream &strm, bool last_connection, | 2878 | Server::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 | ||
| 2733 | inline bool Server::process_and_close_socket(socket_t sock) { | 2940 | inline 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 | ||
| 2749 | inline Client::~Client() {} | 2958 | inline Client::~Client() {} |
| @@ -2770,17 +2979,16 @@ inline socket_t Client::create_client_socket() const { | |||
| 2770 | } | 2979 | } |
| 2771 | 2980 | ||
| 2772 | inline bool Client::read_response_line(Stream &strm, Response &res) { | 2981 | inline 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 | |||
| 3170 | inline 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 | ||
| 2944 | inline bool Client::process_request(Stream &strm, const Request &req, | 3218 | inline 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 | ||
| 3003 | inline bool Client::is_ssl() const { return false; } | 3274 | inline bool Client::is_ssl() const { return false; } |
| @@ -3009,7 +3280,7 @@ inline std::shared_ptr<Response> Client::Get(const char *path) { | |||
| 3009 | 3280 | ||
| 3010 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3281 | inline 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 | ||
| 3015 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3286 | inline 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) { | |||
| 3033 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3304 | inline 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 | ||
| 3039 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3310 | inline 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 | ||
| 3045 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3316 | inline 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 | ||
| 3052 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3323 | inline 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 | ||
| 3059 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3330 | inline 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 | ||
| 3067 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3338 | inline 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 | ||
| 3100 | inline std::shared_ptr<Response> Client::Post(const char *path, | 3371 | inline 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 | ||
| 3106 | inline std::shared_ptr<Response> Client::Post(const char *path, | 3378 | inline std::shared_ptr<Response> |
| 3107 | const Headers &headers, | 3379 | Client::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; | 3385 | inline std::shared_ptr<Response> |
| 3386 | Client::Post(const char *path, const Params ¶ms, bool compress) { | ||
| 3387 | return Post(path, Headers(), params, compress); | ||
| 3121 | } | 3388 | } |
| 3122 | 3389 | ||
| 3123 | inline std::shared_ptr<Response> Client::Post(const char *path, | 3390 | inline std::shared_ptr<Response> Client::Post(const char *path, |
| 3124 | const Params ¶ms) { | 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 | ||
| 3128 | inline std::shared_ptr<Response> | 3399 | inline std::shared_ptr<Response> |
| 3129 | Client::Post(const char *path, const Headers &headers, const Params ¶ms) { | 3400 | Client::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 | |||
| 3408 | inline std::shared_ptr<Response> Client::Post(const char *path, | ||
| 3409 | const Headers &headers, | ||
| 3410 | const Params ¶ms, | ||
| 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 ¶ms) { | |||
| 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 | ||
| 3141 | inline std::shared_ptr<Response> | 3424 | inline std::shared_ptr<Response> |
| 3142 | Client::Post(const char *path, const MultipartFormDataItems &items) { | 3425 | Client::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 | ||
| 3146 | inline std::shared_ptr<Response> | 3430 | inline std::shared_ptr<Response> |
| 3147 | Client::Post(const char *path, const Headers &headers, | 3431 | Client::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 | ||
| 3180 | inline std::shared_ptr<Response> Client::Put(const char *path, | 3457 | inline 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 | ||
| 3186 | inline std::shared_ptr<Response> Client::Put(const char *path, | 3464 | inline std::shared_ptr<Response> |
| 3187 | const Headers &headers, | 3465 | Client::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; | 3471 | inline 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 | ||
| 3203 | inline std::shared_ptr<Response> Client::Patch(const char *path, | 3480 | inline std::shared_ptr<Response> |
| 3204 | const std::string &body, | 3481 | Client::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 | ||
| 3209 | inline std::shared_ptr<Response> Client::Patch(const char *path, | 3489 | inline 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); | 3496 | inline std::shared_ptr<Response> |
| 3219 | req.body = body; | 3497 | Client::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>(); | 3503 | inline 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; | 3512 | inline std::shared_ptr<Response> |
| 3513 | Client::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 | ||
| 3226 | inline std::shared_ptr<Response> Client::Delete(const char *path) { | 3521 | inline 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 | ||
| 3573 | inline void Client::set_read_timeout(time_t sec, time_t usec) { | ||
| 3574 | read_timeout_sec_ = sec; | ||
| 3575 | read_timeout_usec_ = usec; | ||
| 3576 | } | ||
| 3577 | |||
| 3278 | inline void Client::follow_location(bool on) { follow_location_ = on; } | 3578 | inline 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; } | |||
| 3284 | namespace detail { | 3584 | namespace detail { |
| 3285 | 3585 | ||
| 3286 | template <typename U, typename V, typename T> | 3586 | template <typename U, typename V, typename T> |
| 3287 | inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, | 3587 | inline 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: | |||
| 3382 | class SSLInit { | 3681 | class SSLInit { |
| 3383 | public: | 3682 | public: |
| 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 | ||
| 3391 | private: | 3699 | private: |
| 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 |
| 3402 | inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) | 3710 | inline 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 | ||
| 3405 | inline SSLSocketStream::~SSLSocketStream() {} | 3716 | inline SSLSocketStream::~SSLSocketStream() {} |
| 3406 | 3717 | ||
| 3407 | inline int SSLSocketStream::read(char *ptr, size_t size) { | 3718 | inline 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 | ||
| 3477 | inline bool SSLServer::process_and_close_socket(socket_t sock) { | 3787 | inline 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 | ||
| 3530 | inline SSL_CTX* SSLClient::ssl_context() const noexcept { | 3840 | inline SSL_CTX *SSLClient::ssl_context() const noexcept { return ctx_; } |
| 3531 | return ctx_; | ||
| 3532 | } | ||
| 3533 | 3841 | ||
| 3534 | inline bool SSLClient::process_and_close_socket( | 3842 | inline 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); |