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