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