1//
2// httplib.h
3//
4// Copyright (c) 2019 Yuji Hirose. All rights reserved.
5// MIT License
6//
7
8#ifndef CPPHTTPLIB_HTTPLIB_H
9#define CPPHTTPLIB_HTTPLIB_H
10
11/*
12 * Configuration
13 */
14
15#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
16#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
17#endif
18
19#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
20#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
21#endif
22
23#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
24#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
25#endif
26
27#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
28#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
29#endif
30
31#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
32#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
33#endif
34
35#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
36#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
37#endif
38
39#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
40#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
41#endif
42
43#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
44#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max)()
45#endif
46
47#ifndef CPPHTTPLIB_RECV_BUFSIZ
48#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
49#endif
50
51#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
52#define CPPHTTPLIB_THREAD_POOL_COUNT 8
53#endif
54
55/*
56 * Headers
57 */
58
59#ifdef _WIN32
60#ifndef _CRT_SECURE_NO_WARNINGS
61#define _CRT_SECURE_NO_WARNINGS
62#endif //_CRT_SECURE_NO_WARNINGS
63
64#ifndef _CRT_NONSTDC_NO_DEPRECATE
65#define _CRT_NONSTDC_NO_DEPRECATE
66#endif //_CRT_NONSTDC_NO_DEPRECATE
67
68#if defined(_MSC_VER)
69#ifdef _WIN64
70typedef __int64 ssize_t;
71#else
72typedef int ssize_t;
73#endif
74
75#if _MSC_VER < 1900
76#define snprintf _snprintf_s
77#endif
78#endif // _MSC_VER
79
80#ifndef S_ISREG
81#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
82#endif // S_ISREG
83
84#ifndef S_ISDIR
85#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
86#endif // S_ISDIR
87
88#ifndef NOMINMAX
89#define NOMINMAX
90#endif // NOMINMAX
91
92#include <io.h>
93#include <winsock2.h>
94#include <ws2tcpip.h>
95
96#ifndef WSA_FLAG_NO_HANDLE_INHERIT
97#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
98#endif
99
100#ifdef _MSC_VER
101#pragma comment(lib, "ws2_32.lib")
102#endif
103
104#ifndef strcasecmp
105#define strcasecmp _stricmp
106#endif // strcasecmp
107
108typedef SOCKET socket_t;
109#ifdef CPPHTTPLIB_USE_POLL
110#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
111#endif
112
113#else // not _WIN32
114
115#include <arpa/inet.h>
116#include <cstring>
117#include <netdb.h>
118#include <netinet/in.h>
119#ifdef CPPHTTPLIB_USE_POLL
120#include <poll.h>
121#endif
122#include <pthread.h>
123#include <csignal>
124#include <sys/select.h>
125#include <sys/socket.h>
126#include <unistd.h>
127
128typedef int socket_t;
129#define INVALID_SOCKET (-1)
130#endif //_WIN32
131
132#include <cassert>
133#include <atomic>
134#include <condition_variable>
135#include <errno.h>
136#include <fcntl.h>
137#include <fstream>
138#include <functional>
139#include <list>
140#include <map>
141#include <memory>
142#include <mutex>
143#include <random>
144#include <regex>
145#include <string>
146#include <sys/stat.h>
147#include <thread>
148#include <array>
149
150#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
151#include <openssl/err.h>
152#include <openssl/ssl.h>
153#include <openssl/x509v3.h>
154
155// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
156// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
157// #endif
158
159#if OPENSSL_VERSION_NUMBER < 0x10100000L
160#include <openssl/crypto.h>
161inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
162 return M_ASN1_STRING_data(asn1);
163}
164#endif
165#endif
166
167#ifdef CPPHTTPLIB_ZLIB_SUPPORT
168#include <zlib.h>
169#endif
170
171/*
172 * Declaration
173 */
174namespace httplib {
175
176namespace detail {
177
178struct ci {
179 bool operator()(const std::string &s1, const std::string &s2) const {
180 return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(),
181 [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); });
182 }
183};
184
185} // namespace detail
186
187enum class HttpVersion { v1_0 = 0, v1_1 };
188
189using Headers = std::multimap<std::string, std::string, detail::ci>;
190
191using Params = std::multimap<std::string, std::string>;
192using Match = std::smatch;
193
194using DataSink = std::function<void(const char *data, size_t data_len)>;
195
196using Done = std::function<void()>;
197
198using ContentProvider = std::function<void(size_t offset, size_t length, DataSink sink)>;
199
200using ContentProviderWithCloser = std::function<void(size_t offset, size_t length, DataSink sink, Done done)>;
201
202using ContentReceiver = std::function<bool(const char *data, size_t data_length)>;
203
204using ContentReader = std::function<bool(ContentReceiver receiver)>;
205
206using Progress = std::function<bool(uint64_t current, uint64_t total)>;
207
208struct Response;
209using ResponseHandler = std::function<bool(const Response &response)>;
210
211struct MultipartFile {
212 std::string filename;
213 std::string content_type;
214 size_t offset = 0;
215 size_t length = 0;
216};
217using MultipartFiles = std::multimap<std::string, MultipartFile>;
218
219struct MultipartFormData {
220 std::string name;
221 std::string content;
222 std::string filename;
223 std::string content_type;
224};
225using MultipartFormDataItems = std::vector<MultipartFormData>;
226
227using Range = std::pair<ssize_t, ssize_t>;
228using Ranges = std::vector<Range>;
229
230struct Request {
231 std::string method;
232 std::string path;
233 Headers headers;
234 std::string body;
235
236 // for server
237 std::string version;
238 std::string target;
239 Params params;
240 MultipartFiles files;
241 Ranges ranges;
242 Match matches;
243
244 // for client
245 size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
246 ResponseHandler response_handler;
247 ContentReceiver content_receiver;
248 Progress progress;
249
250#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
251 const SSL *ssl;
252#endif
253
254 bool has_header(const char *key) const;
255 std::string get_header_value(const char *key, size_t id = 0) const;
256 size_t get_header_value_count(const char *key) const;
257 void set_header(const char *key, const char *val);
258 void set_header(const char *key, const std::string &val);
259
260 bool has_param(const char *key) const;
261 std::string get_param_value(const char *key, size_t id = 0) const;
262 size_t get_param_value_count(const char *key) const;
263
264 bool has_file(const char *key) const;
265 MultipartFile get_file_value(const char *key) const;
266
267 // private members...
268 size_t content_length;
269 ContentProvider content_provider;
270};
271
272struct Response {
273 std::string version;
274 int status;
275 Headers headers;
276 std::string body;
277
278 bool has_header(const char *key) const;
279 std::string get_header_value(const char *key, size_t id = 0) const;
280 size_t get_header_value_count(const char *key) const;
281 void set_header(const char *key, const char *val);
282 void set_header(const char *key, const std::string &val);
283
284 void set_redirect(const char *url);
285 void set_content(const char *s, size_t n, const char *content_type);
286 void set_content(const std::string &s, const char *content_type);
287
288 void set_content_provider(size_t length, std::function<void(size_t offset, size_t length, DataSink sink)> provider,
289 std::function<void()> resource_releaser = [] {});
290
291 void set_chunked_content_provider(std::function<void(size_t offset, DataSink sink, Done done)> provider,
292 std::function<void()> resource_releaser = [] {});
293
294 Response() : status(-1), content_length(0) {
295 }
296
297 ~Response() {
298 if (content_provider_resource_releaser) {
299 content_provider_resource_releaser();
300 }
301 }
302
303 // private members...
304 size_t content_length;
305 ContentProviderWithCloser content_provider;
306 std::function<void()> content_provider_resource_releaser;
307};
308
309class Stream {
310public:
311 virtual ~Stream() = default;
312 virtual int read(char *ptr, size_t size) = 0;
313 virtual int write(const char *ptr, size_t size1) = 0;
314 virtual int write(const char *ptr) = 0;
315 virtual int write(const std::string &s) = 0;
316 virtual std::string get_remote_addr() const = 0;
317
318 template <typename... Args> int write_format(const char *fmt, const Args &... args);
319};
320
321class SocketStream : public Stream {
322public:
323 SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec);
324 ~SocketStream() override;
325
326 int read(char *ptr, size_t size) override;
327 int write(const char *ptr, size_t size) override;
328 int write(const char *ptr) override;
329 int write(const std::string &s) override;
330 std::string get_remote_addr() const override;
331
332private:
333 socket_t sock_;
334 time_t read_timeout_sec_;
335 time_t read_timeout_usec_;
336};
337
338class BufferStream : public Stream {
339public:
340 BufferStream() = default;
341 ~BufferStream() override = default;
342
343 int read(char *ptr, size_t size) override;
344 int write(const char *ptr, size_t size) override;
345 int write(const char *ptr) override;
346 int write(const std::string &s) override;
347 std::string get_remote_addr() const override;
348
349 const std::string &get_buffer() const;
350
351private:
352 std::string buffer;
353};
354
355class TaskQueue {
356public:
357 TaskQueue() = default;
358 virtual ~TaskQueue() = default;
359 virtual void enqueue(std::function<void()> fn) = 0;
360 virtual void shutdown() = 0;
361};
362
363#if CPPHTTPLIB_THREAD_POOL_COUNT > 0
364class ThreadPool : public TaskQueue {
365public:
366 explicit ThreadPool(size_t n) : shutdown_(false) {
367 while (n) {
368 threads_.emplace_back(worker(*this));
369 n--;
370 }
371 }
372
373 ThreadPool(const ThreadPool &) = delete;
374 ~ThreadPool() override = default;
375
376 void enqueue(std::function<void()> fn) override {
377 std::unique_lock<std::mutex> lock(mutex_);
378 jobs_.push_back(fn);
379 cond_.notify_one();
380 }
381
382 void shutdown() override {
383 // Stop all worker threads...
384 {
385 std::unique_lock<std::mutex> lock(mutex_);
386 shutdown_ = true;
387 }
388
389 cond_.notify_all();
390
391 // Join...
392 for (auto &t : threads_) {
393 t.join();
394 }
395 }
396
397private:
398 struct worker {
399 explicit worker(ThreadPool &pool) : pool_(pool) {
400 }
401
402 void operator()() {
403 for (;;) {
404 std::function<void()> fn;
405 {
406 std::unique_lock<std::mutex> lock(pool_.mutex_);
407
408 pool_.cond_.wait(lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
409
410 if (pool_.shutdown_ && pool_.jobs_.empty()) {
411 break;
412 }
413
414 fn = pool_.jobs_.front();
415 pool_.jobs_.pop_front();
416 }
417
418 assert(true == static_cast<bool>(fn));
419 fn();
420 }
421 }
422
423 ThreadPool &pool_;
424 };
425 friend struct worker;
426
427 std::vector<std::thread> threads_;
428 std::list<std::function<void()>> jobs_;
429
430 bool shutdown_;
431
432 std::condition_variable cond_;
433 std::mutex mutex_;
434};
435#else
436class Threads : public TaskQueue {
437public:
438 Threads() : running_threads_(0) {
439 }
440 virtual ~Threads() {
441 }
442
443 virtual void enqueue(std::function<void()> fn) override {
444 std::thread([=]() {
445 {
446 std::lock_guard<std::mutex> guard(running_threads_mutex_);
447 running_threads_++;
448 }
449
450 fn();
451
452 {
453 std::lock_guard<std::mutex> guard(running_threads_mutex_);
454 running_threads_--;
455 }
456 })
457 .detach();
458 }
459
460 virtual void shutdown() override {
461 for (;;) {
462 std::this_thread::sleep_for(std::chrono::milliseconds(10));
463 std::lock_guard<std::mutex> guard(running_threads_mutex_);
464 if (!running_threads_) {
465 break;
466 }
467 }
468 }
469
470private:
471 std::mutex running_threads_mutex_;
472 int running_threads_;
473};
474#endif
475
476class Server {
477public:
478 using Handler = std::function<void(const Request &, Response &)>;
479 using HandlerWithContentReader =
480 std::function<void(const Request &, Response &, const ContentReader &content_reader)>;
481 using Logger = std::function<void(const Request &, const Response &)>;
482
483 Server();
484
485 virtual ~Server();
486
487 virtual bool is_valid() const;
488
489 Server &Get(const char *pattern, Handler handler);
490 Server &Post(const char *pattern, Handler handler);
491 Server &Post(const char *pattern, HandlerWithContentReader handler);
492 Server &Put(const char *pattern, Handler handler);
493 Server &Put(const char *pattern, HandlerWithContentReader handler);
494 Server &Patch(const char *pattern, Handler handler);
495 Server &Patch(const char *pattern, HandlerWithContentReader handler);
496 Server &Delete(const char *pattern, Handler handler);
497 Server &Options(const char *pattern, Handler handler);
498
499 bool set_base_dir(const char *path);
500 void set_file_request_handler(Handler handler);
501
502 void set_error_handler(Handler handler);
503 void set_logger(Logger logger);
504
505 void set_keep_alive_max_count(size_t count);
506 void set_read_timeout(time_t sec, time_t usec);
507 void set_payload_max_length(size_t length);
508
509 bool bind_to_port(const char *host, int port, int socket_flags = 0);
510 int bind_to_any_port(const char *host, int socket_flags = 0);
511 bool listen_after_bind();
512
513 bool listen(const char *host, int port, int socket_flags = 0);
514
515 bool is_running() const;
516 void stop();
517
518 std::function<TaskQueue *(void)> new_task_queue;
519
520protected:
521 bool process_request(Stream &strm, bool last_connection, bool &connection_close,
522 const std::function<void(Request &)> &setup_request);
523
524 size_t keep_alive_max_count_;
525 time_t read_timeout_sec_;
526 time_t read_timeout_usec_;
527 size_t payload_max_length_;
528
529private:
530 typedef std::vector<std::pair<std::regex, Handler>> Handlers;
531 typedef std::vector<std::pair<std::regex, HandlerWithContentReader>> HandersForContentReader;
532
533 socket_t create_server_socket(const char *host, int port, int socket_flags) const;
534 int bind_internal(const char *host, int port, int socket_flags);
535 bool listen_internal();
536
537 bool routing(Request &req, Response &res, ContentReader content_reader);
538 bool handle_file_request(Request &req, Response &res);
539 bool dispatch_request(Request &req, Response &res, Handlers &handlers);
540 bool dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
541 HandersForContentReader &handlers);
542
543 bool parse_request_line(const char *s, Request &req);
544 bool write_response(Stream &strm, bool last_connection, const Request &req, Response &res);
545 bool write_content_with_provider(Stream &strm, const Request &req, Response &res, const std::string &boundary,
546 const std::string &content_type);
547 bool read_content(Stream &strm, bool last_connection, Request &req, Response &res);
548 bool read_content_with_content_receiver(Stream &strm, bool last_connection, Request &req, Response &res,
549 ContentReceiver reveiver);
550
551 virtual bool process_and_close_socket(socket_t sock);
552
553 std::atomic<bool> is_running_;
554 std::atomic<socket_t> svr_sock_;
555 std::string base_dir_;
556 Handler file_request_handler_;
557 Handlers get_handlers_;
558 Handlers post_handlers_;
559 HandersForContentReader post_handlers_for_content_reader;
560 Handlers put_handlers_;
561 HandersForContentReader put_handlers_for_content_reader;
562 Handlers patch_handlers_;
563 HandersForContentReader patch_handlers_for_content_reader;
564 Handlers delete_handlers_;
565 Handlers options_handlers_;
566 Handler error_handler_;
567 Logger logger_;
568};
569
570class Client {
571public:
572 explicit Client(const char *host, int port = 80, time_t timeout_sec = 300);
573
574 virtual ~Client();
575
576 virtual bool is_valid() const;
577
578 std::shared_ptr<Response> Get(const char *path);
579
580 std::shared_ptr<Response> Get(const char *path, const Headers &headers);
581
582 std::shared_ptr<Response> Get(const char *path, Progress progress);
583
584 std::shared_ptr<Response> Get(const char *path, const Headers &headers, Progress progress);
585
586 std::shared_ptr<Response> Get(const char *path, ContentReceiver content_receiver);
587
588 std::shared_ptr<Response> Get(const char *path, const Headers &headers, ContentReceiver content_receiver);
589
590 std::shared_ptr<Response> Get(const char *path, ContentReceiver content_receiver, Progress progress);
591
592 std::shared_ptr<Response> Get(const char *path, const Headers &headers, ContentReceiver content_receiver,
593 Progress progress);
594
595 std::shared_ptr<Response> Get(const char *path, const Headers &headers, ResponseHandler response_handler,
596 ContentReceiver content_receiver);
597
598 std::shared_ptr<Response> Get(const char *path, const Headers &headers, ResponseHandler response_handler,
599 ContentReceiver content_receiver, Progress progress);
600
601 std::shared_ptr<Response> Head(const char *path);
602
603 std::shared_ptr<Response> Head(const char *path, const Headers &headers);
604
605 std::shared_ptr<Response> Post(const char *path, const std::string &body, const char *content_type,
606 bool compress = false);
607
608 std::shared_ptr<Response> Post(const char *path, const Headers &headers, const std::string &body,
609 const char *content_type, bool compress = false);
610
611 std::shared_ptr<Response> Post(const char *path, size_t content_length, ContentProvider content_provider,
612 const char *content_type, bool compress = false);
613
614 std::shared_ptr<Response> Post(const char *path, const Headers &headers, size_t content_length,
615 ContentProvider content_provider, const char *content_type, bool compress = false);
616
617 std::shared_ptr<Response> Post(const char *path, const Params &params, bool compress = false);
618
619 std::shared_ptr<Response> Post(const char *path, const Headers &headers, const Params &params,
620 bool compress = false);
621
622 std::shared_ptr<Response> Post(const char *path, const MultipartFormDataItems &items, bool compress = false);
623
624 std::shared_ptr<Response> Post(const char *path, const Headers &headers, const MultipartFormDataItems &items,
625 bool compress = false);
626
627 std::shared_ptr<Response> Put(const char *path, const std::string &body, const char *content_type,
628 bool compress = false);
629
630 std::shared_ptr<Response> Put(const char *path, const Headers &headers, const std::string &body,
631 const char *content_type, bool compress = false);
632
633 std::shared_ptr<Response> Put(const char *path, size_t content_length, ContentProvider content_provider,
634 const char *content_type, bool compress = false);
635
636 std::shared_ptr<Response> Put(const char *path, const Headers &headers, size_t content_length,
637 ContentProvider content_provider, const char *content_type, bool compress = false);
638
639 std::shared_ptr<Response> Patch(const char *path, const std::string &body, const char *content_type,
640 bool compress = false);
641
642 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, const std::string &body,
643 const char *content_type, bool compress = false);
644
645 std::shared_ptr<Response> Patch(const char *path, size_t content_length, ContentProvider content_provider,
646 const char *content_type, bool compress = false);
647
648 std::shared_ptr<Response> Patch(const char *path, const Headers &headers, size_t content_length,
649 ContentProvider content_provider, const char *content_type, bool compress = false);
650
651 std::shared_ptr<Response> Delete(const char *path);
652
653 std::shared_ptr<Response> Delete(const char *path, const std::string &body, const char *content_type);
654
655 std::shared_ptr<Response> Delete(const char *path, const Headers &headers);
656
657 std::shared_ptr<Response> Delete(const char *path, const Headers &headers, const std::string &body,
658 const char *content_type);
659
660 std::shared_ptr<Response> Options(const char *path);
661
662 std::shared_ptr<Response> Options(const char *path, const Headers &headers);
663
664 bool send(const Request &req, Response &res);
665
666 bool send(const std::vector<Request> &requests, std::vector<Response> &responses);
667
668 void set_keep_alive_max_count(size_t count);
669 void set_read_timeout(time_t sec, time_t usec);
670
671 void follow_location(bool on);
672
673protected:
674 bool process_request(Stream &strm, const Request &req, Response &res, bool last_connection, bool &connection_close);
675
676 const std::string host_;
677 const int port_;
678 time_t timeout_sec_;
679 const std::string host_and_port_;
680 size_t keep_alive_max_count_;
681 time_t read_timeout_sec_;
682 time_t read_timeout_usec_;
683 size_t follow_location_;
684
685private:
686 socket_t create_client_socket() const;
687 bool read_response_line(Stream &strm, Response &res);
688 void write_request(Stream &strm, const Request &req, bool last_connection);
689 bool redirect(const Request &req, Response &res);
690
691 std::shared_ptr<Response> send_with_content_provider(const char *method, const char *path, const Headers &headers,
692 const std::string &body, size_t content_length,
693 ContentProvider content_provider, const char *content_type,
694 bool compress);
695
696 virtual bool
697 process_and_close_socket(socket_t sock, size_t request_count,
698 std::function<bool(Stream &strm, bool last_connection, bool &connection_close)> callback);
699
700 virtual bool is_ssl() const;
701};
702
703inline void Get(std::vector<Request> &requests, const char *path, const Headers &headers) {
704 Request req;
705 req.method = "GET";
706 req.path = path;
707 req.headers = headers;
708 requests.emplace_back(std::move(req));
709}
710
711inline void Get(std::vector<Request> &requests, const char *path) {
712 Get(requests, path, Headers());
713}
714
715inline void Post(std::vector<Request> &requests, const char *path, const Headers &headers, const std::string &body,
716 const char *content_type) {
717 Request req;
718 req.method = "POST";
719 req.path = path;
720 req.headers = headers;
721 req.headers.emplace("Content-Type", content_type);
722 req.body = body;
723 requests.emplace_back(std::move(req));
724}
725
726inline void Post(std::vector<Request> &requests, const char *path, const std::string &body, const char *content_type) {
727 Post(requests, path, Headers(), body, content_type);
728}
729
730#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
731class SSLSocketStream : public Stream {
732public:
733 SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec);
734 virtual ~SSLSocketStream();
735
736 virtual int read(char *ptr, size_t size);
737 virtual int write(const char *ptr, size_t size);
738 virtual int write(const char *ptr);
739 virtual int write(const std::string &s);
740 virtual std::string get_remote_addr() const;
741
742private:
743 socket_t sock_;
744 SSL *ssl_;
745 time_t read_timeout_sec_;
746 time_t read_timeout_usec_;
747};
748
749class SSLServer : public Server {
750public:
751 SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path = nullptr,
752 const char *client_ca_cert_dir_path = nullptr);
753
754 virtual ~SSLServer();
755
756 virtual bool is_valid() const;
757
758private:
759 virtual bool process_and_close_socket(socket_t sock);
760
761 SSL_CTX *ctx_;
762 std::mutex ctx_mutex_;
763};
764
765class SSLClient : public Client {
766public:
767 SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, const char *client_cert_path = nullptr,
768 const char *client_key_path = nullptr);
769
770 virtual ~SSLClient();
771
772 virtual bool is_valid() const;
773
774 void set_ca_cert_path(const char *ca_ceert_file_path, const char *ca_cert_dir_path = nullptr);
775 void enable_server_certificate_verification(bool enabled);
776
777 long get_openssl_verify_result() const;
778
779 SSL_CTX *ssl_context() const noexcept;
780
781private:
782 virtual bool
783 process_and_close_socket(socket_t sock, size_t request_count,
784 std::function<bool(Stream &strm, bool last_connection, bool &connection_close)> callback);
785 virtual bool is_ssl() const;
786
787 bool verify_host(X509 *server_cert) const;
788 bool verify_host_with_subject_alt_name(X509 *server_cert) const;
789 bool verify_host_with_common_name(X509 *server_cert) const;
790 bool check_host_name(const char *pattern, size_t pattern_len) const;
791
792 SSL_CTX *ctx_;
793 std::mutex ctx_mutex_;
794 std::vector<std::string> host_components_;
795 std::string ca_cert_file_path_;
796 std::string ca_cert_dir_path_;
797 bool server_certificate_verification_ = false;
798 long verify_result_ = 0;
799};
800#endif
801
802/*
803 * Implementation
804 */
805
806namespace detail {
807
808inline bool is_hex(char c, int &v) {
809 if (0x20 <= c && isdigit(c)) {
810 v = c - '0';
811 return true;
812 } else if ('A' <= c && c <= 'F') {
813 v = c - 'A' + 10;
814 return true;
815 } else if ('a' <= c && c <= 'f') {
816 v = c - 'a' + 10;
817 return true;
818 }
819 return false;
820}
821
822inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) {
823 if (i >= s.size()) {
824 return false;
825 }
826
827 val = 0;
828 for (; cnt; i++, cnt--) {
829 if (!s[i]) {
830 return false;
831 }
832 int v = 0;
833 if (is_hex(s[i], v)) {
834 val = val * 16 + v;
835 } else {
836 return false;
837 }
838 }
839 return true;
840}
841
842inline std::string from_i_to_hex(size_t n) {
843 const char *charset = "0123456789abcdef";
844 std::string ret;
845 do {
846 ret = charset[n & 15] + ret;
847 n >>= 4;
848 } while (n > 0);
849 return ret;
850}
851
852inline size_t to_utf8(int code, char *buff) {
853 if (code < 0x0080) {
854 buff[0] = (code & 0x7F);
855 return 1;
856 } else if (code < 0x0800) {
857 buff[0] = (0xC0 | ((code >> 6) & 0x1F));
858 buff[1] = (0x80 | (code & 0x3F));
859 return 2;
860 } else if (code < 0xD800) {
861 buff[0] = (0xE0 | ((code >> 12) & 0xF));
862 buff[1] = (0x80 | ((code >> 6) & 0x3F));
863 buff[2] = (0x80 | (code & 0x3F));
864 return 3;
865 } else if (code < 0xE000) { // D800 - DFFF is invalid...
866 return 0;
867 } else if (code < 0x10000) {
868 buff[0] = (0xE0 | ((code >> 12) & 0xF));
869 buff[1] = (0x80 | ((code >> 6) & 0x3F));
870 buff[2] = (0x80 | (code & 0x3F));
871 return 3;
872 } else if (code < 0x110000) {
873 buff[0] = (0xF0 | ((code >> 18) & 0x7));
874 buff[1] = (0x80 | ((code >> 12) & 0x3F));
875 buff[2] = (0x80 | ((code >> 6) & 0x3F));
876 buff[3] = (0x80 | (code & 0x3F));
877 return 4;
878 }
879
880 // NOTREACHED
881 return 0;
882}
883
884// NOTE: This code came up with the following stackoverflow post:
885// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c
886inline std::string base64_encode(const std::string &in) {
887 static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
888
889 std::string out;
890 out.reserve(in.size());
891
892 int val = 0;
893 int valb = -6;
894
895 for (uint8_t c : in) {
896 val = (val << 8) + c;
897 valb += 8;
898 while (valb >= 0) {
899 out.push_back(lookup[(val >> valb) & 0x3F]);
900 valb -= 6;
901 }
902 }
903
904 if (valb > -6) {
905 out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]);
906 }
907
908 while (out.size() % 4) {
909 out.push_back('=');
910 }
911
912 return out;
913}
914
915inline bool is_file(const std::string &path) {
916 struct stat st;
917 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
918}
919
920inline bool is_dir(const std::string &path) {
921 struct stat st;
922 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
923}
924
925inline bool is_valid_path(const std::string &path) {
926 size_t level = 0;
927 size_t i = 0;
928
929 // Skip slash
930 while (i < path.size() && path[i] == '/') {
931 i++;
932 }
933
934 while (i < path.size()) {
935 // Read component
936 auto beg = i;
937 while (i < path.size() && path[i] != '/') {
938 i++;
939 }
940
941 auto len = i - beg;
942 assert(len > 0);
943
944 if (!path.compare(beg, len, ".")) {
945 ;
946 } else if (!path.compare(beg, len, "..")) {
947 if (level == 0) {
948 return false;
949 }
950 level--;
951 } else {
952 level++;
953 }
954
955 // Skip slash
956 while (i < path.size() && path[i] == '/') {
957 i++;
958 }
959 }
960
961 return true;
962}
963
964inline void read_file(const std::string &path, std::string &out) {
965 std::ifstream fs(path, std::ios_base::binary);
966 fs.seekg(0, std::ios_base::end);
967 auto size = fs.tellg();
968 fs.seekg(0);
969 out.resize(static_cast<size_t>(size));
970 fs.read(&out[0], size);
971}
972
973inline std::string file_extension(const std::string &path) {
974 std::smatch m;
975 auto re = std::regex("\\.([a-zA-Z0-9]+)$");
976 if (std::regex_search(path, m, re)) {
977 return m[1].str();
978 }
979 return std::string();
980}
981
982template <class Fn> void split(const char *b, const char *e, char d, Fn fn) {
983 int i = 0;
984 int beg = 0;
985
986 while (e ? (b + i != e) : (b[i] != '\0')) {
987 if (b[i] == d) {
988 fn(&b[beg], &b[i]);
989 beg = i + 1;
990 }
991 i++;
992 }
993
994 if (i) {
995 fn(&b[beg], &b[i]);
996 }
997}
998
999// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
1000// to store data. The call can set memory on stack for performance.
1001class stream_line_reader {
1002public:
1003 stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size)
1004 : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {
1005 }
1006
1007 const char *ptr() const {
1008 if (glowable_buffer_.empty()) {
1009 return fixed_buffer_;
1010 } else {
1011 return glowable_buffer_.data();
1012 }
1013 }
1014
1015 size_t size() const {
1016 if (glowable_buffer_.empty()) {
1017 return fixed_buffer_used_size_;
1018 } else {
1019 return glowable_buffer_.size();
1020 }
1021 }
1022
1023 bool getline() {
1024 fixed_buffer_used_size_ = 0;
1025 glowable_buffer_.clear();
1026
1027 for (size_t i = 0;; i++) {
1028 char byte;
1029 auto n = strm_.read(&byte, 1);
1030
1031 if (n < 0) {
1032 return false;
1033 } else if (n == 0) {
1034 if (i == 0) {
1035 return false;
1036 } else {
1037 break;
1038 }
1039 }
1040
1041 append(byte);
1042
1043 if (byte == '\n') {
1044 break;
1045 }
1046 }
1047
1048 return true;
1049 }
1050
1051private:
1052 void append(char c) {
1053 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
1054 fixed_buffer_[fixed_buffer_used_size_++] = c;
1055 fixed_buffer_[fixed_buffer_used_size_] = '\0';
1056 } else {
1057 if (glowable_buffer_.empty()) {
1058 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
1059 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
1060 }
1061 glowable_buffer_ += c;
1062 }
1063 }
1064
1065 Stream &strm_;
1066 char *fixed_buffer_;
1067 const size_t fixed_buffer_size_;
1068 size_t fixed_buffer_used_size_ = 0;
1069 std::string glowable_buffer_;
1070};
1071
1072inline int close_socket(socket_t sock) {
1073#ifdef _WIN32
1074 return closesocket(sock);
1075#else
1076 return close(sock);
1077#endif
1078}
1079
1080inline int select_read(socket_t sock, time_t sec, time_t usec) {
1081#ifdef CPPHTTPLIB_USE_POLL
1082 struct pollfd pfd_read;
1083 pfd_read.fd = sock;
1084 pfd_read.events = POLLIN;
1085
1086 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1087
1088 return poll(&pfd_read, 1, timeout);
1089#else
1090 fd_set fds;
1091 FD_ZERO(&fds);
1092 FD_SET(sock, &fds);
1093
1094 timeval tv;
1095 tv.tv_sec = static_cast<long>(sec);
1096 tv.tv_usec = static_cast<long>(usec);
1097
1098 return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
1099#endif
1100}
1101
1102inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
1103#ifdef CPPHTTPLIB_USE_POLL
1104 struct pollfd pfd_read;
1105 pfd_read.fd = sock;
1106 pfd_read.events = POLLIN | POLLOUT;
1107
1108 auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
1109
1110 if (poll(&pfd_read, 1, timeout) > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
1111 int error = 0;
1112 socklen_t len = sizeof(error);
1113 return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &len) >= 0 && !error;
1114 }
1115 return false;
1116#else
1117 fd_set fdsr;
1118 FD_ZERO(&fdsr);
1119 FD_SET(sock, &fdsr);
1120
1121 auto fdsw = fdsr;
1122 auto fdse = fdsr;
1123
1124 timeval tv;
1125 tv.tv_sec = static_cast<long>(sec);
1126 tv.tv_usec = static_cast<long>(usec);
1127
1128 if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
1129 (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
1130 int error = 0;
1131 socklen_t len = sizeof(error);
1132 return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&error), &len) >= 0 && !error;
1133 }
1134 return false;
1135#endif
1136}
1137
1138template <typename T>
1139inline bool process_and_close_socket(bool is_client_request, socket_t sock, size_t keep_alive_max_count,
1140 time_t read_timeout_sec, time_t read_timeout_usec, T callback) {
1141 assert(keep_alive_max_count > 0);
1142
1143 bool ret = false;
1144
1145 if (keep_alive_max_count > 1) {
1146 auto count = keep_alive_max_count;
1147 while (count > 0 && (is_client_request || detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
1148 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
1149 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1150 auto last_connection = count == 1;
1151 auto connection_close = false;
1152
1153 ret = callback(strm, last_connection, connection_close);
1154 if (!ret || connection_close) {
1155 break;
1156 }
1157
1158 count--;
1159 }
1160 } else {
1161 SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
1162 auto dummy_connection_close = false;
1163 ret = callback(strm, true, dummy_connection_close);
1164 }
1165
1166 close_socket(sock);
1167 return ret;
1168}
1169
1170inline int shutdown_socket(socket_t sock) {
1171#ifdef _WIN32
1172 return shutdown(sock, SD_BOTH);
1173#else
1174 return shutdown(sock, SHUT_RDWR);
1175#endif
1176}
1177
1178template <typename Fn> socket_t create_socket(const char *host, int port, Fn fn, int socket_flags = 0) {
1179#ifdef _WIN32
1180#define SO_SYNCHRONOUS_NONALERT 0x20
1181#define SO_OPENTYPE 0x7008
1182
1183 int opt = SO_SYNCHRONOUS_NONALERT;
1184 setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, sizeof(opt));
1185#endif
1186
1187 // Get address info
1188 struct addrinfo hints;
1189 struct addrinfo *result;
1190
1191 memset(&hints, 0, sizeof(struct addrinfo));
1192 hints.ai_family = AF_UNSPEC;
1193 hints.ai_socktype = SOCK_STREAM;
1194 hints.ai_flags = socket_flags;
1195 hints.ai_protocol = 0;
1196
1197 auto service = std::to_string(port);
1198
1199 if (getaddrinfo(host, service.c_str(), &hints, &result)) {
1200 return INVALID_SOCKET;
1201 }
1202
1203 for (auto rp = result; rp; rp = rp->ai_next) {
1204 // Create a socket
1205#ifdef _WIN32
1206 auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);
1207#else
1208 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1209#endif
1210 if (sock == INVALID_SOCKET) {
1211 continue;
1212 }
1213
1214#ifndef _WIN32
1215 if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
1216 continue;
1217 }
1218#endif
1219
1220 // Make 'reuse address' option available
1221 int yes = 1;
1222 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes), sizeof(yes));
1223#ifdef SO_REUSEPORT
1224 setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes), sizeof(yes));
1225#endif
1226
1227 // bind or connect
1228 if (fn(sock, *rp)) {
1229 freeaddrinfo(result);
1230 return sock;
1231 }
1232
1233 close_socket(sock);
1234 }
1235
1236 freeaddrinfo(result);
1237 return INVALID_SOCKET;
1238}
1239
1240inline void set_nonblocking(socket_t sock, bool nonblocking) {
1241#ifdef _WIN32
1242 auto flags = nonblocking ? 1UL : 0UL;
1243 ioctlsocket(sock, FIONBIO, &flags);
1244#else
1245 auto flags = fcntl(sock, F_GETFL, 0);
1246 fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
1247#endif
1248}
1249
1250inline bool is_connection_error() {
1251#ifdef _WIN32
1252 return WSAGetLastError() != WSAEWOULDBLOCK;
1253#else
1254 return errno != EINPROGRESS;
1255#endif
1256}
1257
1258inline std::string get_remote_addr(socket_t sock) {
1259 struct sockaddr_storage addr;
1260 socklen_t len = sizeof(addr);
1261
1262 if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) {
1263 std::array<char, NI_MAXHOST> ipstr{};
1264
1265 if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, ipstr.data(), ipstr.size(), nullptr, 0,
1266 NI_NUMERICHOST)) {
1267 return ipstr.data();
1268 }
1269 }
1270
1271 return std::string();
1272}
1273
1274inline const char *find_content_type(const std::string &path) {
1275 auto ext = file_extension(path);
1276 if (ext == "txt") {
1277 return "text/plain";
1278 } else if (ext == "html" || ext == "htm") {
1279 return "text/html";
1280 } else if (ext == "css") {
1281 return "text/css";
1282 } else if (ext == "jpeg" || ext == "jpg") {
1283 return "image/jpg";
1284 } else if (ext == "png") {
1285 return "image/png";
1286 } else if (ext == "gif") {
1287 return "image/gif";
1288 } else if (ext == "svg") {
1289 return "image/svg+xml";
1290 } else if (ext == "ico") {
1291 return "image/x-icon";
1292 } else if (ext == "json") {
1293 return "application/json";
1294 } else if (ext == "pdf") {
1295 return "application/pdf";
1296 } else if (ext == "js") {
1297 return "application/javascript";
1298 } else if (ext == "xml") {
1299 return "application/xml";
1300 } else if (ext == "xhtml") {
1301 return "application/xhtml+xml";
1302 }
1303 return nullptr;
1304}
1305
1306inline const char *status_message(int status) {
1307 switch (status) {
1308 case 200:
1309 return "OK";
1310 case 206:
1311 return "Partial Content";
1312 case 301:
1313 return "Moved Permanently";
1314 case 302:
1315 return "Found";
1316 case 303:
1317 return "See Other";
1318 case 304:
1319 return "Not Modified";
1320 case 400:
1321 return "Bad Request";
1322 case 403:
1323 return "Forbidden";
1324 case 404:
1325 return "Not Found";
1326 case 413:
1327 return "Payload Too Large";
1328 case 414:
1329 return "Request-URI Too Long";
1330 case 415:
1331 return "Unsupported Media Type";
1332 case 416:
1333 return "Range Not Satisfiable";
1334
1335 default:
1336 case 500:
1337 return "Internal Server Error";
1338 }
1339}
1340
1341#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1342inline bool can_compress(const std::string &content_type) {
1343 return !content_type.find("text/") || content_type == "image/svg+xml" || content_type == "application/javascript" ||
1344 content_type == "application/json" || content_type == "application/xml" ||
1345 content_type == "application/xhtml+xml";
1346}
1347
1348inline bool compress(std::string &content) {
1349 z_stream strm;
1350 strm.zalloc = Z_NULL;
1351 strm.zfree = Z_NULL;
1352 strm.opaque = Z_NULL;
1353
1354 auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
1355 if (ret != Z_OK) {
1356 return false;
1357 }
1358
1359 strm.avail_in = content.size();
1360 strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
1361
1362 std::string compressed;
1363
1364 std::array<char, 16384> buff{};
1365 do {
1366 strm.avail_out = buff.size();
1367 strm.next_out = reinterpret_cast<Bytef *>(buff.data());
1368 ret = deflate(&strm, Z_FINISH);
1369 assert(ret != Z_STREAM_ERROR);
1370 compressed.append(buff.data(), buff.size() - strm.avail_out);
1371 } while (strm.avail_out == 0);
1372
1373 assert(ret == Z_STREAM_END);
1374 assert(strm.avail_in == 0);
1375
1376 content.swap(compressed);
1377
1378 deflateEnd(&strm);
1379 return true;
1380}
1381
1382class decompressor {
1383public:
1384 decompressor() {
1385 strm.zalloc = Z_NULL;
1386 strm.zfree = Z_NULL;
1387 strm.opaque = Z_NULL;
1388
1389 // 15 is the value of wbits, which should be at the maximum possible value
1390 // to ensure that any gzip stream can be decoded. The offset of 16 specifies
1391 // that the stream to decompress will be formatted with a gzip wrapper.
1392 is_valid_ = inflateInit2(&strm, 16 + 15) == Z_OK;
1393 }
1394
1395 ~decompressor() {
1396 inflateEnd(&strm);
1397 }
1398
1399 bool is_valid() const {
1400 return is_valid_;
1401 }
1402
1403 template <typename T> bool decompress(const char *data, size_t data_length, T callback) {
1404 int ret = Z_OK;
1405
1406 strm.avail_in = data_length;
1407 strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
1408
1409 std::array<char, 16384> buff{};
1410 do {
1411 strm.avail_out = buff.size();
1412 strm.next_out = reinterpret_cast<Bytef *>(buff.data());
1413
1414 ret = inflate(&strm, Z_NO_FLUSH);
1415 assert(ret != Z_STREAM_ERROR);
1416 switch (ret) {
1417 case Z_NEED_DICT:
1418 case Z_DATA_ERROR:
1419 case Z_MEM_ERROR:
1420 inflateEnd(&strm);
1421 return false;
1422 }
1423
1424 if (!callback(buff.data(), buff.size() - strm.avail_out)) {
1425 return false;
1426 }
1427 } while (strm.avail_out == 0);
1428
1429 return ret == Z_STREAM_END;
1430 }
1431
1432private:
1433 bool is_valid_;
1434 z_stream strm;
1435};
1436#endif
1437
1438inline bool has_header(const Headers &headers, const char *key) {
1439 return headers.find(key) != headers.end();
1440}
1441
1442inline const char *get_header_value(const Headers &headers, const char *key, size_t id = 0, const char *def = nullptr) {
1443 auto it = headers.find(key);
1444 std::advance(it, id);
1445 if (it != headers.end()) {
1446 return it->second.c_str();
1447 }
1448 return def;
1449}
1450
1451inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, int def = 0) {
1452 auto it = headers.find(key);
1453 if (it != headers.end()) {
1454 return std::strtoull(it->second.data(), nullptr, 10);
1455 }
1456 return def;
1457}
1458
1459inline bool read_headers(Stream &strm, Headers &headers) {
1460 static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)");
1461
1462 const auto bufsiz = 2048;
1463 char buf[bufsiz];
1464
1465 stream_line_reader line_reader(strm, buf, bufsiz);
1466
1467 for (;;) {
1468 if (!line_reader.getline()) {
1469 return false;
1470 }
1471 if (!strcmp(line_reader.ptr(), "\r\n")) {
1472 break;
1473 }
1474 std::cmatch m;
1475 if (std::regex_match(line_reader.ptr(), m, re)) {
1476 auto key = std::string(m[1]);
1477 auto val = std::string(m[2]);
1478 headers.emplace(key, val);
1479 }
1480 }
1481
1482 return true;
1483}
1484
1485inline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiver out) {
1486 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1487
1488 uint64_t r = 0;
1489 while (r < len) {
1490 auto read_len = static_cast<size_t>(len - r);
1491 auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
1492 if (n <= 0) {
1493 return false;
1494 }
1495
1496 if (!out(buf, n)) {
1497 return false;
1498 }
1499
1500 r += n;
1501
1502 if (progress) {
1503 if (!progress(r, len)) {
1504 return false;
1505 }
1506 }
1507 }
1508
1509 return true;
1510}
1511
1512inline void skip_content_with_length(Stream &strm, uint64_t len) {
1513 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1514 uint64_t r = 0;
1515 while (r < len) {
1516 auto read_len = static_cast<size_t>(len - r);
1517 auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
1518 if (n <= 0) {
1519 return;
1520 }
1521 r += n;
1522 }
1523}
1524
1525inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
1526 char buf[CPPHTTPLIB_RECV_BUFSIZ];
1527 for (;;) {
1528 auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
1529 if (n < 0) {
1530 return false;
1531 } else if (n == 0) {
1532 return true;
1533 }
1534 if (!out(buf, n)) {
1535 return false;
1536 }
1537 }
1538
1539 return true;
1540}
1541
1542inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
1543 const auto bufsiz = 16;
1544 char buf[bufsiz];
1545
1546 stream_line_reader line_reader(strm, buf, bufsiz);
1547
1548 if (!line_reader.getline()) {
1549 return false;
1550 }
1551
1552 auto chunk_len = std::stoi(line_reader.ptr(), 0, 16);
1553
1554 while (chunk_len > 0) {
1555 if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
1556 return false;
1557 }
1558
1559 if (!line_reader.getline()) {
1560 return false;
1561 }
1562
1563 if (strcmp(line_reader.ptr(), "\r\n")) {
1564 break;
1565 }
1566
1567 if (!line_reader.getline()) {
1568 return false;
1569 }
1570
1571 chunk_len = std::stoi(line_reader.ptr(), 0, 16);
1572 }
1573
1574 if (chunk_len == 0) {
1575 // Reader terminator after chunks
1576 if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n"))
1577 return false;
1578 }
1579
1580 return true;
1581}
1582
1583inline bool is_chunked_transfer_encoding(const Headers &headers) {
1584 return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked");
1585}
1586
1587template <typename T>
1588bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, Progress progress,
1589 ContentReceiver receiver) {
1590
1591 ContentReceiver out = [&](const char *buf, size_t n) { return receiver(buf, n); };
1592
1593#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1594 detail::decompressor decompressor;
1595
1596 if (!decompressor.is_valid()) {
1597 status = 500;
1598 return false;
1599 }
1600
1601 if (x.get_header_value("Content-Encoding") == "gzip") {
1602 out = [&](const char *buf, size_t n) {
1603 return decompressor.decompress(buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); });
1604 };
1605 }
1606#else
1607 if (x.get_header_value("Content-Encoding") == "gzip") {
1608 status = 415;
1609 return false;
1610 }
1611#endif
1612
1613 auto ret = true;
1614 auto exceed_payload_max_length = false;
1615
1616 if (is_chunked_transfer_encoding(x.headers)) {
1617 ret = read_content_chunked(strm, out);
1618 } else if (!has_header(x.headers, "Content-Length")) {
1619 ret = read_content_without_length(strm, out);
1620 } else {
1621 auto len = get_header_value_uint64(x.headers, "Content-Length", 0);
1622 if (len > payload_max_length) {
1623 exceed_payload_max_length = true;
1624 skip_content_with_length(strm, len);
1625 ret = false;
1626 } else if (len > 0) {
1627 ret = read_content_with_length(strm, len, progress, out);
1628 }
1629 }
1630
1631 if (!ret) {
1632 status = exceed_payload_max_length ? 413 : 400;
1633 }
1634
1635 return ret;
1636}
1637
1638template <typename T> inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
1639 auto write_len = 0;
1640 for (const auto &x : info.headers) {
1641 auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
1642 if (len < 0) {
1643 return len;
1644 }
1645 write_len += len;
1646 }
1647 for (const auto &x : headers) {
1648 auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
1649 if (len < 0) {
1650 return len;
1651 }
1652 write_len += len;
1653 }
1654 auto len = strm.write("\r\n");
1655 if (len < 0) {
1656 return len;
1657 }
1658 write_len += len;
1659 return write_len;
1660}
1661
1662inline ssize_t write_content(Stream &strm, ContentProviderWithCloser content_provider, size_t offset, size_t length) {
1663 size_t begin_offset = offset;
1664 size_t end_offset = offset + length;
1665 while (offset < end_offset) {
1666 ssize_t written_length = 0;
1667 content_provider(offset, end_offset - offset,
1668 [&](const char *d, size_t l) {
1669 offset += l;
1670 written_length = strm.write(d, l);
1671 },
1672 [&](void) { written_length = -1; });
1673 if (written_length < 0) {
1674 return written_length;
1675 }
1676 }
1677 return static_cast<ssize_t>(offset - begin_offset);
1678}
1679
1680inline ssize_t write_content_chunked(Stream &strm, ContentProviderWithCloser content_provider) {
1681 size_t offset = 0;
1682 auto data_available = true;
1683 ssize_t total_written_length = 0;
1684 while (data_available) {
1685 ssize_t written_length = 0;
1686 content_provider(offset, 0,
1687 [&](const char *d, size_t l) {
1688 data_available = l > 0;
1689 offset += l;
1690
1691 // Emit chunked response header and footer for each chunk
1692 auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
1693 written_length = strm.write(chunk);
1694 },
1695 [&](void) {
1696 data_available = false;
1697 written_length = strm.write("0\r\n\r\n");
1698 });
1699
1700 if (written_length < 0) {
1701 return written_length;
1702 }
1703 total_written_length += written_length;
1704 }
1705 return total_written_length;
1706}
1707
1708template <typename T> inline bool redirect(T &cli, const Request &req, Response &res, const std::string &path) {
1709 Request new_req;
1710 new_req.method = req.method;
1711 new_req.path = path;
1712 new_req.headers = req.headers;
1713 new_req.body = req.body;
1714 new_req.redirect_count = req.redirect_count - 1;
1715 new_req.response_handler = req.response_handler;
1716 new_req.content_receiver = req.content_receiver;
1717 new_req.progress = req.progress;
1718
1719 Response new_res;
1720 auto ret = cli.send(new_req, new_res);
1721 if (ret) {
1722 res = new_res;
1723 }
1724 return ret;
1725}
1726
1727inline std::string encode_url(const std::string &s) {
1728 std::string result;
1729
1730 for (auto i = 0; s[i]; i++) {
1731 switch (s[i]) {
1732 case ' ':
1733 result += "%20";
1734 break;
1735 case '+':
1736 result += "%2B";
1737 break;
1738 case '\r':
1739 result += "%0D";
1740 break;
1741 case '\n':
1742 result += "%0A";
1743 break;
1744 case '\'':
1745 result += "%27";
1746 break;
1747 case ',':
1748 result += "%2C";
1749 break;
1750 case ':':
1751 result += "%3A";
1752 break;
1753 case ';':
1754 result += "%3B";
1755 break;
1756 default:
1757 auto c = static_cast<uint8_t>(s[i]);
1758 if (c >= 0x80) {
1759 result += '%';
1760 char hex[4];
1761 size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
1762 assert(len == 2);
1763 result.append(hex, len);
1764 } else {
1765 result += s[i];
1766 }
1767 break;
1768 }
1769 }
1770
1771 return result;
1772}
1773
1774inline std::string decode_url(const std::string &s) {
1775 std::string result;
1776
1777 for (size_t i = 0; i < s.size(); i++) {
1778 if (s[i] == '%' && i + 1 < s.size()) {
1779 if (s[i + 1] == 'u') {
1780 int val = 0;
1781 if (from_hex_to_i(s, i + 2, 4, val)) {
1782 // 4 digits Unicode codes
1783 char buff[4];
1784 size_t len = to_utf8(val, buff);
1785 if (len > 0) {
1786 result.append(buff, len);
1787 }
1788 i += 5; // 'u0000'
1789 } else {
1790 result += s[i];
1791 }
1792 } else {
1793 int val = 0;
1794 if (from_hex_to_i(s, i + 1, 2, val)) {
1795 // 2 digits hex codes
1796 result += static_cast<char>(val);
1797 i += 2; // '00'
1798 } else {
1799 result += s[i];
1800 }
1801 }
1802 } else if (s[i] == '+') {
1803 result += ' ';
1804 } else {
1805 result += s[i];
1806 }
1807 }
1808
1809 return result;
1810}
1811
1812inline void parse_query_text(const std::string &s, Params &params) {
1813 split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) {
1814 std::string key;
1815 std::string val;
1816 split(b, e, '=', [&](const char *b, const char *e) {
1817 if (key.empty()) {
1818 key.assign(b, e);
1819 } else {
1820 val.assign(b, e);
1821 }
1822 });
1823 params.emplace(key, decode_url(val));
1824 });
1825}
1826
1827inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) {
1828 auto pos = content_type.find("boundary=");
1829 if (pos == std::string::npos) {
1830 return false;
1831 }
1832
1833 boundary = content_type.substr(pos + 9);
1834 return true;
1835}
1836
1837inline bool parse_multipart_formdata(const std::string &boundary, const std::string &body, MultipartFiles &files) {
1838 static std::string dash = "--";
1839 static std::string crlf = "\r\n";
1840
1841 static std::regex re_content_type("Content-Type: (.*?)$", std::regex_constants::icase);
1842
1843 static std::regex re_content_disposition("Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?",
1844 std::regex_constants::icase);
1845
1846 auto dash_boundary = dash + boundary;
1847
1848 auto pos = body.find(dash_boundary);
1849 if (pos != 0) {
1850 return false;
1851 }
1852
1853 pos += dash_boundary.size();
1854
1855 auto next_pos = body.find(crlf, pos);
1856 if (next_pos == std::string::npos) {
1857 return false;
1858 }
1859
1860 pos = next_pos + crlf.size();
1861
1862 while (pos < body.size()) {
1863 next_pos = body.find(crlf, pos);
1864 if (next_pos == std::string::npos) {
1865 return false;
1866 }
1867
1868 std::string name;
1869 MultipartFile file;
1870
1871 auto header = body.substr(pos, (next_pos - pos));
1872
1873 while (pos != next_pos) {
1874 std::smatch m;
1875 if (std::regex_match(header, m, re_content_type)) {
1876 file.content_type = m[1];
1877 } else if (std::regex_match(header, m, re_content_disposition)) {
1878 name = m[1];
1879 file.filename = m[2];
1880 }
1881
1882 pos = next_pos + crlf.size();
1883
1884 next_pos = body.find(crlf, pos);
1885 if (next_pos == std::string::npos) {
1886 return false;
1887 }
1888
1889 header = body.substr(pos, (next_pos - pos));
1890 }
1891
1892 pos = next_pos + crlf.size();
1893
1894 next_pos = body.find(crlf + dash_boundary, pos);
1895
1896 if (next_pos == std::string::npos) {
1897 return false;
1898 }
1899
1900 file.offset = pos;
1901 file.length = next_pos - pos;
1902
1903 pos = next_pos + crlf.size() + dash_boundary.size();
1904
1905 next_pos = body.find(crlf, pos);
1906 if (next_pos == std::string::npos) {
1907 return false;
1908 }
1909
1910 files.emplace(name, file);
1911
1912 pos = next_pos + crlf.size();
1913 }
1914
1915 return true;
1916}
1917
1918inline bool parse_range_header(const std::string &s, Ranges &ranges) {
1919 try {
1920 static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
1921 std::smatch m;
1922 if (std::regex_match(s, m, re_first_range)) {
1923 auto pos = m.position(1);
1924 auto len = m.length(1);
1925 detail::split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
1926 static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
1927 std::cmatch m;
1928 if (std::regex_match(b, e, m, re_another_range)) {
1929 ssize_t first = -1;
1930 if (!m.str(1).empty()) {
1931 first = static_cast<ssize_t>(std::stoll(m.str(1)));
1932 }
1933
1934 ssize_t last = -1;
1935 if (!m.str(2).empty()) {
1936 last = static_cast<ssize_t>(std::stoll(m.str(2)));
1937 }
1938
1939 if (first != -1 && last != -1 && first > last) {
1940 throw std::runtime_error("invalid range error");
1941 }
1942 ranges.emplace_back(std::make_pair(first, last));
1943 }
1944 });
1945 return true;
1946 }
1947 return false;
1948 } catch (...) {
1949 return false;
1950 }
1951}
1952
1953inline std::string to_lower(const char *beg, const char *end) {
1954 std::string out;
1955 auto it = beg;
1956 while (it != end) {
1957 out += static_cast<char>(::tolower(*it));
1958 it++;
1959 }
1960 return out;
1961}
1962
1963inline std::string make_multipart_data_boundary() {
1964 static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1965
1966 std::random_device seed_gen;
1967 std::mt19937 engine(seed_gen());
1968
1969 std::string result = "--cpp-httplib-multipart-data-";
1970
1971 for (auto i = 0; i < 16; i++) {
1972 result += data[engine() % (sizeof(data) - 1)];
1973 }
1974
1975 return result;
1976}
1977
1978inline std::pair<size_t, size_t> get_range_offset_and_length(const Request &req, size_t content_length, size_t index) {
1979 auto r = req.ranges[index];
1980
1981 if (r.first == -1 && r.second == -1) {
1982 return std::make_pair(0, content_length);
1983 }
1984
1985 if (r.first == -1) {
1986 r.first = content_length - r.second;
1987 r.second = content_length - 1;
1988 }
1989
1990 if (r.second == -1) {
1991 r.second = content_length - 1;
1992 }
1993
1994 return std::make_pair(r.first, r.second - r.first + 1);
1995}
1996
1997inline std::string make_content_range_header_field(size_t offset, size_t length, size_t content_length) {
1998 std::string field = "bytes ";
1999 field += std::to_string(offset);
2000 field += "-";
2001 field += std::to_string(offset + length - 1);
2002 field += "/";
2003 field += std::to_string(content_length);
2004 return field;
2005}
2006
2007template <typename SToken, typename CToken, typename Content>
2008bool process_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary,
2009 const std::string &content_type, SToken stoken, CToken ctoken, Content content) {
2010 for (size_t i = 0; i < req.ranges.size(); i++) {
2011 ctoken("--");
2012 stoken(boundary);
2013 ctoken("\r\n");
2014 if (!content_type.empty()) {
2015 ctoken("Content-Type: ");
2016 stoken(content_type);
2017 ctoken("\r\n");
2018 }
2019
2020 auto offsets = detail::get_range_offset_and_length(req, res.body.size(), i);
2021 auto offset = offsets.first;
2022 auto length = offsets.second;
2023
2024 ctoken("Content-Range: ");
2025 stoken(make_content_range_header_field(offset, length, res.body.size()));
2026 ctoken("\r\n");
2027 ctoken("\r\n");
2028 if (!content(offset, length)) {
2029 return false;
2030 }
2031 ctoken("\r\n");
2032 }
2033
2034 ctoken("--");
2035 stoken(boundary);
2036 ctoken("--\r\n");
2037
2038 return true;
2039}
2040
2041inline std::string make_multipart_ranges_data(const Request &req, Response &res, const std::string &boundary,
2042 const std::string &content_type) {
2043 std::string data;
2044
2045 process_multipart_ranges_data(req, res, boundary, content_type, [&](const std::string &token) { data += token; },
2046 [&](const char *token) { data += token; },
2047 [&](size_t offset, size_t length) {
2048 data += res.body.substr(offset, length);
2049 return true;
2050 });
2051
2052 return data;
2053}
2054
2055inline size_t get_multipart_ranges_data_length(const Request &req, Response &res, const std::string &boundary,
2056 const std::string &content_type) {
2057 size_t data_length = 0;
2058
2059 process_multipart_ranges_data(req, res, boundary, content_type,
2060 [&](const std::string &token) { data_length += token.size(); },
2061 [&](const char *token) { data_length += strlen(token); },
2062 [&](size_t /*offset*/, size_t length) {
2063 data_length += length;
2064 return true;
2065 });
2066
2067 return data_length;
2068}
2069
2070inline bool write_multipart_ranges_data(Stream &strm, const Request &req, Response &res, const std::string &boundary,
2071 const std::string &content_type) {
2072 return process_multipart_ranges_data(
2073 req, res, boundary, content_type, [&](const std::string &token) { strm.write(token); },
2074 [&](const char *token) { strm.write(token); },
2075 [&](size_t offset, size_t length) {
2076 return detail::write_content(strm, res.content_provider, offset, length) >= 0;
2077 });
2078}
2079
2080inline std::pair<size_t, size_t> get_range_offset_and_length(const Request &req, const Response &res, size_t index) {
2081 auto r = req.ranges[index];
2082
2083 if (r.second == -1) {
2084 r.second = res.content_length - 1;
2085 }
2086
2087 return std::make_pair(r.first, r.second - r.first + 1);
2088}
2089
2090#ifdef _WIN32
2091class WSInit {
2092public:
2093 WSInit() {
2094 WSADATA wsaData;
2095 WSAStartup(0x0002, &wsaData);
2096 }
2097
2098 ~WSInit() {
2099 WSACleanup();
2100 }
2101};
2102
2103static WSInit wsinit_;
2104#endif
2105
2106} // namespace detail
2107
2108// Header utilities
2109inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
2110 std::string field = "bytes=";
2111 auto i = 0;
2112 for (auto r : ranges) {
2113 if (i != 0) {
2114 field += ", ";
2115 }
2116 if (r.first != -1) {
2117 field += std::to_string(r.first);
2118 }
2119 field += '-';
2120 if (r.second != -1) {
2121 field += std::to_string(r.second);
2122 }
2123 i++;
2124 }
2125 return std::make_pair("Range", field);
2126}
2127
2128inline std::pair<std::string, std::string> make_basic_authentication_header(const std::string &username,
2129 const std::string &password) {
2130 auto field = "Basic " + detail::base64_encode(username + ":" + password);
2131 return std::make_pair("Authorization", field);
2132}
2133
2134// Request implementation
2135inline bool Request::has_header(const char *key) const {
2136 return detail::has_header(headers, key);
2137}
2138
2139inline std::string Request::get_header_value(const char *key, size_t id) const {
2140 return detail::get_header_value(headers, key, id, "");
2141}
2142
2143inline size_t Request::get_header_value_count(const char *key) const {
2144 auto r = headers.equal_range(key);
2145 return std::distance(r.first, r.second);
2146}
2147
2148inline void Request::set_header(const char *key, const char *val) {
2149 headers.emplace(key, val);
2150}
2151
2152inline void Request::set_header(const char *key, const std::string &val) {
2153 headers.emplace(key, val);
2154}
2155
2156inline bool Request::has_param(const char *key) const {
2157 return params.find(key) != params.end();
2158}
2159
2160inline std::string Request::get_param_value(const char *key, size_t id) const {
2161 auto it = params.find(key);
2162 std::advance(it, id);
2163 if (it != params.end()) {
2164 return it->second;
2165 }
2166 return std::string();
2167}
2168
2169inline size_t Request::get_param_value_count(const char *key) const {
2170 auto r = params.equal_range(key);
2171 return std::distance(r.first, r.second);
2172}
2173
2174inline bool Request::has_file(const char *key) const {
2175 return files.find(key) != files.end();
2176}
2177
2178inline MultipartFile Request::get_file_value(const char *key) const {
2179 auto it = files.find(key);
2180 if (it != files.end()) {
2181 return it->second;
2182 }
2183 return MultipartFile();
2184}
2185
2186// Response implementation
2187inline bool Response::has_header(const char *key) const {
2188 return headers.find(key) != headers.end();
2189}
2190
2191inline std::string Response::get_header_value(const char *key, size_t id) const {
2192 return detail::get_header_value(headers, key, id, "");
2193}
2194
2195inline size_t Response::get_header_value_count(const char *key) const {
2196 auto r = headers.equal_range(key);
2197 return std::distance(r.first, r.second);
2198}
2199
2200inline void Response::set_header(const char *key, const char *val) {
2201 headers.emplace(key, val);
2202}
2203
2204inline void Response::set_header(const char *key, const std::string &val) {
2205 headers.emplace(key, val);
2206}
2207
2208inline void Response::set_redirect(const char *url) {
2209 set_header("Location", url);
2210 status = 302;
2211}
2212
2213inline void Response::set_content(const char *s, size_t n, const char *content_type) {
2214 body.assign(s, n);
2215 set_header("Content-Type", content_type);
2216}
2217
2218inline void Response::set_content(const std::string &s, const char *content_type) {
2219 body = s;
2220 set_header("Content-Type", content_type);
2221}
2222
2223inline void Response::set_content_provider(size_t length,
2224 std::function<void(size_t offset, size_t length, DataSink sink)> provider,
2225 std::function<void()> resource_releaser) {
2226 assert(length > 0);
2227 content_length = length;
2228 content_provider = [provider](size_t offset, size_t length, DataSink sink, Done) {
2229 provider(offset, length, sink);
2230 };
2231 content_provider_resource_releaser = resource_releaser;
2232}
2233
2234inline void
2235Response::set_chunked_content_provider(std::function<void(size_t offset, DataSink sink, Done done)> provider,
2236 std::function<void()> resource_releaser) {
2237 content_length = 0;
2238 content_provider = [provider](size_t offset, size_t, DataSink sink, Done done) { provider(offset, sink, done); };
2239 content_provider_resource_releaser = resource_releaser;
2240}
2241
2242// Rstream implementation
2243template <typename... Args> inline int Stream::write_format(const char *fmt, const Args &... args) {
2244 std::array<char, 2048> buf;
2245
2246#if defined(_MSC_VER) && _MSC_VER < 1900
2247 auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
2248#else
2249 auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...);
2250#endif
2251 if (n <= 0) {
2252 return n;
2253 }
2254
2255 if (n >= static_cast<int>(buf.size()) - 1) {
2256 std::vector<char> glowable_buf(buf.size());
2257
2258 while (n >= static_cast<int>(glowable_buf.size() - 1)) {
2259 glowable_buf.resize(glowable_buf.size() * 2);
2260#if defined(_MSC_VER) && _MSC_VER < 1900
2261 n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...);
2262#else
2263 n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
2264#endif
2265 }
2266 return write(&glowable_buf[0], n);
2267 } else {
2268 return write(buf.data(), n);
2269 }
2270}
2271
2272// Socket stream implementation
2273inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec)
2274 : sock_(sock), read_timeout_sec_(read_timeout_sec), read_timeout_usec_(read_timeout_usec) {
2275}
2276
2277inline SocketStream::~SocketStream() {
2278}
2279
2280inline int SocketStream::read(char *ptr, size_t size) {
2281 if (detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
2282 return recv(sock_, ptr, static_cast<int>(size), 0);
2283 }
2284 return -1;
2285}
2286
2287inline int SocketStream::write(const char *ptr, size_t size) {
2288 return send(sock_, ptr, static_cast<int>(size), 0);
2289}
2290
2291inline int SocketStream::write(const char *ptr) {
2292 return write(ptr, strlen(ptr));
2293}
2294
2295inline int SocketStream::write(const std::string &s) {
2296 return write(s.data(), s.size());
2297}
2298
2299inline std::string SocketStream::get_remote_addr() const {
2300 return detail::get_remote_addr(sock_);
2301}
2302
2303// Buffer stream implementation
2304inline int BufferStream::read(char *ptr, size_t size) {
2305#if defined(_MSC_VER) && _MSC_VER < 1900
2306 return static_cast<int>(buffer._Copy_s(ptr, size, size));
2307#else
2308 return static_cast<int>(buffer.copy(ptr, size));
2309#endif
2310}
2311
2312inline int BufferStream::write(const char *ptr, size_t size) {
2313 buffer.append(ptr, size);
2314 return static_cast<int>(size);
2315}
2316
2317inline int BufferStream::write(const char *ptr) {
2318 return write(ptr, strlen(ptr));
2319}
2320
2321inline int BufferStream::write(const std::string &s) {
2322 return write(s.data(), s.size());
2323}
2324
2325inline std::string BufferStream::get_remote_addr() const {
2326 return "";
2327}
2328
2329inline const std::string &BufferStream::get_buffer() const {
2330 return buffer;
2331}
2332
2333// HTTP server implementation
2334inline Server::Server()
2335 : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
2336 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND), payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH),
2337 is_running_(false), svr_sock_(INVALID_SOCKET) {
2338#ifndef _WIN32
2339 signal(SIGPIPE, SIG_IGN);
2340#endif
2341 new_task_queue = [] {
2342#if CPPHTTPLIB_THREAD_POOL_COUNT > 0
2343 return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT);
2344#else
2345 return new Threads();
2346#endif
2347 };
2348}
2349
2350inline Server::~Server() {
2351}
2352
2353inline Server &Server::Get(const char *pattern, Handler handler) {
2354 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2355 return *this;
2356}
2357
2358inline Server &Server::Post(const char *pattern, Handler handler) {
2359 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2360 return *this;
2361}
2362
2363inline Server &Server::Post(const char *pattern, HandlerWithContentReader handler) {
2364 post_handlers_for_content_reader.push_back(std::make_pair(std::regex(pattern), handler));
2365 return *this;
2366}
2367
2368inline Server &Server::Put(const char *pattern, Handler handler) {
2369 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2370 return *this;
2371}
2372
2373inline Server &Server::Put(const char *pattern, HandlerWithContentReader handler) {
2374 put_handlers_for_content_reader.push_back(std::make_pair(std::regex(pattern), handler));
2375 return *this;
2376}
2377
2378inline Server &Server::Patch(const char *pattern, Handler handler) {
2379 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2380 return *this;
2381}
2382
2383inline Server &Server::Patch(const char *pattern, HandlerWithContentReader handler) {
2384 patch_handlers_for_content_reader.push_back(std::make_pair(std::regex(pattern), handler));
2385 return *this;
2386}
2387
2388inline Server &Server::Delete(const char *pattern, Handler handler) {
2389 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2390 return *this;
2391}
2392
2393inline Server &Server::Options(const char *pattern, Handler handler) {
2394 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
2395 return *this;
2396}
2397
2398inline bool Server::set_base_dir(const char *path) {
2399 if (detail::is_dir(path)) {
2400 base_dir_ = path;
2401 return true;
2402 }
2403 return false;
2404}
2405
2406inline void Server::set_file_request_handler(Handler handler) {
2407 file_request_handler_ = std::move(handler);
2408}
2409
2410inline void Server::set_error_handler(Handler handler) {
2411 error_handler_ = std::move(handler);
2412}
2413
2414inline void Server::set_logger(Logger logger) {
2415 logger_ = std::move(logger);
2416}
2417
2418inline void Server::set_keep_alive_max_count(size_t count) {
2419 keep_alive_max_count_ = count;
2420}
2421
2422inline void Server::set_read_timeout(time_t sec, time_t usec) {
2423 read_timeout_sec_ = sec;
2424 read_timeout_usec_ = usec;
2425}
2426
2427inline void Server::set_payload_max_length(size_t length) {
2428 payload_max_length_ = length;
2429}
2430
2431inline bool Server::bind_to_port(const char *host, int port, int socket_flags) {
2432 if (bind_internal(host, port, socket_flags) < 0)
2433 return false;
2434 return true;
2435}
2436inline int Server::bind_to_any_port(const char *host, int socket_flags) {
2437 return bind_internal(host, 0, socket_flags);
2438}
2439
2440inline bool Server::listen_after_bind() {
2441 return listen_internal();
2442}
2443
2444inline bool Server::listen(const char *host, int port, int socket_flags) {
2445 return bind_to_port(host, port, socket_flags) && listen_internal();
2446}
2447
2448inline bool Server::is_running() const {
2449 return is_running_;
2450}
2451
2452inline void Server::stop() {
2453 if (is_running_) {
2454 assert(svr_sock_ != INVALID_SOCKET);
2455 std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
2456 detail::shutdown_socket(sock);
2457 detail::close_socket(sock);
2458 }
2459}
2460
2461inline bool Server::parse_request_line(const char *s, Request &req) {
2462 static std::regex re("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
2463 "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n");
2464
2465 std::cmatch m;
2466 if (std::regex_match(s, m, re)) {
2467 req.version = std::string(m[5]);
2468 req.method = std::string(m[1]);
2469 req.target = std::string(m[2]);
2470 req.path = detail::decode_url(m[3]);
2471
2472 // Parse query text
2473 auto len = std::distance(m[4].first, m[4].second);
2474 if (len > 0) {
2475 detail::parse_query_text(m[4], req.params);
2476 }
2477
2478 return true;
2479 }
2480
2481 return false;
2482}
2483
2484inline bool Server::write_response(Stream &strm, bool last_connection, const Request &req, Response &res) {
2485 assert(res.status != -1);
2486
2487 if (400 <= res.status && error_handler_) {
2488 error_handler_(req, res);
2489 }
2490
2491 // Response line
2492 if (!strm.write_format("HTTP/1.1 %d %s\r\n", res.status, detail::status_message(res.status))) {
2493 return false;
2494 }
2495
2496 // Headers
2497 if (last_connection || req.get_header_value("Connection") == "close") {
2498 res.set_header("Connection", "close");
2499 }
2500
2501 if (!last_connection && req.get_header_value("Connection") == "Keep-Alive") {
2502 res.set_header("Connection", "Keep-Alive");
2503 }
2504
2505 if (!res.has_header("Content-Type")) {
2506 res.set_header("Content-Type", "text/plain");
2507 }
2508
2509 if (!res.has_header("Accept-Ranges")) {
2510 res.set_header("Accept-Ranges", "bytes");
2511 }
2512
2513 std::string content_type;
2514 std::string boundary;
2515
2516 if (req.ranges.size() > 1) {
2517 boundary = detail::make_multipart_data_boundary();
2518
2519 auto it = res.headers.find("Content-Type");
2520 if (it != res.headers.end()) {
2521 content_type = it->second;
2522 res.headers.erase(it);
2523 }
2524
2525 res.headers.emplace("Content-Type", "multipart/byteranges; boundary=" + boundary);
2526 }
2527
2528 if (res.body.empty()) {
2529 if (res.content_length > 0) {
2530 size_t length = 0;
2531 if (req.ranges.empty()) {
2532 length = res.content_length;
2533 } else if (req.ranges.size() == 1) {
2534 auto offsets = detail::get_range_offset_and_length(req, res.content_length, 0);
2535 auto offset = offsets.first;
2536 length = offsets.second;
2537 auto content_range = detail::make_content_range_header_field(offset, length, res.content_length);
2538 res.set_header("Content-Range", content_range);
2539 } else {
2540 length = detail::get_multipart_ranges_data_length(req, res, boundary, content_type);
2541 }
2542 res.set_header("Content-Length", std::to_string(length));
2543 } else {
2544 if (res.content_provider) {
2545 res.set_header("Transfer-Encoding", "chunked");
2546 } else {
2547 res.set_header("Content-Length", "0");
2548 }
2549 }
2550 } else {
2551 if (req.ranges.empty()) {
2552 ;
2553 } else if (req.ranges.size() == 1) {
2554 auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0);
2555 auto offset = offsets.first;
2556 auto length = offsets.second;
2557 auto content_range = detail::make_content_range_header_field(offset, length, res.body.size());
2558 res.set_header("Content-Range", content_range);
2559 res.body = res.body.substr(offset, length);
2560 } else {
2561 res.body = detail::make_multipart_ranges_data(req, res, boundary, content_type);
2562 }
2563
2564#ifdef CPPHTTPLIB_ZLIB_SUPPORT
2565 // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0
2566 const auto &encodings = req.get_header_value("Accept-Encoding");
2567 if (encodings.find("gzip") != std::string::npos && detail::can_compress(res.get_header_value("Content-Type"))) {
2568 if (detail::compress(res.body)) {
2569 res.set_header("Content-Encoding", "gzip");
2570 }
2571 }
2572#endif
2573
2574 auto length = std::to_string(res.body.size());
2575 res.set_header("Content-Length", length);
2576 }
2577
2578 if (!detail::write_headers(strm, res, Headers())) {
2579 return false;
2580 }
2581
2582 // Body
2583 if (req.method != "HEAD") {
2584 if (!res.body.empty()) {
2585 if (!strm.write(res.body)) {
2586 return false;
2587 }
2588 } else if (res.content_provider) {
2589 if (!write_content_with_provider(strm, req, res, boundary, content_type)) {
2590 return false;
2591 }
2592 }
2593 }
2594
2595 // Log
2596 if (logger_) {
2597 logger_(req, res);
2598 }
2599
2600 return true;
2601}
2602
2603inline bool Server::write_content_with_provider(Stream &strm, const Request &req, Response &res,
2604 const std::string &boundary, const std::string &content_type) {
2605 if (res.content_length) {
2606 if (req.ranges.empty()) {
2607 if (detail::write_content(strm, res.content_provider, 0, res.content_length) < 0) {
2608 return false;
2609 }
2610 } else if (req.ranges.size() == 1) {
2611 auto offsets = detail::get_range_offset_and_length(req, res.content_length, 0);
2612 auto offset = offsets.first;
2613 auto length = offsets.second;
2614 if (detail::write_content(strm, res.content_provider, offset, length) < 0) {
2615 return false;
2616 }
2617 } else {
2618 if (!detail::write_multipart_ranges_data(strm, req, res, boundary, content_type)) {
2619 return false;
2620 }
2621 }
2622 } else {
2623 if (detail::write_content_chunked(strm, res.content_provider) < 0) {
2624 return false;
2625 }
2626 }
2627 return true;
2628}
2629
2630inline bool Server::read_content(Stream &strm, bool last_connection, Request &req, Response &res) {
2631 if (!detail::read_content(strm, req, payload_max_length_, res.status, Progress(), [&](const char *buf, size_t n) {
2632 if (req.body.size() + n > req.body.max_size()) {
2633 return false;
2634 }
2635 req.body.append(buf, n);
2636 return true;
2637 })) {
2638 return write_response(strm, last_connection, req, res);
2639 }
2640
2641 const auto &content_type = req.get_header_value("Content-Type");
2642
2643 if (!content_type.find("application/x-www-form-urlencoded")) {
2644 detail::parse_query_text(req.body, req.params);
2645 } else if (!content_type.find("multipart/form-data")) {
2646 std::string boundary;
2647 if (!detail::parse_multipart_boundary(content_type, boundary) ||
2648 !detail::parse_multipart_formdata(boundary, req.body, req.files)) {
2649 res.status = 400;
2650 return write_response(strm, last_connection, req, res);
2651 }
2652 }
2653
2654 return true;
2655}
2656
2657inline bool Server::read_content_with_content_receiver(Stream &strm, bool last_connection, Request &req, Response &res,
2658 ContentReceiver receiver) {
2659 if (!detail::read_content(strm, req, payload_max_length_, res.status, Progress(),
2660 [&](const char *buf, size_t n) { return receiver(buf, n); })) {
2661 return write_response(strm, last_connection, req, res);
2662 }
2663
2664 return true;
2665}
2666
2667inline bool Server::handle_file_request(Request &req, Response &res) {
2668 if (!base_dir_.empty() && detail::is_valid_path(req.path)) {
2669 std::string path = base_dir_ + req.path;
2670
2671 if (!path.empty() && path.back() == '/') {
2672 path += "index.html";
2673 }
2674
2675 if (detail::is_file(path)) {
2676 detail::read_file(path, res.body);
2677 auto type = detail::find_content_type(path);
2678 if (type) {
2679 res.set_header("Content-Type", type);
2680 }
2681 res.status = 200;
2682 if (file_request_handler_) {
2683 file_request_handler_(req, res);
2684 }
2685 return true;
2686 }
2687 }
2688
2689 return false;
2690}
2691
2692inline socket_t Server::create_server_socket(const char *host, int port, int socket_flags) const {
2693 return detail::create_socket(host, port,
2694 [](socket_t sock, struct addrinfo &ai) -> bool {
2695 if (::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
2696 return false;
2697 }
2698 if (::listen(sock, 5)) { // Listen through 5 channels
2699 return false;
2700 }
2701 return true;
2702 },
2703 socket_flags);
2704}
2705
2706inline int Server::bind_internal(const char *host, int port, int socket_flags) {
2707 if (!is_valid()) {
2708 return -1;
2709 }
2710
2711 svr_sock_ = create_server_socket(host, port, socket_flags);
2712 if (svr_sock_ == INVALID_SOCKET) {
2713 return -1;
2714 }
2715
2716 if (port == 0) {
2717 struct sockaddr_storage address;
2718 socklen_t len = sizeof(address);
2719 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&address), &len) == -1) {
2720 return -1;
2721 }
2722 if (address.ss_family == AF_INET) {
2723 return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port);
2724 } else if (address.ss_family == AF_INET6) {
2725 return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
2726 } else {
2727 return -1;
2728 }
2729 } else {
2730 return port;
2731 }
2732}
2733
2734inline bool Server::listen_internal() {
2735 auto ret = true;
2736 is_running_ = true;
2737
2738 {
2739 std::unique_ptr<TaskQueue> task_queue(new_task_queue());
2740
2741 for (;;) {
2742 if (svr_sock_ == INVALID_SOCKET) {
2743 // The server socket was closed by 'stop' method.
2744 break;
2745 }
2746
2747 auto val = detail::select_read(svr_sock_, 0, 100000);
2748
2749 if (val == 0) { // Timeout
2750 continue;
2751 }
2752
2753 socket_t sock = accept(svr_sock_, nullptr, nullptr);
2754
2755 if (sock == INVALID_SOCKET) {
2756 if (errno == EMFILE) {
2757 // The per-process limit of open file descriptors has been reached.
2758 // Try to accept new connections after a short sleep.
2759 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2760 continue;
2761 }
2762 if (svr_sock_ != INVALID_SOCKET) {
2763 detail::close_socket(svr_sock_);
2764 ret = false;
2765 } else {
2766 ; // The server socket was closed by user.
2767 }
2768 break;
2769 }
2770
2771 task_queue->enqueue([=]() { process_and_close_socket(sock); });
2772 }
2773
2774 task_queue->shutdown();
2775 }
2776
2777 is_running_ = false;
2778 return ret;
2779}
2780
2781inline bool Server::routing(Request &req, Response &res, ContentReader content_reader) {
2782 // File handler
2783 if (req.method == "GET" && handle_file_request(req, res)) {
2784 return true;
2785 }
2786
2787 // Content reader handler
2788 if (req.method == "POST") {
2789 if (dispatch_request_for_content_reader(req, res, content_reader, post_handlers_for_content_reader)) {
2790 return true;
2791 }
2792 } else if (req.method == "PUT") {
2793 if (dispatch_request_for_content_reader(req, res, content_reader, put_handlers_for_content_reader)) {
2794 return true;
2795 }
2796 } else if (req.method == "PATCH") {
2797 if (dispatch_request_for_content_reader(req, res, content_reader, patch_handlers_for_content_reader)) {
2798 return true;
2799 }
2800 }
2801
2802 // Read content into `req.body`
2803 if (!content_reader(nullptr)) {
2804 return false;
2805 }
2806
2807 // Regular handler
2808 if (req.method == "GET" || req.method == "HEAD") {
2809 return dispatch_request(req, res, get_handlers_);
2810 } else if (req.method == "POST") {
2811 return dispatch_request(req, res, post_handlers_);
2812 } else if (req.method == "PUT") {
2813 return dispatch_request(req, res, put_handlers_);
2814 } else if (req.method == "DELETE") {
2815 return dispatch_request(req, res, delete_handlers_);
2816 } else if (req.method == "OPTIONS") {
2817 return dispatch_request(req, res, options_handlers_);
2818 } else if (req.method == "PATCH") {
2819 return dispatch_request(req, res, patch_handlers_);
2820 }
2821
2822 res.status = 400;
2823 return false;
2824}
2825
2826inline bool Server::dispatch_request(Request &req, Response &res, Handlers &handlers) {
2827 for (const auto &x : handlers) {
2828 const auto &pattern = x.first;
2829 const auto &handler = x.second;
2830
2831 if (std::regex_match(req.path, req.matches, pattern)) {
2832 handler(req, res);
2833 return true;
2834 }
2835 }
2836 return false;
2837}
2838
2839inline bool Server::dispatch_request_for_content_reader(Request &req, Response &res, ContentReader content_reader,
2840 HandersForContentReader &handlers) {
2841 for (const auto &x : handlers) {
2842 const auto &pattern = x.first;
2843 const auto &handler = x.second;
2844
2845 if (std::regex_match(req.path, req.matches, pattern)) {
2846 handler(req, res, content_reader);
2847 return true;
2848 }
2849 }
2850 return false;
2851}
2852
2853inline bool Server::process_request(Stream &strm, bool last_connection, bool &connection_close,
2854 const std::function<void(Request &)> &setup_request) {
2855 std::array<char, 2048> buf{};
2856
2857 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
2858
2859 // Connection has been closed on client
2860 if (!line_reader.getline()) {
2861 return false;
2862 }
2863
2864 Request req;
2865 Response res;
2866
2867 res.version = "HTTP/1.1";
2868
2869 // Check if the request URI doesn't exceed the limit
2870 if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
2871 Headers dummy;
2872 detail::read_headers(strm, dummy);
2873 res.status = 414;
2874 return write_response(strm, last_connection, req, res);
2875 }
2876
2877 // Request line and headers
2878 if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) {
2879 res.status = 400;
2880 return write_response(strm, last_connection, req, res);
2881 }
2882
2883 if (req.get_header_value("Connection") == "close") {
2884 connection_close = true;
2885 }
2886
2887 if (req.version == "HTTP/1.0" && req.get_header_value("Connection") != "Keep-Alive") {
2888 connection_close = true;
2889 }
2890
2891 req.set_header("REMOTE_ADDR", strm.get_remote_addr());
2892
2893 if (req.has_header("Range")) {
2894 const auto &range_header_value = req.get_header_value("Range");
2895 if (!detail::parse_range_header(range_header_value, req.ranges)) {
2896 // TODO: error
2897 }
2898 }
2899
2900 if (setup_request) {
2901 setup_request(req);
2902 }
2903
2904 // Body
2905 ContentReader content_reader = [&](ContentReceiver receiver) {
2906 if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") {
2907 if (receiver) {
2908 return read_content_with_content_receiver(strm, last_connection, req, res, receiver);
2909 } else {
2910 return read_content(strm, last_connection, req, res);
2911 }
2912 } else if (req.method == "PRI") {
2913 return read_content(strm, last_connection, req, res);
2914 }
2915 return true;
2916 };
2917
2918 // Rounting
2919 if (routing(req, res, content_reader)) {
2920 if (res.status == -1) {
2921 res.status = req.ranges.empty() ? 200 : 206;
2922 }
2923 } else {
2924 if (res.status == -1) {
2925 res.status = 404;
2926 }
2927 }
2928
2929 return write_response(strm, last_connection, req, res);
2930}
2931
2932inline bool Server::is_valid() const {
2933 return true;
2934}
2935
2936inline bool Server::process_and_close_socket(socket_t sock) {
2937 return detail::process_and_close_socket(false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
2938 [this](Stream &strm, bool last_connection, bool &connection_close) {
2939 return process_request(strm, last_connection, connection_close,
2940 nullptr);
2941 });
2942}
2943
2944// HTTP client implementation
2945inline Client::Client(const char *host, int port, time_t timeout_sec)
2946 : host_(host), port_(port), timeout_sec_(timeout_sec), host_and_port_(host_ + ":" + std::to_string(port_)),
2947 keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
2948 read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND), follow_location_(false) {
2949}
2950
2951inline Client::~Client() {
2952}
2953
2954inline bool Client::is_valid() const {
2955 return true;
2956}
2957
2958inline socket_t Client::create_client_socket() const {
2959 return detail::create_socket(host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool {
2960 detail::set_nonblocking(sock, true);
2961
2962 auto ret = connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
2963 if (ret < 0) {
2964 if (detail::is_connection_error() || !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) {
2965 detail::close_socket(sock);
2966 return false;
2967 }
2968 }
2969
2970 detail::set_nonblocking(sock, false);
2971 return true;
2972 });
2973}
2974
2975inline bool Client::read_response_line(Stream &strm, Response &res) {
2976 std::array<char, 2048> buf;
2977
2978 detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
2979
2980 if (!line_reader.getline()) {
2981 return false;
2982 }
2983
2984 const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n");
2985
2986 std::cmatch m;
2987 if (std::regex_match(line_reader.ptr(), m, re)) {
2988 res.version = std::string(m[1]);
2989 res.status = std::stoi(std::string(m[2]));
2990 }
2991
2992 return true;
2993}
2994
2995inline bool Client::send(const Request &req, Response &res) {
2996 if (req.path.empty()) {
2997 return false;
2998 }
2999
3000 auto sock = create_client_socket();
3001 if (sock == INVALID_SOCKET) {
3002 return false;
3003 }
3004
3005 auto ret = process_and_close_socket(sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) {
3006 return process_request(strm, req, res, last_connection, connection_close);
3007 });
3008
3009 if (ret && follow_location_ && (300 < res.status && res.status < 400)) {
3010 ret = redirect(req, res);
3011 }
3012
3013 return ret;
3014}
3015
3016inline bool Client::send(const std::vector<Request> &requests, std::vector<Response> &responses) {
3017 size_t i = 0;
3018 while (i < requests.size()) {
3019 auto sock = create_client_socket();
3020 if (sock == INVALID_SOCKET) {
3021 return false;
3022 }
3023
3024 if (!process_and_close_socket(sock, requests.size() - i,
3025 [&](Stream &strm, bool last_connection, bool &connection_close) -> bool {
3026 auto &req = requests[i];
3027 auto res = Response();
3028 i++;
3029
3030 if (req.path.empty()) {
3031 return false;
3032 }
3033 auto ret = process_request(strm, req, res, last_connection, connection_close);
3034
3035 if (ret && follow_location_ && (300 < res.status && res.status < 400)) {
3036 ret = redirect(req, res);
3037 }
3038
3039 if (ret) {
3040 responses.emplace_back(std::move(res));
3041 }
3042
3043 return ret;
3044 })) {
3045 return false;
3046 }
3047 }
3048
3049 return true;
3050}
3051
3052inline bool Client::redirect(const Request &req, Response &res) {
3053 if (req.redirect_count == 0) {
3054 return false;
3055 }
3056
3057 auto location = res.get_header_value("location");
3058 if (location.empty()) {
3059 return false;
3060 }
3061
3062 std::regex re(R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
3063
3064 auto scheme = is_ssl() ? "https" : "http";
3065
3066 std::smatch m;
3067 if (regex_match(location, m, re)) {
3068 auto next_scheme = m[1].str();
3069 auto next_host = m[2].str();
3070 auto next_path = m[3].str();
3071 if (next_host.empty()) {
3072 next_host = host_;
3073 }
3074 if (next_path.empty()) {
3075 next_path = "/";
3076 }
3077
3078 if (next_scheme == scheme && next_host == host_) {
3079 return detail::redirect(*this, req, res, next_path);
3080 } else {
3081 if (next_scheme == "https") {
3082#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3083 SSLClient cli(next_host.c_str());
3084 cli.follow_location(true);
3085 return detail::redirect(cli, req, res, next_path);
3086#else
3087 return false;
3088#endif
3089 } else {
3090 Client cli(next_host.c_str());
3091 cli.follow_location(true);
3092 return detail::redirect(cli, req, res, next_path);
3093 }
3094 }
3095 }
3096 return false;
3097}
3098
3099inline void Client::write_request(Stream &strm, const Request &req, bool last_connection) {
3100 BufferStream bstrm;
3101
3102 // Request line
3103 auto path = detail::encode_url(req.path);
3104
3105 bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
3106
3107 // Additonal headers
3108 Headers headers;
3109 if (last_connection) {
3110 headers.emplace("Connection", "close");
3111 }
3112
3113 if (!req.has_header("Host")) {
3114 if (is_ssl()) {
3115 if (port_ == 443) {
3116 headers.emplace("Host", host_);
3117 } else {
3118 headers.emplace("Host", host_and_port_);
3119 }
3120 } else {
3121 if (port_ == 80) {
3122 headers.emplace("Host", host_);
3123 } else {
3124 headers.emplace("Host", host_and_port_);
3125 }
3126 }
3127 }
3128
3129 if (!req.has_header("Accept")) {
3130 headers.emplace("Accept", "*/*");
3131 }
3132
3133 if (!req.has_header("User-Agent")) {
3134 headers.emplace("User-Agent", "cpp-httplib/0.2");
3135 }
3136
3137 if (req.body.empty()) {
3138 if (req.content_provider) {
3139 auto length = std::to_string(req.content_length);
3140 headers.emplace("Content-Length", length);
3141 } else {
3142 headers.emplace("Content-Length", "0");
3143 }
3144 } else {
3145 if (!req.has_header("Content-Type")) {
3146 headers.emplace("Content-Type", "text/plain");
3147 }
3148
3149 if (!req.has_header("Content-Length")) {
3150 auto length = std::to_string(req.body.size());
3151 headers.emplace("Content-Length", length);
3152 }
3153 }
3154
3155 detail::write_headers(bstrm, req, headers);
3156
3157 // Flush buffer
3158 auto &data = bstrm.get_buffer();
3159 strm.write(data.data(), data.size());
3160
3161 // Body
3162 if (req.body.empty()) {
3163 if (req.content_provider) {
3164 size_t offset = 0;
3165 size_t end_offset = req.content_length;
3166 while (offset < end_offset) {
3167 req.content_provider(offset, end_offset - offset, [&](const char *d, size_t l) {
3168 auto written_length = strm.write(d, l);
3169 offset += written_length;
3170 });
3171 }
3172 }
3173 } else {
3174 strm.write(req.body);
3175 }
3176}
3177
3178inline std::shared_ptr<Response> Client::send_with_content_provider(const char *method, const char *path,
3179 const Headers &headers, const std::string &body,
3180 size_t content_length,
3181 ContentProvider content_provider,
3182 const char *content_type, bool compress) {
3183#ifndef CPPHTTPLIB_ZLIB_SUPPORT
3184 (void)compress;
3185#endif
3186
3187 Request req;
3188 req.method = method;
3189 req.headers = headers;
3190 req.path = path;
3191
3192 req.headers.emplace("Content-Type", content_type);
3193
3194#ifdef CPPHTTPLIB_ZLIB_SUPPORT
3195 if (compress) {
3196 if (content_provider) {
3197 size_t offset = 0;
3198 while (offset < content_length) {
3199 content_provider(offset, content_length - offset, [&](const char *data, size_t data_len) {
3200 req.body.append(data, data_len);
3201 offset += data_len;
3202 });
3203 }
3204 } else {
3205 req.body = body;
3206 }
3207
3208 if (!detail::compress(req.body)) {
3209 return nullptr;
3210 }
3211 req.headers.emplace("Content-Encoding", "gzip");
3212 } else
3213#endif
3214 {
3215 if (content_provider) {
3216 req.content_length = content_length;
3217 req.content_provider = content_provider;
3218 } else {
3219 req.body = body;
3220 }
3221 }
3222
3223 auto res = std::make_shared<Response>();
3224
3225 return send(req, *res) ? res : nullptr;
3226}
3227
3228inline bool Client::process_request(Stream &strm, const Request &req, Response &res, bool last_connection,
3229 bool &connection_close) {
3230 // Send request
3231 write_request(strm, req, last_connection);
3232
3233 // Receive response and headers
3234 if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) {
3235 return false;
3236 }
3237
3238 if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") {
3239 connection_close = true;
3240 }
3241
3242 if (req.response_handler) {
3243 if (!req.response_handler(res)) {
3244 return false;
3245 }
3246 }
3247
3248 // Body
3249 if (req.method != "HEAD") {
3250 ContentReceiver out = [&](const char *buf, size_t n) {
3251 if (res.body.size() + n > res.body.max_size()) {
3252 return false;
3253 }
3254 res.body.append(buf, n);
3255 return true;
3256 };
3257
3258 if (req.content_receiver) {
3259 out = [&](const char *buf, size_t n) { return req.content_receiver(buf, n); };
3260 }
3261
3262 int dummy_status;
3263 if (!detail::read_content(strm, res, std::numeric_limits<size_t>::max(), dummy_status, req.progress, out)) {
3264 return false;
3265 }
3266 }
3267
3268 return true;
3269}
3270
3271inline bool Client::process_and_close_socket(
3272 socket_t sock, size_t request_count,
3273 std::function<bool(Stream &strm, bool last_connection, bool &connection_close)> callback) {
3274 request_count = std::min(request_count, keep_alive_max_count_);
3275 return detail::process_and_close_socket(true, sock, request_count, read_timeout_sec_, read_timeout_usec_, callback);
3276}
3277
3278inline bool Client::is_ssl() const {
3279 return false;
3280}
3281
3282inline std::shared_ptr<Response> Client::Get(const char *path) {
3283 Progress dummy;
3284 return Get(path, Headers(), dummy);
3285}
3286
3287inline std::shared_ptr<Response> Client::Get(const char *path, Progress progress) {
3288 return Get(path, Headers(), std::move(progress));
3289}
3290
3291inline std::shared_ptr<Response> Client::Get(const char *path, const Headers &headers) {
3292 Progress dummy;
3293 return Get(path, headers, dummy);
3294}
3295
3296inline std::shared_ptr<Response> Client::Get(const char *path, const Headers &headers, Progress progress) {
3297 Request req;
3298 req.method = "GET";
3299 req.path = path;
3300 req.headers = headers;
3301 req.progress = std::move(progress);
3302
3303 auto res = std::make_shared<Response>();
3304 return send(req, *res) ? res : nullptr;
3305}
3306
3307inline std::shared_ptr<Response> Client::Get(const char *path, ContentReceiver content_receiver) {
3308 Progress dummy;
3309 return Get(path, Headers(), nullptr, std::move(content_receiver), dummy);
3310}
3311
3312inline std::shared_ptr<Response> Client::Get(const char *path, ContentReceiver content_receiver, Progress progress) {
3313 return Get(path, Headers(), nullptr, std::move(content_receiver), progress);
3314}
3315
3316inline std::shared_ptr<Response> Client::Get(const char *path, const Headers &headers,
3317 ContentReceiver content_receiver) {
3318 Progress dummy;
3319 return Get(path, headers, nullptr, std::move(content_receiver), dummy);
3320}
3321
3322inline std::shared_ptr<Response> Client::Get(const char *path, const Headers &headers, ContentReceiver content_receiver,
3323 Progress progress) {
3324 return Get(path, headers, nullptr, std::move(content_receiver), progress);
3325}
3326
3327inline std::shared_ptr<Response> Client::Get(const char *path, const Headers &headers, ResponseHandler response_handler,
3328 ContentReceiver content_receiver) {
3329 Progress dummy;
3330 return Get(path, headers, std::move(response_handler), content_receiver, dummy);
3331}
3332
3333inline std::shared_ptr<Response> Client::Get(const char *path, const Headers &headers, ResponseHandler response_handler,
3334 ContentReceiver content_receiver, Progress progress) {
3335 Request req;
3336 req.method = "GET";
3337 req.path = path;
3338 req.headers = headers;
3339 req.response_handler = std::move(response_handler);
3340 req.content_receiver = std::move(content_receiver);
3341 req.progress = std::move(progress);
3342
3343 auto res = std::make_shared<Response>();
3344 return send(req, *res) ? res : nullptr;
3345}
3346
3347inline std::shared_ptr<Response> Client::Head(const char *path) {
3348 return Head(path, Headers());
3349}
3350
3351inline std::shared_ptr<Response> Client::Head(const char *path, const Headers &headers) {
3352 Request req;
3353 req.method = "HEAD";
3354 req.headers = headers;
3355 req.path = path;
3356
3357 auto res = std::make_shared<Response>();
3358
3359 return send(req, *res) ? res : nullptr;
3360}
3361
3362inline std::shared_ptr<Response> Client::Post(const char *path, const std::string &body, const char *content_type,
3363 bool compress) {
3364 return Post(path, Headers(), body, content_type, compress);
3365}
3366
3367inline std::shared_ptr<Response> Client::Post(const char *path, const Headers &headers, const std::string &body,
3368 const char *content_type, bool compress) {
3369 return send_with_content_provider("POST", path, headers, body, 0, nullptr, content_type, compress);
3370}
3371
3372inline std::shared_ptr<Response> Client::Post(const char *path, const Params &params, bool compress) {
3373 return Post(path, Headers(), params, compress);
3374}
3375
3376inline std::shared_ptr<Response> Client::Post(const char *path, size_t content_length, ContentProvider content_provider,
3377 const char *content_type, bool compress) {
3378 return Post(path, Headers(), content_length, content_provider, content_type, compress);
3379}
3380
3381inline std::shared_ptr<Response> Client::Post(const char *path, const Headers &headers, size_t content_length,
3382 ContentProvider content_provider, const char *content_type,
3383 bool compress) {
3384 return send_with_content_provider("POST", path, headers, std::string(), content_length, content_provider,
3385 content_type, compress);
3386}
3387
3388inline std::shared_ptr<Response> Client::Post(const char *path, const Headers &headers, const Params &params,
3389 bool compress) {
3390 std::string query;
3391 for (auto it = params.begin(); it != params.end(); ++it) {
3392 if (it != params.begin()) {
3393 query += "&";
3394 }
3395 query += it->first;
3396 query += "=";
3397 query += detail::encode_url(it->second);
3398 }
3399
3400 return Post(path, headers, query, "application/x-www-form-urlencoded", compress);
3401}
3402
3403inline std::shared_ptr<Response> Client::Post(const char *path, const MultipartFormDataItems &items, bool compress) {
3404 return Post(path, Headers(), items, compress);
3405}
3406
3407inline std::shared_ptr<Response> Client::Post(const char *path, const Headers &headers,
3408 const MultipartFormDataItems &items, bool compress) {
3409 auto boundary = detail::make_multipart_data_boundary();
3410
3411 std::string body;
3412
3413 for (const auto &item : items) {
3414 body += "--" + boundary + "\r\n";
3415 body += "Content-Disposition: form-data; name=\"" + item.name + "\"";
3416 if (!item.filename.empty()) {
3417 body += "; filename=\"" + item.filename + "\"";
3418 }
3419 body += "\r\n";
3420 if (!item.content_type.empty()) {
3421 body += "Content-Type: " + item.content_type + "\r\n";
3422 }
3423 body += "\r\n";
3424 body += item.content + "\r\n";
3425 }
3426
3427 body += "--" + boundary + "--\r\n";
3428
3429 std::string content_type = "multipart/form-data; boundary=" + boundary;
3430 return Post(path, headers, body, content_type.c_str(), compress);
3431}
3432
3433inline std::shared_ptr<Response> Client::Put(const char *path, const std::string &body, const char *content_type,
3434 bool compress) {
3435 return Put(path, Headers(), body, content_type, compress);
3436}
3437
3438inline std::shared_ptr<Response> Client::Put(const char *path, const Headers &headers, const std::string &body,
3439 const char *content_type, bool compress) {
3440 return send_with_content_provider("PUT", path, headers, body, 0, nullptr, content_type, compress);
3441}
3442
3443inline std::shared_ptr<Response> Client::Put(const char *path, size_t content_length, ContentProvider content_provider,
3444 const char *content_type, bool compress) {
3445 return Put(path, Headers(), content_length, content_provider, content_type, compress);
3446}
3447
3448inline std::shared_ptr<Response> Client::Put(const char *path, const Headers &headers, size_t content_length,
3449 ContentProvider content_provider, const char *content_type,
3450 bool compress) {
3451 return send_with_content_provider("PUT", path, headers, std::string(), content_length, content_provider,
3452 content_type, compress);
3453}
3454
3455inline std::shared_ptr<Response> Client::Patch(const char *path, const std::string &body, const char *content_type,
3456 bool compress) {
3457 return Patch(path, Headers(), body, content_type, compress);
3458}
3459
3460inline std::shared_ptr<Response> Client::Patch(const char *path, const Headers &headers, const std::string &body,
3461 const char *content_type, bool compress) {
3462 return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, content_type, compress);
3463}
3464
3465inline std::shared_ptr<Response> Client::Patch(const char *path, size_t content_length,
3466 ContentProvider content_provider, const char *content_type,
3467 bool compress) {
3468 return Patch(path, Headers(), content_length, content_provider, content_type, compress);
3469}
3470
3471inline std::shared_ptr<Response> Client::Patch(const char *path, const Headers &headers, size_t content_length,
3472 ContentProvider content_provider, const char *content_type,
3473 bool compress) {
3474 return send_with_content_provider("PATCH", path, headers, std::string(), content_length, content_provider,
3475 content_type, compress);
3476}
3477
3478inline std::shared_ptr<Response> Client::Delete(const char *path) {
3479 return Delete(path, Headers(), std::string(), nullptr);
3480}
3481
3482inline std::shared_ptr<Response> Client::Delete(const char *path, const std::string &body, const char *content_type) {
3483 return Delete(path, Headers(), body, content_type);
3484}
3485
3486inline std::shared_ptr<Response> Client::Delete(const char *path, const Headers &headers) {
3487 return Delete(path, headers, std::string(), nullptr);
3488}
3489
3490inline std::shared_ptr<Response> Client::Delete(const char *path, const Headers &headers, const std::string &body,
3491 const char *content_type) {
3492 Request req;
3493 req.method = "DELETE";
3494 req.headers = headers;
3495 req.path = path;
3496
3497 if (content_type) {
3498 req.headers.emplace("Content-Type", content_type);
3499 }
3500 req.body = body;
3501
3502 auto res = std::make_shared<Response>();
3503
3504 return send(req, *res) ? res : nullptr;
3505}
3506
3507inline std::shared_ptr<Response> Client::Options(const char *path) {
3508 return Options(path, Headers());
3509}
3510
3511inline std::shared_ptr<Response> Client::Options(const char *path, const Headers &headers) {
3512 Request req;
3513 req.method = "OPTIONS";
3514 req.path = path;
3515 req.headers = headers;
3516
3517 auto res = std::make_shared<Response>();
3518
3519 return send(req, *res) ? res : nullptr;
3520}
3521
3522inline void Client::set_keep_alive_max_count(size_t count) {
3523 keep_alive_max_count_ = count;
3524}
3525
3526inline void Client::set_read_timeout(time_t sec, time_t usec) {
3527 read_timeout_sec_ = sec;
3528 read_timeout_usec_ = usec;
3529}
3530
3531inline void Client::follow_location(bool on) {
3532 follow_location_ = on;
3533}
3534
3535/*
3536 * SSL Implementation
3537 */
3538#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3539namespace detail {
3540
3541template <typename U, typename V, typename T>
3542inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, size_t keep_alive_max_count,
3543 time_t read_timeout_sec, time_t read_timeout_usec, SSL_CTX *ctx,
3544 std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup, T callback) {
3545 assert(keep_alive_max_count > 0);
3546
3547 SSL *ssl = nullptr;
3548 {
3549 std::lock_guard<std::mutex> guard(ctx_mutex);
3550 ssl = SSL_new(ctx);
3551 }
3552
3553 if (!ssl) {
3554 close_socket(sock);
3555 return false;
3556 }
3557
3558 auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
3559 SSL_set_bio(ssl, bio, bio);
3560
3561 if (!setup(ssl)) {
3562 SSL_shutdown(ssl);
3563 {
3564 std::lock_guard<std::mutex> guard(ctx_mutex);
3565 SSL_free(ssl);
3566 }
3567
3568 close_socket(sock);
3569 return false;
3570 }
3571
3572 bool ret = false;
3573
3574 if (SSL_connect_or_accept(ssl) == 1) {
3575 if (keep_alive_max_count > 1) {
3576 auto count = keep_alive_max_count;
3577 while (count > 0 && (is_client_request || detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
3578 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
3579 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
3580 auto last_connection = count == 1;
3581 auto connection_close = false;
3582
3583 ret = callback(ssl, strm, last_connection, connection_close);
3584 if (!ret || connection_close) {
3585 break;
3586 }
3587
3588 count--;
3589 }
3590 } else {
3591 SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
3592 auto dummy_connection_close = false;
3593 ret = callback(ssl, strm, true, dummy_connection_close);
3594 }
3595 }
3596
3597 SSL_shutdown(ssl);
3598 {
3599 std::lock_guard<std::mutex> guard(ctx_mutex);
3600 SSL_free(ssl);
3601 }
3602
3603 close_socket(sock);
3604
3605 return ret;
3606}
3607
3608#if OPENSSL_VERSION_NUMBER < 0x10100000L
3609static std::shared_ptr<std::vector<std::mutex>> openSSL_locks_;
3610
3611class SSLThreadLocks {
3612public:
3613 SSLThreadLocks() {
3614 openSSL_locks_ = std::make_shared<std::vector<std::mutex>>(CRYPTO_num_locks());
3615 CRYPTO_set_locking_callback(locking_callback);
3616 }
3617
3618 ~SSLThreadLocks() {
3619 CRYPTO_set_locking_callback(nullptr);
3620 }
3621
3622private:
3623 static void locking_callback(int mode, int type, const char * /*file*/, int /*line*/) {
3624 auto &locks = *openSSL_locks_;
3625 if (mode & CRYPTO_LOCK) {
3626 locks[type].lock();
3627 } else {
3628 locks[type].unlock();
3629 }
3630 }
3631};
3632
3633#endif
3634
3635class SSLInit {
3636public:
3637 SSLInit() {
3638#if OPENSSL_VERSION_NUMBER < 0x1010001fL
3639 SSL_load_error_strings();
3640 SSL_library_init();
3641#else
3642 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
3643#endif
3644 }
3645
3646 ~SSLInit() {
3647#if OPENSSL_VERSION_NUMBER < 0x1010001fL
3648 ERR_free_strings();
3649#endif
3650 }
3651
3652private:
3653#if OPENSSL_VERSION_NUMBER < 0x10100000L
3654 SSLThreadLocks thread_init_;
3655#endif
3656};
3657
3658static SSLInit sslinit_;
3659
3660} // namespace detail
3661
3662// SSL socket stream implementation
3663inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec)
3664 : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), read_timeout_usec_(read_timeout_usec) {
3665}
3666
3667inline SSLSocketStream::~SSLSocketStream() {
3668}
3669
3670inline int SSLSocketStream::read(char *ptr, size_t size) {
3671 if (SSL_pending(ssl_) > 0 || detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
3672 return SSL_read(ssl_, ptr, static_cast<int>(size));
3673 }
3674 return -1;
3675}
3676
3677inline int SSLSocketStream::write(const char *ptr, size_t size) {
3678 return SSL_write(ssl_, ptr, static_cast<int>(size));
3679}
3680
3681inline int SSLSocketStream::write(const char *ptr) {
3682 return write(ptr, strlen(ptr));
3683}
3684
3685inline int SSLSocketStream::write(const std::string &s) {
3686 return write(s.data(), s.size());
3687}
3688
3689inline std::string SSLSocketStream::get_remote_addr() const {
3690 return detail::get_remote_addr(sock_);
3691}
3692
3693// SSL HTTP server implementation
3694inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, const char *client_ca_cert_file_path,
3695 const char *client_ca_cert_dir_path) {
3696 ctx_ = SSL_CTX_new(SSLv23_server_method());
3697
3698 if (ctx_) {
3699 SSL_CTX_set_options(ctx_, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
3700 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
3701
3702 // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
3703 // SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
3704 // EC_KEY_free(ecdh);
3705
3706 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
3707 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) {
3708 SSL_CTX_free(ctx_);
3709 ctx_ = nullptr;
3710 } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
3711 // if (client_ca_cert_file_path) {
3712 // auto list = SSL_load_client_CA_file(client_ca_cert_file_path);
3713 // SSL_CTX_set_client_CA_list(ctx_, list);
3714 // }
3715
3716 SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, client_ca_cert_dir_path);
3717
3718 SSL_CTX_set_verify(ctx_,
3719 SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
3720 nullptr);
3721 }
3722 }
3723}
3724
3725inline SSLServer::~SSLServer() {
3726 if (ctx_) {
3727 SSL_CTX_free(ctx_);
3728 }
3729}
3730
3731inline bool SSLServer::is_valid() const {
3732 return ctx_;
3733}
3734
3735inline bool SSLServer::process_and_close_socket(socket_t sock) {
3736 return detail::process_and_close_socket_ssl(
3737 false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, ctx_, ctx_mutex_, SSL_accept,
3738 [](SSL * /*ssl*/) { return true; },
3739 [this](SSL *ssl, Stream &strm, bool last_connection, bool &connection_close) {
3740 return process_request(strm, last_connection, connection_close, [&](Request &req) { req.ssl = ssl; });
3741 });
3742}
3743
3744// SSL HTTP client implementation
3745inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, const char *client_cert_path,
3746 const char *client_key_path)
3747 : Client(host, port, timeout_sec) {
3748 ctx_ = SSL_CTX_new(SSLv23_client_method());
3749
3750 detail::split(&host_[0], &host_[host_.size()], '.',
3751 [&](const char *b, const char *e) { host_components_.emplace_back(std::string(b, e)); });
3752 if (client_cert_path && client_key_path) {
3753 if (SSL_CTX_use_certificate_file(ctx_, client_cert_path, SSL_FILETYPE_PEM) != 1 ||
3754 SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != 1) {
3755 SSL_CTX_free(ctx_);
3756 ctx_ = nullptr;
3757 }
3758 }
3759}
3760
3761inline SSLClient::~SSLClient() {
3762 if (ctx_) {
3763 SSL_CTX_free(ctx_);
3764 }
3765}
3766
3767inline bool SSLClient::is_valid() const {
3768 return ctx_;
3769}
3770
3771inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path) {
3772 if (ca_cert_file_path) {
3773 ca_cert_file_path_ = ca_cert_file_path;
3774 }
3775 if (ca_cert_dir_path) {
3776 ca_cert_dir_path_ = ca_cert_dir_path;
3777 }
3778}
3779
3780inline void SSLClient::enable_server_certificate_verification(bool enabled) {
3781 server_certificate_verification_ = enabled;
3782}
3783
3784inline long SSLClient::get_openssl_verify_result() const {
3785 return verify_result_;
3786}
3787
3788inline SSL_CTX *SSLClient::ssl_context() const noexcept {
3789 return ctx_;
3790}
3791
3792inline bool SSLClient::process_and_close_socket(
3793 socket_t sock, size_t request_count,
3794 std::function<bool(Stream &strm, bool last_connection, bool &connection_close)> callback) {
3795
3796 request_count = std::min(request_count, keep_alive_max_count_);
3797
3798 return is_valid() && detail::process_and_close_socket_ssl(
3799 true, sock, request_count, read_timeout_sec_, read_timeout_usec_, ctx_, ctx_mutex_,
3800 [&](SSL *ssl) {
3801 if (ca_cert_file_path_.empty()) {
3802 SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
3803 } else {
3804 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), nullptr)) {
3805 return false;
3806 }
3807 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
3808 }
3809
3810 if (SSL_connect(ssl) != 1) {
3811 return false;
3812 }
3813
3814 if (server_certificate_verification_) {
3815 verify_result_ = SSL_get_verify_result(ssl);
3816
3817 if (verify_result_ != X509_V_OK) {
3818 return false;
3819 }
3820
3821 auto server_cert = SSL_get_peer_certificate(ssl);
3822
3823 if (server_cert == nullptr) {
3824 return false;
3825 }
3826
3827 if (!verify_host(server_cert)) {
3828 X509_free(server_cert);
3829 return false;
3830 }
3831 X509_free(server_cert);
3832 }
3833
3834 return true;
3835 },
3836 [&](SSL *ssl) {
3837 SSL_set_tlsext_host_name(ssl, host_.c_str());
3838 return true;
3839 },
3840 [&](SSL * /*ssl*/, Stream &strm, bool last_connection, bool &connection_close) {
3841 return callback(strm, last_connection, connection_close);
3842 });
3843}
3844
3845inline bool SSLClient::is_ssl() const {
3846 return true;
3847}
3848
3849inline bool SSLClient::verify_host(X509 *server_cert) const {
3850 /* Quote from RFC2818 section 3.1 "Server Identity"
3851
3852 If a subjectAltName extension of type dNSName is present, that MUST
3853 be used as the identity. Otherwise, the (most specific) Common Name
3854 field in the Subject field of the certificate MUST be used. Although
3855 the use of the Common Name is existing practice, it is deprecated and
3856 Certification Authorities are encouraged to use the dNSName instead.
3857
3858 Matching is performed using the matching rules specified by
3859 [RFC2459]. If more than one identity of a given type is present in
3860 the certificate (e.g., more than one dNSName name, a match in any one
3861 of the set is considered acceptable.) Names may contain the wildcard
3862 character * which is considered to match any single domain name
3863 component or component fragment. E.g., *.a.com matches foo.a.com but
3864 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
3865
3866 In some cases, the URI is specified as an IP address rather than a
3867 hostname. In this case, the iPAddress subjectAltName must be present
3868 in the certificate and must exactly match the IP in the URI.
3869
3870 */
3871 return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert);
3872}
3873
3874inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
3875 auto ret = false;
3876
3877 auto type = GEN_DNS;
3878
3879 struct in6_addr addr6;
3880 struct in_addr addr;
3881 size_t addr_len = 0;
3882
3883#ifndef __MINGW32__
3884 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
3885 type = GEN_IPADD;
3886 addr_len = sizeof(struct in6_addr);
3887 } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
3888 type = GEN_IPADD;
3889 addr_len = sizeof(struct in_addr);
3890 }
3891#endif
3892
3893 auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(
3894 X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));
3895
3896 if (alt_names) {
3897 auto dsn_matched = false;
3898 auto ip_mached = false;
3899
3900 auto count = sk_GENERAL_NAME_num(alt_names);
3901
3902 for (auto i = 0; i < count && !dsn_matched; i++) {
3903 auto val = sk_GENERAL_NAME_value(alt_names, i);
3904 if (val->type == type) {
3905 auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
3906 auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
3907
3908 if (strlen(name) == name_len) {
3909 switch (type) {
3910 case GEN_DNS:
3911 dsn_matched = check_host_name(name, name_len);
3912 break;
3913
3914 case GEN_IPADD:
3915 if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {
3916 ip_mached = true;
3917 }
3918 break;
3919 }
3920 }
3921 }
3922 }
3923
3924 if (dsn_matched || ip_mached) {
3925 ret = true;
3926 }
3927 }
3928
3929 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
3930
3931 return ret;
3932}
3933
3934inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
3935 const auto subject_name = X509_get_subject_name(server_cert);
3936
3937 if (subject_name != nullptr) {
3938 char name[BUFSIZ];
3939 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name));
3940
3941 if (name_len != -1) {
3942 return check_host_name(name, name_len);
3943 }
3944 }
3945
3946 return false;
3947}
3948
3949inline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const {
3950 if (host_.size() == pattern_len && host_ == pattern) {
3951 return true;
3952 }
3953
3954 // Wildcard match
3955 // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
3956 std::vector<std::string> pattern_components;
3957 detail::split(&pattern[0], &pattern[pattern_len], '.',
3958 [&](const char *b, const char *e) { pattern_components.emplace_back(std::string(b, e)); });
3959
3960 if (host_components_.size() != pattern_components.size()) {
3961 return false;
3962 }
3963
3964 auto itr = pattern_components.begin();
3965 for (const auto &h : host_components_) {
3966 auto &p = *itr;
3967 if (p != h && p != "*") {
3968 auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h));
3969 if (!partial_match) {
3970 return false;
3971 }
3972 }
3973 ++itr;
3974 }
3975
3976 return true;
3977}
3978#endif
3979
3980} // namespace httplib
3981
3982#endif // CPPHTTPLIB_HTTPLIB_H
3983