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