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