8 #ifndef CPPHTTPLIB_HTTPLIB_H
9 #define CPPHTTPLIB_HTTPLIB_H
12 #ifndef _CRT_SECURE_NO_WARNINGS
13 #define _CRT_SECURE_NO_WARNINGS
16 #ifndef _CRT_NONSTDC_NO_DEPRECATE
17 #define _CRT_NONSTDC_NO_DEPRECATE
20 #if defined(_MSC_VER) && _MSC_VER < 1900
21 #define snprintf _snprintf_s
25 #define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
29 #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
40 #pragma comment(lib, "ws2_32.lib")
43 #define strcasecmp _stricmp
48 #include <arpa/inet.h>
51 #include <netinet/in.h>
54 #include <sys/select.h>
55 #include <sys/socket.h>
59 #define INVALID_SOCKET (-1)
74 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
75 #include <openssl/err.h>
76 #include <openssl/ssl.h>
77 #include <openssl/x509v3.h>
80 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
87 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
88 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
89 #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
90 #define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
91 #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH std::numeric_limits<size_t>::max()
98 bool operator()(
const std::string &s1,
const std::string &s2)
const {
99 return std::lexicographical_compare(
100 s1.begin(), s1.end(), s2.begin(), s2.end(),
101 [](
char c1,
char c2) { return ::tolower(c1) < ::tolower(c2); });
107 enum class HttpVersion { v1_0 = 0, v1_1 };
109 typedef std::multimap<std::string, std::string, detail::ci> Headers;
111 template <
typename uint64_t,
typename... Args>
112 std::pair<std::string, std::string> make_range_header(
uint64_t value,
115 typedef std::map<std::string, std::string> Params;
116 typedef std::smatch Match;
119 struct MultipartFile {
120 std::string filename;
121 std::string content_type;
125 typedef std::multimap<std::string, MultipartFile> MultipartFiles;
135 MultipartFiles files;
140 bool has_header(
const char *key)
const;
141 std::string get_header_value(
const char *key,
size_t id = 0)
const;
142 size_t get_header_value_count(
const char *key)
const;
143 void set_header(
const char *key,
const char *val);
145 bool has_param(
const char *key)
const;
146 std::string get_param_value(
const char *key,
size_t id = 0)
const;
147 size_t get_param_value_count(
const char *key)
const;
149 bool has_file(
const char *key)
const;
150 MultipartFile get_file_value(
const char *key)
const;
158 std::function<std::string(
uint64_t offset)> streamcb;
160 bool has_header(
const char *key)
const;
161 std::string get_header_value(
const char *key,
size_t id = 0)
const;
162 size_t get_header_value_count(
const char *key)
const;
163 void set_header(
const char *key,
const char *val);
165 void set_redirect(
const char *uri);
166 void set_content(
const char *s,
size_t n,
const char *content_type);
167 void set_content(
const std::string &s,
const char *content_type);
169 Response() : status(-1) {}
175 virtual int read(
char *ptr,
size_t size) = 0;
176 virtual int write(
const char *ptr,
size_t size1) = 0;
177 virtual int write(
const char *ptr) = 0;
178 virtual std::string get_remote_addr()
const = 0;
180 template <
typename... Args>
181 void write_format(
const char *fmt,
const Args &... args);
184 class SocketStream :
public Stream {
187 virtual ~SocketStream();
189 virtual int read(
char *ptr,
size_t size);
190 virtual int write(
const char *ptr,
size_t size);
191 virtual int write(
const char *ptr);
192 virtual std::string get_remote_addr()
const;
198 class BufferStream :
public Stream {
201 virtual ~BufferStream() {}
203 virtual int read(
char *ptr,
size_t size);
204 virtual int write(
const char *ptr,
size_t size);
205 virtual int write(
const char *ptr);
206 virtual std::string get_remote_addr()
const;
208 const std::string &get_buffer()
const;
216 typedef std::function<void(
const Request &, Response &)>
Handler;
217 typedef std::function<void(
const Request &,
const Response &)> Logger;
223 virtual bool is_valid()
const;
225 Server &Get(
const char *pattern,
Handler handler);
226 Server &Post(
const char *pattern,
Handler handler);
228 Server &Put(
const char *pattern,
Handler handler);
229 Server &Patch(
const char *pattern,
Handler handler);
230 Server &Delete(
const char *pattern,
Handler handler);
231 Server &Options(
const char *pattern,
Handler handler);
233 bool set_base_dir(
const char *path);
235 void set_error_handler(
Handler handler);
236 void set_logger(Logger logger);
238 void set_keep_alive_max_count(
size_t count);
239 void set_payload_max_length(
uint64_t length);
241 int bind_to_any_port(
const char *host,
int socket_flags = 0);
242 bool listen_after_bind();
244 bool listen(
const char *host,
int port,
int socket_flags = 0);
246 bool is_running()
const;
250 bool process_request(
Stream &strm,
bool last_connection,
251 bool &connection_close);
253 size_t keep_alive_max_count_;
254 size_t payload_max_length_;
257 typedef std::vector<std::pair<std::regex, Handler>> Handlers;
259 socket_t create_server_socket(
const char *host,
int port,
260 int socket_flags)
const;
261 int bind_internal(
const char *host,
int port,
int socket_flags);
262 bool listen_internal();
264 bool routing(Request &req, Response &res);
265 bool handle_file_request(Request &req, Response &res);
266 bool dispatch_request(Request &req, Response &res, Handlers &handlers);
268 bool parse_request_line(
const char *s, Request &req);
269 void write_response(
Stream &strm,
bool last_connection,
const Request &req,
272 virtual bool read_and_close_socket(
socket_t sock);
276 std::string base_dir_;
277 Handlers get_handlers_;
278 Handlers post_handlers_;
279 Handlers put_handlers_;
280 Handlers patch_handlers_;
281 Handlers delete_handlers_;
282 Handlers options_handlers_;
287 std::mutex running_threads_mutex_;
288 int running_threads_;
293 Client(
const char *host,
int port = 80, time_t timeout_sec = 300);
297 virtual bool is_valid()
const;
299 std::shared_ptr<Response> Get(
const char *path, Progress progress =
nullptr);
300 std::shared_ptr<Response> Get(
const char *path,
const Headers &headers,
301 Progress progress =
nullptr);
303 std::shared_ptr<Response> Head(
const char *path);
304 std::shared_ptr<Response> Head(
const char *path,
const Headers &headers);
306 std::shared_ptr<Response> Post(
const char *path,
const std::string &body,
307 const char *content_type);
308 std::shared_ptr<Response> Post(
const char *path,
const Headers &headers,
309 const std::string &body,
310 const char *content_type);
312 std::shared_ptr<Response> Post(
const char *path,
const Params ¶ms);
313 std::shared_ptr<Response> Post(
const char *path,
const Headers &headers,
314 const Params ¶ms);
316 std::shared_ptr<Response> Put(
const char *path,
const std::string &body,
317 const char *content_type);
318 std::shared_ptr<Response> Put(
const char *path,
const Headers &headers,
319 const std::string &body,
320 const char *content_type);
322 std::shared_ptr<Response> Patch(
const char *path,
const std::string &body,
323 const char *content_type);
324 std::shared_ptr<Response> Patch(
const char *path,
const Headers &headers,
325 const std::string &body,
326 const char *content_type);
328 std::shared_ptr<Response> Delete(
const char *path,
329 const std::string &body = std::string(),
330 const char *content_type =
nullptr);
331 std::shared_ptr<Response> Delete(
const char *path,
const Headers &headers,
332 const std::string &body = std::string(),
333 const char *content_type =
nullptr);
335 std::shared_ptr<Response> Options(
const char *path);
336 std::shared_ptr<Response> Options(
const char *path,
const Headers &headers);
338 bool send(Request &req, Response &res);
341 bool process_request(
Stream &strm, Request &req, Response &res,
342 bool &connection_close);
344 const std::string host_;
347 const std::string host_and_port_;
350 socket_t create_client_socket()
const;
351 bool read_response_line(
Stream &strm, Response &res);
352 void write_request(
Stream &strm, Request &req);
354 virtual bool read_and_close_socket(
socket_t sock, Request &req,
356 virtual bool is_ssl()
const;
359 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
360 class SSLSocketStream :
public Stream {
362 SSLSocketStream(
socket_t sock, SSL *ssl);
363 virtual ~SSLSocketStream();
365 virtual int read(
char *ptr,
size_t size);
366 virtual int write(
const char *ptr,
size_t size);
367 virtual int write(
const char *ptr);
368 virtual std::string get_remote_addr()
const;
375 class SSLServer :
public Server {
377 SSLServer(
const char *cert_path,
const char *private_key_path);
379 virtual ~SSLServer();
381 virtual bool is_valid()
const;
384 virtual bool read_and_close_socket(
socket_t sock);
387 std::mutex ctx_mutex_;
390 class SSLClient :
public Client {
392 SSLClient(
const char *host,
int port = 443, time_t timeout_sec = 300);
394 virtual ~SSLClient();
396 virtual bool is_valid()
const;
398 void set_ca_cert_path(
const char *ca_cert_path);
399 void enable_server_certificate_verification(
bool enabled);
401 long get_openssl_verify_result()
const;
404 virtual bool read_and_close_socket(
socket_t sock, Request &req,
406 virtual bool is_ssl()
const;
408 bool verify_host(X509 *server_cert)
const;
409 bool verify_host_with_subject_alt_name(X509 *server_cert)
const;
410 bool verify_host_with_common_name(X509 *server_cert)
const;
411 bool check_host_name(
const char *pattern,
size_t pattern_len)
const;
414 std::mutex ctx_mutex_;
415 std::vector<std::string> host_components_;
416 std::string ca_cert_path_;
417 bool server_certificate_verification_ =
false;
418 long verify_result_ = 0;
427 template <
class Fn>
void split(
const char *b,
const char *e,
char d, Fn fn) {
431 while (e ? (b + i != e) : (b[i] !=
'\0')) {
439 if (i) { fn(&b[beg], &b[i]); }
444 class stream_line_reader {
446 stream_line_reader(
Stream &strm,
char *fixed_buffer,
size_t fixed_buffer_size)
447 : strm_(strm), fixed_buffer_(fixed_buffer),
448 fixed_buffer_size_(fixed_buffer_size) {}
450 const char *ptr()
const {
451 if (glowable_buffer_.empty()) {
452 return fixed_buffer_;
454 return glowable_buffer_.data();
458 size_t size()
const {
459 if (glowable_buffer_.empty()) {
460 return fixed_buffer_used_size_;
462 return glowable_buffer_.size();
467 fixed_buffer_used_size_ = 0;
468 glowable_buffer_.clear();
470 for (
size_t i = 0;; i++) {
472 auto n = strm_.read(&
byte, 1);
486 if (
byte ==
'\n') {
break; }
493 void append(
char c) {
494 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
495 fixed_buffer_[fixed_buffer_used_size_++] = c;
496 fixed_buffer_[fixed_buffer_used_size_] =
'\0';
498 if (glowable_buffer_.empty()) {
499 assert(fixed_buffer_[fixed_buffer_used_size_] ==
'\0');
500 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
502 glowable_buffer_ += c;
508 const size_t fixed_buffer_size_;
509 size_t fixed_buffer_used_size_;
510 std::string glowable_buffer_;
513 inline int close_socket(
socket_t sock) {
515 return closesocket(sock);
521 inline int select_read(
socket_t sock, time_t sec, time_t usec) {
527 tv.tv_sec =
static_cast<long>(sec);
528 tv.tv_usec =
static_cast<long>(usec);
530 return select(
static_cast<int>(sock + 1), &fds,
nullptr,
nullptr, &tv);
533 inline bool wait_until_socket_is_ready(
socket_t sock, time_t sec, time_t usec) {
542 tv.tv_sec =
static_cast<long>(sec);
543 tv.tv_usec =
static_cast<long>(usec);
545 if (select(
static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) < 0) {
547 }
else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) {
549 socklen_t len =
sizeof(error);
550 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (
char *)&error, &len) < 0 ||
561 template <
typename T>
562 inline bool read_and_close_socket(
socket_t sock,
size_t keep_alive_max_count,
566 if (keep_alive_max_count > 0) {
567 auto count = keep_alive_max_count;
571 SocketStream strm(sock);
572 auto last_connection = count == 1;
573 auto connection_close =
false;
575 ret = callback(strm, last_connection, connection_close);
576 if (!ret || connection_close) {
break; }
581 SocketStream strm(sock);
582 auto dummy_connection_close =
false;
583 ret = callback(strm,
true, dummy_connection_close);
590 inline int shutdown_socket(
socket_t sock) {
592 return shutdown(sock, SD_BOTH);
594 return shutdown(sock, SHUT_RDWR);
598 template <
typename Fn>
599 socket_t create_socket(
const char *host,
int port, Fn fn,
600 int socket_flags = 0) {
602 #define SO_SYNCHRONOUS_NONALERT 0x20
603 #define SO_OPENTYPE 0x7008
605 int opt = SO_SYNCHRONOUS_NONALERT;
611 struct addrinfo hints;
612 struct addrinfo *result;
614 memset(&hints, 0,
sizeof(
struct addrinfo));
615 hints.ai_family = AF_UNSPEC;
616 hints.ai_socktype = SOCK_STREAM;
617 hints.ai_flags = socket_flags;
618 hints.ai_protocol = 0;
622 if (getaddrinfo(host, service.c_str(), &hints, &result)) {
626 for (
auto rp = result; rp; rp = rp->ai_next) {
628 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
633 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (
char *)&
yes,
sizeof(
yes));
637 freeaddrinfo(result);
644 freeaddrinfo(result);
648 inline void set_nonblocking(
socket_t sock,
bool nonblocking) {
650 auto flags = nonblocking ? 1UL : 0UL;
651 ioctlsocket(sock, FIONBIO, &flags);
653 auto flags = fcntl(sock, F_GETFL, 0);
655 nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
659 inline bool is_connection_error() {
661 return WSAGetLastError() != WSAEWOULDBLOCK;
663 return errno != EINPROGRESS;
667 inline std::string get_remote_addr(
socket_t sock) {
668 struct sockaddr_storage addr;
669 socklen_t len =
sizeof(addr);
671 if (!getpeername(sock, (
struct sockaddr *)&addr, &len)) {
672 char ipstr[NI_MAXHOST];
674 if (!getnameinfo((
struct sockaddr *)&addr, len, ipstr,
sizeof(ipstr),
675 nullptr, 0, NI_NUMERICHOST)) {
680 return std::string();
683 inline bool is_file(
const std::string &path) {
685 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
688 inline bool is_dir(
const std::string &path) {
690 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
693 inline bool is_valid_path(
const std::string &path) {
698 while (i < path.size() && path[i] ==
'/') {
702 while (i < path.size()) {
705 while (i < path.size() && path[i] !=
'/') {
712 if (!path.compare(beg, len,
".")) {
714 }
else if (!path.compare(beg, len,
"..")) {
715 if (level == 0) {
return false; }
722 while (i < path.size() && path[i] ==
'/') {
730 inline void read_file(
const std::string &path, std::string &out) {
731 std::ifstream fs(path, std::ios_base::binary);
732 fs.seekg(0, std::ios_base::end);
733 auto size = fs.tellg();
735 out.resize(
static_cast<size_t>(size));
736 fs.read(&out[0], size);
739 inline std::string file_extension(
const std::string &path) {
741 auto pat = std::regex(
"\\.([a-zA-Z0-9]+)$");
742 if (std::regex_search(path, m, pat)) {
return m[1].str(); }
743 return std::string();
746 inline const char *find_content_type(
const std::string &path) {
747 auto ext = file_extension(path);
750 }
else if (ext ==
"html") {
752 }
else if (ext ==
"css") {
754 }
else if (ext ==
"jpeg" || ext ==
"jpg") {
756 }
else if (ext ==
"png") {
758 }
else if (ext ==
"gif") {
760 }
else if (ext ==
"svg") {
761 return "image/svg+xml";
762 }
else if (ext ==
"ico") {
763 return "image/x-icon";
764 }
else if (ext ==
"json") {
765 return "application/json";
766 }
else if (ext ==
"pdf") {
767 return "application/pdf";
768 }
else if (ext ==
"js") {
769 return "application/javascript";
770 }
else if (ext ==
"xml") {
771 return "application/xml";
772 }
else if (ext ==
"xhtml") {
773 return "application/xhtml+xml";
778 inline const char *status_message(
int status) {
780 case 200:
return "OK";
781 case 301:
return "Moved Permanently";
782 case 302:
return "Found";
783 case 303:
return "See Other";
784 case 304:
return "Not Modified";
785 case 400:
return "Bad Request";
786 case 403:
return "Forbidden";
787 case 404:
return "Not Found";
788 case 413:
return "Payload Too Large";
789 case 414:
return "Request-URI Too Long";
790 case 415:
return "Unsupported Media Type";
792 case 500:
return "Internal Server Error";
796 inline bool has_header(
const Headers &headers,
const char *key) {
797 return headers.find(key) != headers.end();
800 inline const char *get_header_value(
const Headers &headers,
const char *key,
801 size_t id = 0,
const char *def =
nullptr) {
802 auto it = headers.find(key);
803 std::advance(it,
id);
804 if (it != headers.end()) {
return it->second.c_str(); }
808 inline uint64_t get_header_value_uint64(
const Headers &headers,
const char *key,
810 auto it = headers.find(key);
811 if (it != headers.end()) {
812 return std::strtoull(it->second.data(),
nullptr, 10);
817 inline bool read_headers(
Stream &strm, Headers &headers) {
818 static std::regex re(R
"((.+?):\s*(.+?)\s*\r\n)");
820 const auto bufsiz = 2048;
823 stream_line_reader reader(strm, buf, bufsiz);
826 if (!reader.getline()) {
return false; }
827 if (!strcmp(reader.ptr(),
"\r\n")) {
break; }
829 if (std::regex_match(reader.ptr(), m, re)) {
830 auto key = std::string(m[1]);
831 auto val = std::string(m[2]);
832 headers.emplace(key, val);
839 inline bool read_content_with_length(
Stream &strm, std::string &out,
size_t len,
844 auto n = strm.read(&out[r], len - r);
845 if (n <= 0) {
return false; }
850 if (!progress(r, len)) {
return false; }
857 inline void skip_content_with_length(
Stream &strm,
size_t len) {
861 auto n = strm.read(buf, BUFSIZ);
862 if (n <= 0) {
return; }
867 inline bool read_content_without_length(
Stream &strm, std::string &out) {
870 auto n = strm.read(&
byte, 1);
882 inline bool read_content_chunked(
Stream &strm, std::string &out) {
883 const auto bufsiz = 16;
886 stream_line_reader reader(strm, buf, bufsiz);
888 if (!reader.getline()) {
return false; }
890 auto chunk_len = std::stoi(reader.ptr(), 0, 16);
892 while (chunk_len > 0) {
894 if (!read_content_with_length(strm, chunk, chunk_len,
nullptr)) {
898 if (!reader.getline()) {
return false; }
900 if (strcmp(reader.ptr(),
"\r\n")) {
break; }
904 if (!reader.getline()) {
return false; }
906 chunk_len = std::stoi(reader.ptr(), 0, 16);
909 if (chunk_len == 0) {
911 if (!reader.getline() || strcmp(reader.ptr(),
"\r\n"))
return false;
917 template <
typename T>
918 bool read_content(
Stream &strm, T &x,
uint64_t payload_max_length,
919 bool &exceed_payload_max_length,
920 Progress progress = Progress()) {
921 if (has_header(x.headers,
"Content-Length")) {
922 auto len = get_header_value_uint64(x.headers,
"Content-Length", 0);
924 const auto &encoding =
925 get_header_value(x.headers,
"Transfer-Encoding", 0,
"");
926 if (!strcasecmp(encoding,
"chunked")) {
927 return read_content_chunked(strm, x.body);
931 if ((len > payload_max_length) ||
933 (
sizeof(
size_t) <
sizeof(
uint64_t) &&
934 len > std::numeric_limits<size_t>::max())) {
935 exceed_payload_max_length =
true;
936 skip_content_with_length(strm, len);
940 return read_content_with_length(strm, x.body, len, progress);
942 const auto &encoding =
943 get_header_value(x.headers,
"Transfer-Encoding", 0,
"");
944 if (!strcasecmp(encoding,
"chunked")) {
945 return read_content_chunked(strm, x.body);
947 return read_content_without_length(strm, x.body);
952 template <
typename T>
inline void write_headers(
Stream &strm,
const T &info) {
953 for (
const auto &x : info.headers) {
954 strm.write_format(
"%s: %s\r\n", x.first.c_str(), x.second.c_str());
959 inline std::string encode_url(
const std::string &s) {
962 for (
auto i = 0; s[i]; i++) {
964 case ' ': result +=
"%20";
break;
965 case '+': result +=
"%2B";
break;
966 case '\r': result +=
"%0D";
break;
967 case '\n': result +=
"%0A";
break;
968 case '\'': result +=
"%27";
break;
969 case ',': result +=
"%2C";
break;
970 case ':': result +=
"%3A";
break;
971 case ';': result +=
"%3B";
break;
973 auto c =
static_cast<uint8_t>(s[i]);
977 size_t len = snprintf(hex,
sizeof(hex) - 1,
"%02X", c);
979 result.append(hex, len);
990 inline bool is_hex(
char c,
int &v) {
991 if (0x20 <= c && isdigit(c)) {
994 }
else if (
'A' <= c && c <=
'F') {
997 }
else if (
'a' <= c && c <=
'f') {
1004 inline bool from_hex_to_i(
const std::string &s,
size_t i,
size_t cnt,
1006 if (i >= s.size()) {
return false; }
1009 for (; cnt; i++, cnt--) {
1010 if (!s[i]) {
return false; }
1012 if (is_hex(s[i], v)) {
1021 inline std::string from_i_to_hex(
uint64_t n) {
1022 const char *charset =
"0123456789abcdef";
1025 ret = charset[n & 15] + ret;
1031 inline size_t to_utf8(
int code,
char *buff) {
1032 if (code < 0x0080) {
1033 buff[0] = (code & 0x7F);
1035 }
else if (code < 0x0800) {
1036 buff[0] = (0xC0 | ((code >> 6) & 0x1F));
1037 buff[1] = (0x80 | (code & 0x3F));
1039 }
else if (code < 0xD800) {
1040 buff[0] = (0xE0 | ((code >> 12) & 0xF));
1041 buff[1] = (0x80 | ((code >> 6) & 0x3F));
1042 buff[2] = (0x80 | (code & 0x3F));
1044 }
else if (code < 0xE000) {
1046 }
else if (code < 0x10000) {
1047 buff[0] = (0xE0 | ((code >> 12) & 0xF));
1048 buff[1] = (0x80 | ((code >> 6) & 0x3F));
1049 buff[2] = (0x80 | (code & 0x3F));
1051 }
else if (code < 0x110000) {
1052 buff[0] = (0xF0 | ((code >> 18) & 0x7));
1053 buff[1] = (0x80 | ((code >> 12) & 0x3F));
1054 buff[2] = (0x80 | ((code >> 6) & 0x3F));
1055 buff[3] = (0x80 | (code & 0x3F));
1063 inline std::string decode_url(
const std::string &s) {
1066 for (
size_t i = 0; i < s.size(); i++) {
1067 if (s[i] ==
'%' && i + 1 < s.size()) {
1068 if (s[i + 1] ==
'u') {
1070 if (from_hex_to_i(s, i + 2, 4, val)) {
1073 size_t len = to_utf8(val, buff);
1074 if (len > 0) { result.append(buff, len); }
1081 if (from_hex_to_i(s, i + 1, 2, val)) {
1089 }
else if (s[i] ==
'+') {
1099 inline void parse_query_text(
const std::string &s, Params ¶ms) {
1100 split(&s[0], &s[s.size()],
'&', [&](
const char *b,
const char *e) {
1103 split(b, e,
'=', [&](const char *b, const char *e) {
1110 params.emplace(key, decode_url(val));
1114 inline bool parse_multipart_boundary(
const std::string &content_type,
1115 std::string &boundary) {
1116 auto pos = content_type.find(
"boundary=");
1117 if (
pos == std::string::npos) {
return false; }
1119 boundary = content_type.substr(
pos + 9);
1123 inline bool parse_multipart_formdata(
const std::string &boundary,
1124 const std::string &body,
1125 MultipartFiles &files) {
1126 static std::string dash =
"--";
1127 static std::string crlf =
"\r\n";
1129 static std::regex re_content_type(
"Content-Type: (.*?)",
1130 std::regex_constants::icase);
1132 static std::regex re_content_disposition(
1133 "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?",
1134 std::regex_constants::icase);
1136 auto dash_boundary = dash + boundary;
1138 auto pos = body.find(dash_boundary);
1139 if (
pos != 0) {
return false; }
1141 pos += dash_boundary.size();
1143 auto next_pos = body.find(crlf,
pos);
1144 if (next_pos == std::string::npos) {
return false; }
1146 pos = next_pos + crlf.size();
1148 while (
pos < body.size()) {
1149 next_pos = body.find(crlf,
pos);
1150 if (next_pos == std::string::npos) {
return false; }
1155 auto header = body.substr(
pos, (next_pos -
pos));
1157 while (
pos != next_pos) {
1159 if (std::regex_match(header, m, re_content_type)) {
1160 file.content_type = m[1];
1161 }
else if (std::regex_match(header, m, re_content_disposition)) {
1163 file.filename = m[2];
1166 pos = next_pos + crlf.size();
1168 next_pos = body.find(crlf,
pos);
1169 if (next_pos == std::string::npos) {
return false; }
1171 header = body.substr(
pos, (next_pos -
pos));
1174 pos = next_pos + crlf.size();
1176 next_pos = body.find(crlf + dash_boundary,
pos);
1178 if (next_pos == std::string::npos) {
return false; }
1181 file.length = next_pos -
pos;
1183 pos = next_pos + crlf.size() + dash_boundary.size();
1185 next_pos = body.find(crlf,
pos);
1186 if (next_pos == std::string::npos) {
return false; }
1188 files.emplace(name, file);
1190 pos = next_pos + crlf.size();
1196 inline std::string to_lower(
const char *beg,
const char *end) {
1200 out += ::tolower(*it);
1206 inline void make_range_header_core(std::string &) {}
1208 template <
typename u
int64_t>
1209 inline void make_range_header_core(std::string &field,
uint64_t value) {
1210 if (!field.empty()) { field +=
", "; }
1214 template <
typename uint64_t,
typename... Args>
1215 inline void make_range_header_core(std::string &field,
uint64_t value1,
1217 if (!field.empty()) { field +=
", "; }
1219 make_range_header_core(field, args...);
1222 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1223 inline bool can_compress(
const std::string &content_type) {
1224 return !content_type.find(
"text/") || content_type ==
"image/svg+xml" ||
1225 content_type ==
"application/javascript" ||
1226 content_type ==
"application/json" ||
1227 content_type ==
"application/xml" ||
1228 content_type ==
"application/xhtml+xml";
1231 inline void compress(std::string &content) {
1239 if (ret !=
Z_OK) {
return; }
1244 std::string compressed;
1246 const auto bufsiz = 16384;
1252 compressed.append(buff, bufsiz - strm.
avail_out);
1255 content.swap(compressed);
1260 inline void decompress(std::string &content) {
1270 if (ret !=
Z_OK) {
return; }
1275 std::string decompressed;
1277 const auto bufsiz = 16384;
1283 decompressed.append(buff, bufsiz - strm.
avail_out);
1286 content.swap(decompressed);
1297 WSAStartup(0x0002, &wsaData);
1300 ~WSInit() { WSACleanup(); }
1303 static WSInit wsinit_;
1309 template <
typename uint64_t,
typename... Args>
1310 inline std::pair<std::string, std::string> make_range_header(
uint64_t value,
1313 detail::make_range_header_core(field,
value, args...);
1314 field.insert(0,
"bytes=");
1315 return std::make_pair(
"Range", field);
1319 inline bool Request::has_header(
const char *key)
const {
1320 return detail::has_header(headers, key);
1323 inline std::string Request::get_header_value(
const char *key,
size_t id)
const {
1324 return detail::get_header_value(headers, key,
id,
"");
1327 inline size_t Request::get_header_value_count(
const char *key)
const {
1328 auto r = headers.equal_range(key);
1329 return std::distance(r.first, r.second);
1332 inline void Request::set_header(
const char *key,
const char *val) {
1333 headers.emplace(key, val);
1336 inline bool Request::has_param(
const char *key)
const {
1337 return params.find(key) != params.end();
1340 inline std::string Request::get_param_value(
const char *key,
size_t id)
const {
1341 auto it = params.find(key);
1342 std::advance(it,
id);
1343 if (it != params.end()) {
return it->second; }
1344 return std::string();
1347 inline size_t Request::get_param_value_count(
const char *key)
const {
1348 auto r = params.equal_range(key);
1349 return std::distance(r.first, r.second);
1352 inline bool Request::has_file(
const char *key)
const {
1353 return files.find(key) != files.end();
1356 inline MultipartFile Request::get_file_value(
const char *key)
const {
1357 auto it = files.find(key);
1358 if (it != files.end()) {
return it->second; }
1359 return MultipartFile();
1363 inline bool Response::has_header(
const char *key)
const {
1364 return headers.find(key) != headers.end();
1367 inline std::string Response::get_header_value(
const char *key,
1369 return detail::get_header_value(headers, key,
id,
"");
1372 inline size_t Response::get_header_value_count(
const char *key)
const {
1373 auto r = headers.equal_range(key);
1374 return std::distance(r.first, r.second);
1377 inline void Response::set_header(
const char *key,
const char *val) {
1378 headers.emplace(key, val);
1381 inline void Response::set_redirect(
const char *url) {
1382 set_header(
"Location", url);
1386 inline void Response::set_content(
const char *s,
size_t n,
1387 const char *content_type) {
1389 set_header(
"Content-Type", content_type);
1392 inline void Response::set_content(
const std::string &s,
1393 const char *content_type) {
1395 set_header(
"Content-Type", content_type);
1399 template <
typename... Args>
1400 inline void Stream::write_format(
const char *fmt,
const Args &... args) {
1401 const auto bufsiz = 2048;
1404 #if defined(_MSC_VER) && _MSC_VER < 1900
1405 auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...);
1407 auto n = snprintf(buf, bufsiz - 1, fmt, args...);
1410 if (n >= bufsiz - 1) {
1411 std::vector<char> glowable_buf(bufsiz);
1413 while (n >=
static_cast<int>(glowable_buf.size() - 1)) {
1414 glowable_buf.resize(glowable_buf.size() * 2);
1415 #if defined(_MSC_VER) && _MSC_VER < 1900
1416 n = _snprintf_s(&glowable_buf[0], glowable_buf.size(),
1417 glowable_buf.size() - 1, fmt, args...);
1419 n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
1422 write(&glowable_buf[0], n);
1430 inline SocketStream::SocketStream(
socket_t sock) : sock_(sock) {}
1432 inline SocketStream::~SocketStream() {}
1434 inline int SocketStream::read(
char *ptr,
size_t size) {
1435 return recv(sock_, ptr,
static_cast<int>(size), 0);
1438 inline int SocketStream::write(
const char *ptr,
size_t size) {
1439 return send(sock_, ptr,
static_cast<int>(size), 0);
1442 inline int SocketStream::write(
const char *ptr) {
1443 return write(ptr, strlen(ptr));
1446 inline std::string SocketStream::get_remote_addr()
const {
1447 return detail::get_remote_addr(sock_);
1451 inline int BufferStream::read(
char *ptr,
size_t size) {
1452 #if defined(_MSC_VER) && _MSC_VER < 1900
1453 return static_cast<int>(buffer._Copy_s(ptr, size, size));
1455 return static_cast<int>(buffer.copy(ptr, size));
1459 inline int BufferStream::write(
const char *ptr,
size_t size) {
1460 buffer.append(ptr, size);
1461 return static_cast<int>(size);
1464 inline int BufferStream::write(
const char *ptr) {
1465 size_t size = strlen(ptr);
1466 buffer.append(ptr, size);
1467 return static_cast<int>(size);
1470 inline std::string BufferStream::get_remote_addr()
const {
return ""; }
1472 inline const std::string &BufferStream::get_buffer()
const {
return buffer; }
1475 inline Server::Server()
1480 signal(SIGPIPE, SIG_IGN);
1484 inline Server::~Server() {}
1486 inline Server &Server::Get(
const char *pattern,
Handler handler) {
1487 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1491 inline Server &Server::Post(
const char *pattern,
Handler handler) {
1492 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1496 inline Server &Server::Put(
const char *pattern,
Handler handler) {
1497 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1501 inline Server &Server::Patch(
const char *pattern,
Handler handler) {
1502 patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1506 inline Server &Server::Delete(
const char *pattern,
Handler handler) {
1507 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1511 inline Server &Server::Options(
const char *pattern,
Handler handler) {
1512 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1516 inline bool Server::set_base_dir(
const char *path) {
1517 if (detail::is_dir(path)) {
1524 inline void Server::set_error_handler(
Handler handler) {
1525 error_handler_ = handler;
1528 inline void Server::set_logger(Logger logger) { logger_ = logger; }
1530 inline void Server::set_keep_alive_max_count(
size_t count) {
1531 keep_alive_max_count_ = count;
1534 inline void Server::set_payload_max_length(
uint64_t length) {
1535 payload_max_length_ = length;
1538 inline int Server::bind_to_any_port(
const char *host,
int socket_flags) {
1539 return bind_internal(host, 0, socket_flags);
1542 inline bool Server::listen_after_bind() {
return listen_internal(); }
1544 inline bool Server::listen(
const char *host,
int port,
int socket_flags) {
1545 if (bind_internal(host, port, socket_flags) < 0)
return false;
1546 return listen_internal();
1549 inline bool Server::is_running()
const {
return is_running_; }
1551 inline void Server::stop() {
1554 auto sock = svr_sock_;
1556 detail::shutdown_socket(sock);
1557 detail::close_socket(sock);
1561 inline bool Server::parse_request_line(
const char *s, Request &req) {
1562 static std::regex re(
"(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS) "
1563 "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n");
1566 if (std::regex_match(s, m, re)) {
1567 req.version = std::string(m[5]);
1568 req.method = std::string(m[1]);
1569 req.target = std::string(m[2]);
1570 req.path = detail::decode_url(m[3]);
1573 auto len = std::distance(m[4].first, m[4].second);
1574 if (len > 0) { detail::parse_query_text(m[4], req.params); }
1582 inline void Server::write_response(
Stream &strm,
bool last_connection,
1583 const Request &req, Response &res) {
1584 assert(res.status != -1);
1586 if (400 <= res.status && error_handler_) { error_handler_(req, res); }
1589 strm.write_format(
"HTTP/1.1 %d %s\r\n", res.status,
1590 detail::status_message(res.status));
1593 if (last_connection || req.get_header_value(
"Connection") ==
"close") {
1594 res.set_header(
"Connection",
"close");
1597 if (!last_connection && req.get_header_value(
"Connection") ==
"Keep-Alive") {
1598 res.set_header(
"Connection",
"Keep-Alive");
1600 res.set_header(
"Access-Control-Allow-Origin",
"*") ;
1601 if (res.body.empty()) {
1602 if (!res.has_header(
"Content-Length")) {
1605 res.set_header(
"Transfer-Encoding",
"chunked");
1607 res.set_header(
"Content-Length",
"0");
1611 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1613 const auto &encodings = req.get_header_value(
"Accept-Encoding");
1614 if (encodings.find(
"gzip") != std::string::npos &&
1615 detail::can_compress(res.get_header_value(
"Content-Type"))) {
1616 detail::compress(res.body);
1617 res.set_header(
"Content-Encoding",
"gzip");
1621 if (!res.has_header(
"Content-Type")) {
1622 res.set_header(
"Content-Type",
"text/plain");
1626 res.set_header(
"Content-Length", length.c_str());
1629 detail::write_headers(strm, res);
1632 if (req.method !=
"HEAD") {
1633 if (!res.body.empty()) {
1634 strm.write(res.body.c_str(), res.body.size());
1635 }
else if (res.streamcb) {
1636 bool chunked_response = !res.has_header(
"Content-Length");
1638 bool data_available =
true;
1639 while (data_available) {
1640 std::string chunk = res.streamcb(offset);
1641 offset += chunk.size();
1642 data_available = !chunk.empty();
1644 if (chunked_response)
1645 chunk = detail::from_i_to_hex(chunk.size()) +
"\r\n" + chunk +
"\r\n";
1646 if (strm.write(chunk.c_str(), chunk.size()) < 0)
break;
1652 if (logger_) { logger_(req, res); }
1655 inline bool Server::handle_file_request(Request &req, Response &res) {
1656 if (!base_dir_.empty() && detail::is_valid_path(req.path)) {
1657 std::string path = base_dir_ + req.path;
1659 if (!path.empty() && path.back() ==
'/') { path +=
"index.html"; }
1661 if (detail::is_file(path)) {
1662 detail::read_file(path, res.body);
1663 auto type = detail::find_content_type(path);
1664 if (
type) { res.set_header(
"Content-Type",
type); }
1673 inline socket_t Server::create_server_socket(
const char *host,
int port,
1674 int socket_flags)
const {
1675 return detail::create_socket(
1677 [](
socket_t sock,
struct addrinfo &ai) ->
bool {
1678 if (::bind(sock, ai.ai_addr,
static_cast<int>(ai.ai_addrlen))) {
1681 if (::listen(sock, 5)) {
1689 inline int Server::bind_internal(
const char *host,
int port,
int socket_flags) {
1690 if (!is_valid()) {
return -1; }
1692 svr_sock_ = create_server_socket(host, port, socket_flags);
1696 struct sockaddr_storage address;
1697 socklen_t len =
sizeof(address);
1698 if (getsockname(svr_sock_,
reinterpret_cast<struct sockaddr *
>(&address),
1702 if (address.ss_family == AF_INET) {
1703 return ntohs(
reinterpret_cast<struct sockaddr_in *
>(&address)->sin_port);
1704 }
else if (address.ss_family == AF_INET6) {
1706 reinterpret_cast<struct sockaddr_in6 *
>(&address)->sin6_port);
1715 inline bool Server::listen_internal() {
1721 auto val = detail::select_read(svr_sock_, 0, 100000);
1731 socket_t sock = accept(svr_sock_,
nullptr,
nullptr);
1735 detail::close_socket(svr_sock_);
1746 std::lock_guard<std::mutex> guard(running_threads_mutex_);
1750 read_and_close_socket(sock);
1753 std::lock_guard<std::mutex> guard(running_threads_mutex_);
1761 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1762 std::lock_guard<std::mutex> guard(running_threads_mutex_);
1763 if (!running_threads_) {
break; }
1766 is_running_ =
false;
1771 inline bool Server::routing(Request &req, Response &res) {
1772 if (req.method ==
"GET" && handle_file_request(req, res)) {
return true; }
1774 if (req.method ==
"GET" || req.method ==
"HEAD") {
1775 return dispatch_request(req, res, get_handlers_);
1776 }
else if (req.method ==
"POST") {
1777 return dispatch_request(req, res, post_handlers_);
1778 }
else if (req.method ==
"PUT") {
1779 return dispatch_request(req, res, put_handlers_);
1780 }
else if (req.method ==
"PATCH") {
1781 return dispatch_request(req, res, patch_handlers_);
1782 }
else if (req.method ==
"DELETE") {
1783 return dispatch_request(req, res, delete_handlers_);
1784 }
else if (req.method ==
"OPTIONS") {
1785 return dispatch_request(req, res, options_handlers_);
1790 inline bool Server::dispatch_request(Request &req, Response &res,
1791 Handlers &handlers) {
1792 for (
const auto &x : handlers) {
1793 const auto &pattern = x.first;
1794 const auto &handler = x.second;
1796 if (std::regex_match(req.path, req.matches, pattern)) {
1804 inline bool Server::process_request(
Stream &strm,
bool last_connection,
1805 bool &connection_close) {
1806 const auto bufsiz = 2048;
1809 detail::stream_line_reader reader(strm, buf, bufsiz);
1812 if (!reader.getline()) {
return false; }
1817 res.version =
"HTTP/1.1";
1822 write_response(strm, last_connection, req, res);
1827 if (!parse_request_line(reader.ptr(), req) ||
1828 !detail::read_headers(strm, req.headers)) {
1830 write_response(strm, last_connection, req, res);
1834 if (req.get_header_value(
"Connection") ==
"close") {
1835 connection_close =
true;
1838 req.set_header(
"REMOTE_ADDR", strm.get_remote_addr().c_str());
1841 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH") {
1842 bool exceed_payload_max_length =
false;
1843 if (!detail::read_content(strm, req, payload_max_length_,
1844 exceed_payload_max_length)) {
1845 res.status = exceed_payload_max_length ? 413 : 400;
1846 write_response(strm, last_connection, req, res);
1847 return !exceed_payload_max_length;
1850 const auto &content_type = req.get_header_value(
"Content-Type");
1852 if (req.get_header_value(
"Content-Encoding") ==
"gzip") {
1853 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
1854 detail::decompress(req.body);
1857 write_response(strm, last_connection, req, res);
1862 if (!content_type.find(
"application/x-www-form-urlencoded")) {
1863 detail::parse_query_text(req.body, req.params);
1864 }
else if (!content_type.find(
"multipart/form-data")) {
1865 std::string boundary;
1866 if (!detail::parse_multipart_boundary(content_type, boundary) ||
1867 !detail::parse_multipart_formdata(boundary, req.body, req.files)) {
1869 write_response(strm, last_connection, req, res);
1875 if (routing(req, res)) {
1876 if (res.status == -1) { res.status = 200; }
1881 write_response(strm, last_connection, req, res);
1885 inline bool Server::is_valid()
const {
return true; }
1887 inline bool Server::read_and_close_socket(
socket_t sock) {
1888 return detail::read_and_close_socket(
1889 sock, keep_alive_max_count_,
1890 [
this](
Stream &strm,
bool last_connection,
bool &connection_close) {
1891 return process_request(strm, last_connection, connection_close);
1896 inline Client::Client(
const char *host,
int port, time_t timeout_sec)
1897 : host_(host), port_(port), timeout_sec_(timeout_sec),
1900 inline Client::~Client() {}
1902 inline bool Client::is_valid()
const {
return true; }
1904 inline socket_t Client::create_client_socket()
const {
1905 return detail::create_socket(
1906 host_.c_str(), port_, [=](
socket_t sock,
struct addrinfo &ai) ->
bool {
1907 detail::set_nonblocking(sock, true);
1909 auto ret = connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
1911 if (detail::is_connection_error() ||
1912 !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) {
1913 detail::close_socket(sock);
1918 detail::set_nonblocking(sock,
false);
1923 inline bool Client::read_response_line(
Stream &strm, Response &res) {
1924 const auto bufsiz = 2048;
1927 detail::stream_line_reader reader(strm, buf, bufsiz);
1929 if (!reader.getline()) {
return false; }
1931 const static std::regex re(
"(HTTP/1\\.[01]) (\\d+?) .*\r\n");
1934 if (std::regex_match(reader.ptr(), m, re)) {
1935 res.version = std::string(m[1]);
1936 res.status = std::stoi(std::string(m[2]));
1942 inline bool Client::send(Request &req, Response &res) {
1943 if (req.path.empty()) {
return false; }
1945 auto sock = create_client_socket();
1948 return read_and_close_socket(sock, req, res);
1951 inline void Client::write_request(
Stream &strm, Request &req) {
1955 auto path = detail::encode_url(req.path);
1957 bstrm.write_format(
"%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
1960 if (!req.has_header(
"Host")) {
1963 req.set_header(
"Host", host_.c_str());
1965 req.set_header(
"Host", host_and_port_.c_str());
1969 req.set_header(
"Host", host_.c_str());
1971 req.set_header(
"Host", host_and_port_.c_str());
1976 if (!req.has_header(
"Accept")) { req.set_header(
"Accept",
"*/*"); }
1978 if (!req.has_header(
"User-Agent")) {
1979 req.set_header(
"User-Agent",
"cpp-httplib/0.2");
1984 req.set_header(
"Connection",
"close");
1987 if (req.body.empty()) {
1988 if (req.method ==
"POST" || req.method ==
"PUT" || req.method ==
"PATCH") {
1989 req.set_header(
"Content-Length",
"0");
1992 if (!req.has_header(
"Content-Type")) {
1993 req.set_header(
"Content-Type",
"text/plain");
1996 if (!req.has_header(
"Content-Length")) {
1998 req.set_header(
"Content-Length", length.c_str());
2002 detail::write_headers(bstrm, req);
2005 if (!req.body.empty()) { bstrm.write(req.body.c_str(), req.body.size()); }
2008 auto &data = bstrm.get_buffer();
2009 strm.write(data.data(), data.size());
2012 inline bool Client::process_request(
Stream &strm, Request &req, Response &res,
2013 bool &connection_close) {
2015 write_request(strm, req);
2018 if (!read_response_line(strm, res) ||
2019 !detail::read_headers(strm, res.headers)) {
2023 if (res.get_header_value(
"Connection") ==
"close" ||
2024 res.version ==
"HTTP/1.0") {
2025 connection_close =
true;
2029 if (req.method !=
"HEAD") {
2030 bool exceed_payload_max_length =
false;
2031 if (!detail::read_content(strm, res, std::numeric_limits<uint64_t>::max(),
2032 exceed_payload_max_length, req.progress)) {
2036 if (res.get_header_value(
"Content-Encoding") ==
"gzip") {
2037 #ifdef CPPHTTPLIB_ZLIB_SUPPORT
2038 detail::decompress(res.body);
2048 inline bool Client::read_and_close_socket(
socket_t sock, Request &req,
2050 return detail::read_and_close_socket(
2052 [&](
Stream &strm,
bool ,
bool &connection_close) {
2053 return process_request(strm, req, res, connection_close);
2057 inline bool Client::is_ssl()
const {
return false; }
2059 inline std::shared_ptr<Response> Client::Get(
const char *path,
2060 Progress progress) {
2061 return Get(path, Headers(), progress);
2064 inline std::shared_ptr<Response>
2065 Client::Get(
const char *path,
const Headers &headers, Progress progress) {
2069 req.headers = headers;
2070 req.progress = progress;
2072 auto res = std::make_shared<Response>();
2074 return send(req, *res) ? res :
nullptr;
2077 inline std::shared_ptr<Response> Client::Head(
const char *path) {
2078 return Head(path, Headers());
2081 inline std::shared_ptr<Response> Client::Head(
const char *path,
2082 const Headers &headers) {
2084 req.method =
"HEAD";
2085 req.headers = headers;
2088 auto res = std::make_shared<Response>();
2090 return send(req, *res) ? res :
nullptr;
2093 inline std::shared_ptr<Response> Client::Post(
const char *path,
2094 const std::string &body,
2095 const char *content_type) {
2096 return Post(path, Headers(), body, content_type);
2099 inline std::shared_ptr<Response> Client::Post(
const char *path,
2100 const Headers &headers,
2101 const std::string &body,
2102 const char *content_type) {
2104 req.method =
"POST";
2105 req.headers = headers;
2108 req.headers.emplace(
"Content-Type", content_type);
2111 auto res = std::make_shared<Response>();
2113 return send(req, *res) ? res :
nullptr;
2116 inline std::shared_ptr<Response> Client::Post(
const char *path,
2117 const Params ¶ms) {
2118 return Post(path, Headers(), params);
2121 inline std::shared_ptr<Response>
2122 Client::Post(
const char *path,
const Headers &headers,
const Params ¶ms) {
2124 for (
auto it = params.begin(); it != params.end(); ++it) {
2125 if (it != params.begin()) { query +=
"&"; }
2128 query += detail::encode_url(it->second);
2131 return Post(path, headers, query,
"application/x-www-form-urlencoded");
2134 inline std::shared_ptr<Response> Client::Put(
const char *path,
2135 const std::string &body,
2136 const char *content_type) {
2137 return Put(path, Headers(), body, content_type);
2140 inline std::shared_ptr<Response> Client::Put(
const char *path,
2141 const Headers &headers,
2142 const std::string &body,
2143 const char *content_type) {
2146 req.headers = headers;
2149 req.headers.emplace(
"Content-Type", content_type);
2152 auto res = std::make_shared<Response>();
2154 return send(req, *res) ? res :
nullptr;
2157 inline std::shared_ptr<Response> Client::Patch(
const char *path,
2158 const std::string &body,
2159 const char *content_type) {
2160 return Patch(path, Headers(), body, content_type);
2163 inline std::shared_ptr<Response> Client::Patch(
const char *path,
2164 const Headers &headers,
2165 const std::string &body,
2166 const char *content_type) {
2168 req.method =
"PATCH";
2169 req.headers = headers;
2172 req.headers.emplace(
"Content-Type", content_type);
2175 auto res = std::make_shared<Response>();
2177 return send(req, *res) ? res :
nullptr;
2180 inline std::shared_ptr<Response> Client::Delete(
const char *path,
2181 const std::string &body,
2182 const char *content_type) {
2183 return Delete(path, Headers(), body, content_type);
2186 inline std::shared_ptr<Response> Client::Delete(
const char *path,
2187 const Headers &headers,
2188 const std::string &body,
2189 const char *content_type) {
2191 req.method =
"DELETE";
2192 req.headers = headers;
2195 if (content_type) { req.headers.emplace(
"Content-Type", content_type); }
2198 auto res = std::make_shared<Response>();
2200 return send(req, *res) ? res :
nullptr;
2203 inline std::shared_ptr<Response> Client::Options(
const char *path) {
2204 return Options(path, Headers());
2207 inline std::shared_ptr<Response> Client::Options(
const char *path,
2208 const Headers &headers) {
2210 req.method =
"OPTIONS";
2212 req.headers = headers;
2214 auto res = std::make_shared<Response>();
2216 return send(req, *res) ? res :
nullptr;
2222 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2225 template <
typename U,
typename V,
typename T>
2227 read_and_close_socket_ssl(
socket_t sock,
size_t keep_alive_max_count,
2230 SSL_CTX *ctx, std::mutex &ctx_mutex,
2231 U SSL_connect_or_accept, V setup, T callback) {
2234 std::lock_guard<std::mutex> guard(ctx_mutex);
2237 if (!ssl) {
return false; }
2240 auto bio = BIO_new_socket(sock, BIO_NOCLOSE);
2241 SSL_set_bio(ssl, bio, bio);
2243 if (!setup(ssl)) {
return false; }
2247 if (SSL_connect_or_accept(ssl) == 1) {
2248 if (keep_alive_max_count > 0) {
2249 auto count = keep_alive_max_count;
2253 SSLSocketStream strm(sock, ssl);
2254 auto last_connection = count == 1;
2255 auto connection_close =
false;
2257 ret = callback(strm, last_connection, connection_close);
2258 if (!ret || connection_close) {
break; }
2263 SSLSocketStream strm(sock, ssl);
2264 auto dummy_connection_close =
false;
2265 ret = callback(strm,
true, dummy_connection_close);
2271 std::lock_guard<std::mutex> guard(ctx_mutex);
2284 SSL_load_error_strings();
2288 ~SSLInit() { ERR_free_strings(); }
2291 static SSLInit sslinit_;
2296 inline SSLSocketStream::SSLSocketStream(
socket_t sock, SSL *ssl)
2297 : sock_(sock), ssl_(ssl) {}
2299 inline SSLSocketStream::~SSLSocketStream() {}
2301 inline int SSLSocketStream::read(
char *ptr,
size_t size) {
2302 return SSL_read(ssl_, ptr, size);
2305 inline int SSLSocketStream::write(
const char *ptr,
size_t size) {
2306 return SSL_write(ssl_, ptr, size);
2309 inline int SSLSocketStream::write(
const char *ptr) {
2310 return write(ptr, strlen(ptr));
2313 inline std::string SSLSocketStream::get_remote_addr()
const {
2314 return detail::get_remote_addr(sock_);
2318 inline SSLServer::SSLServer(
const char *cert_path,
2319 const char *private_key_path) {
2320 ctx_ = SSL_CTX_new(SSLv23_server_method());
2323 SSL_CTX_set_options(ctx_,
2324 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
2325 SSL_OP_NO_COMPRESSION |
2326 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
2332 if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
2333 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
2341 inline SSLServer::~SSLServer() {
2342 if (ctx_) { SSL_CTX_free(ctx_); }
2345 inline bool SSLServer::is_valid()
const {
return ctx_; }
2347 inline bool SSLServer::read_and_close_socket(
socket_t sock) {
2348 return detail::read_and_close_socket_ssl(
2349 sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept,
2350 [](SSL * ) {
return true; },
2351 [
this](
Stream &strm,
bool last_connection,
bool &connection_close) {
2352 return process_request(strm, last_connection, connection_close);
2357 inline SSLClient::SSLClient(
const char *host,
int port, time_t timeout_sec)
2358 : Client(host, port, timeout_sec) {
2359 ctx_ = SSL_CTX_new(SSLv23_client_method());
2361 detail::split(&host_[0], &host_[host_.size()],
'.',
2362 [&](
const char *b,
const char *e) {
2363 host_components_.emplace_back(std::string(b, e));
2367 inline SSLClient::~SSLClient() {
2368 if (ctx_) { SSL_CTX_free(ctx_); }
2371 inline bool SSLClient::is_valid()
const {
return ctx_; }
2373 inline void SSLClient::set_ca_cert_path(
const char *ca_cert_path) {
2374 ca_cert_path_ = ca_cert_path;
2377 inline void SSLClient::enable_server_certificate_verification(
bool enabled) {
2378 server_certificate_verification_ = enabled;
2381 inline long SSLClient::get_openssl_verify_result()
const {
2382 return verify_result_;
2385 inline bool SSLClient::read_and_close_socket(
socket_t sock, Request &req,
2388 return is_valid() &&
2389 detail::read_and_close_socket_ssl(
2390 sock, 0, ctx_, ctx_mutex_,
2392 if (ca_cert_path_.empty()) {
2393 SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
2395 if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_path_.c_str(),
2399 SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER,
nullptr);
2402 if (SSL_connect(ssl) != 1) {
return false; }
2404 if (server_certificate_verification_) {
2405 verify_result_ = SSL_get_verify_result(ssl);
2407 if (verify_result_ != X509_V_OK) {
return false; }
2409 auto server_cert = SSL_get_peer_certificate(ssl);
2411 if (server_cert ==
nullptr) {
return false; }
2413 if (!verify_host(server_cert)) {
return false; }
2419 SSL_set_tlsext_host_name(ssl, host_.c_str());
2423 bool &connection_close) {
2424 return process_request(strm, req, res, connection_close);
2428 inline bool SSLClient::is_ssl()
const {
return true; }
2430 inline bool SSLClient::verify_host(X509 *server_cert)
const {
2452 return verify_host_with_subject_alt_name(server_cert) ||
2453 verify_host_with_common_name(server_cert);
2457 SSLClient::verify_host_with_subject_alt_name(X509 *server_cert)
const {
2460 auto type = GEN_DNS;
2462 struct in6_addr addr6;
2463 struct in_addr addr;
2464 size_t addr_len = 0;
2466 if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {
2468 addr_len =
sizeof(
struct in6_addr);
2469 }
else if (inet_pton(AF_INET, host_.c_str(), &addr)) {
2471 addr_len =
sizeof(
struct in_addr);
2475 X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr,
nullptr);
2478 auto dsn_matched =
false;
2479 auto ip_mached =
false;
2481 auto count = sk_GENERAL_NAME_num(alt_names);
2483 for (
auto i = 0; i < count && !dsn_matched; i++) {
2484 auto val = sk_GENERAL_NAME_value(alt_names, i);
2485 if (val->type ==
type) {
2486 auto name = (
const char *)ASN1_STRING_data(val->d.ia5);
2487 auto name_len = (size_t)ASN1_STRING_length(val->d.ia5);
2489 if (strlen(name) == name_len) {
2491 case GEN_DNS: dsn_matched = check_host_name(name, name_len);
break;
2494 if (!memcmp(&addr6, name, addr_len) ||
2495 !memcmp(&addr, name, addr_len)) {
2504 if (dsn_matched || ip_mached) { ret =
true; }
2507 GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
2512 inline bool SSLClient::verify_host_with_common_name(X509 *server_cert)
const {
2513 const auto subject_name = X509_get_subject_name(server_cert);
2515 if (subject_name !=
nullptr) {
2517 auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
2518 name,
sizeof(name));
2520 if (name_len != -1) {
return check_host_name(name, name_len); }
2526 inline bool SSLClient::check_host_name(
const char *pattern,
2527 size_t pattern_len)
const {
2528 if (host_.size() == pattern_len && host_ == pattern) {
2534 std::vector<std::string> pattern_components;
2535 detail::split(&pattern[0], &pattern[pattern_len],
'.',
2536 [&](
const char *b,
const char *e) {
2537 pattern_components.emplace_back(std::string(b, e));
2540 if (host_components_.size() != pattern_components.size()) {
return false; }
2542 auto itr = pattern_components.begin();
2543 for (
const auto &h : host_components_) {
2545 if (p != h && p !=
"*") {
2546 auto partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*' &&
2547 !p.compare(0, p.size() - 1, h));
2548 if (!partial_match) {
return false; }
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.hpp:25318
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
Definition: Server.h:88
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH
Definition: Server.h:91
#define INVALID_SOCKET
Definition: Server.h:59
int socket_t
Definition: Server.h:58
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT
Definition: Server.h:89
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
Definition: Server.h:87
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
Definition: Server.h:90
Concept for receiving events from GenericReader upon parsing. The functions return true if no error o...
Concept for reading and writing characters.
@ pos
Definition: Typedefs.h:19
type
The type the bitset is encoded with.
Definition: bitset.hpp:44
std::true_type yes
Definition: traits.hpp:49
detail namespace with internal helper functions
Definition: json.hpp:260
Definition: json.hpp:5678
void close(FILE *out)
Definition: Vtk.h:123
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1282
unsigned char uint8_t
Definition: stdint.h:124
unsigned __int64 uint64_t
Definition: stdint.h:136
uInt avail_in
Definition: zlib.h:88
alloc_func zalloc
Definition: zlib.h:98
uInt avail_out
Definition: zlib.h:92
z_const Bytef * next_in
Definition: zlib.h:87
free_func zfree
Definition: zlib.h:99
voidpf opaque
Definition: zlib.h:100
Bytef * next_out
Definition: zlib.h:91
Byte FAR Bytef
Definition: zconf.h:406
#define Z_DEFLATED
Definition: zlib.h:209
#define Z_DEFAULT_STRATEGY
Definition: zlib.h:200
#define deflateInit2(strm, level, method, windowBits, memLevel, strategy)
Definition: zlib.h:1798
#define inflateInit2(strm, windowBits)
Definition: zlib.h:1801
#define Z_FINISH
Definition: zlib.h:172
#define Z_OK
Definition: zlib.h:177
#define Z_NO_FLUSH
Definition: zlib.h:168
#define Z_NULL
Definition: zlib.h:212
#define Z_DEFAULT_COMPRESSION
Definition: zlib.h:193