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