summaryrefslogtreecommitdiff
path: root/externals/httplib/httplib.h
diff options
context:
space:
mode:
authorGravatar bunnei2018-10-06 02:43:09 -0400
committerGravatar GitHub2018-10-06 02:43:09 -0400
commitb8b90ce6e61329ebda226b9917ed961be3b80d1f (patch)
tree8e6b82419db7fb24eb5d8f06346a0a1228f9513c /externals/httplib/httplib.h
parentMerge pull request #1447 from lioncash/mutex (diff)
parentReview comments - part 5 (diff)
downloadyuzu-b8b90ce6e61329ebda226b9917ed961be3b80d1f.tar.gz
yuzu-b8b90ce6e61329ebda226b9917ed961be3b80d1f.tar.xz
yuzu-b8b90ce6e61329ebda226b9917ed961be3b80d1f.zip
Merge pull request #1332 from FearlessTobi/port-web-backend
Port web_service from Citra
Diffstat (limited to 'externals/httplib/httplib.h')
-rw-r--r--externals/httplib/httplib.h2344
1 files changed, 2344 insertions, 0 deletions
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
new file mode 100644
index 000000000..dd9afe693
--- /dev/null
+++ b/externals/httplib/httplib.h
@@ -0,0 +1,2344 @@
1//
2// httplib.h
3//
4// Copyright (c) 2017 Yuji Hirose. All rights reserved.
5// MIT License
6//
7
8#ifndef _CPPHTTPLIB_HTTPLIB_H_
9#define _CPPHTTPLIB_HTTPLIB_H_
10
11#ifdef _WIN32
12#ifndef _CRT_SECURE_NO_WARNINGS
13#define _CRT_SECURE_NO_WARNINGS
14#endif
15#ifndef _CRT_NONSTDC_NO_DEPRECATE
16#define _CRT_NONSTDC_NO_DEPRECATE
17#endif
18
19#if defined(_MSC_VER) && _MSC_VER < 1900
20#define snprintf _snprintf_s
21#endif
22
23#ifndef S_ISREG
24#define S_ISREG(m) (((m)&S_IFREG)==S_IFREG)
25#endif
26#ifndef S_ISDIR
27#define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR)
28#endif
29
30#include <io.h>
31#include <winsock2.h>
32#include <ws2tcpip.h>
33
34#undef min
35#undef max
36
37#ifndef strcasecmp
38#define strcasecmp _stricmp
39#endif
40
41typedef SOCKET socket_t;
42#else
43#include <pthread.h>
44#include <unistd.h>
45#include <netdb.h>
46#include <cstring>
47#include <netinet/in.h>
48#include <arpa/inet.h>
49#include <signal.h>
50#include <sys/socket.h>
51#include <sys/select.h>
52
53typedef int socket_t;
54#define INVALID_SOCKET (-1)
55#endif
56
57#include <fstream>
58#include <functional>
59#include <map>
60#include <memory>
61#include <mutex>
62#include <regex>
63#include <string>
64#include <thread>
65#include <sys/stat.h>
66#include <fcntl.h>
67#include <assert.h>
68
69#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
70#include <openssl/ssl.h>
71#endif
72
73#ifdef CPPHTTPLIB_ZLIB_SUPPORT
74#include <zlib.h>
75#endif
76
77/*
78 * Configuration
79 */
80#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
81#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
82
83namespace httplib
84{
85
86namespace detail {
87
88struct ci {
89 bool operator() (const std::string & s1, const std::string & s2) const {
90 return std::lexicographical_compare(
91 s1.begin(), s1.end(),
92 s2.begin(), s2.end(),
93 [](char c1, char c2) {
94 return ::tolower(c1) < ::tolower(c2);
95 });
96 }
97};
98
99} // namespace detail
100
101enum class HttpVersion { v1_0 = 0, v1_1 };
102
103typedef std::multimap<std::string, std::string, detail::ci> Headers;
104
105template<typename uint64_t, typename... Args>
106std::pair<std::string, std::string> make_range_header(uint64_t value, Args... args);
107
108typedef std::multimap<std::string, std::string> Params;
109typedef std::smatch Match;
110typedef std::function<void (uint64_t current, uint64_t total)> Progress;
111
112struct MultipartFile {
113 std::string filename;
114 std::string content_type;
115 size_t offset = 0;
116 size_t length = 0;
117};
118typedef std::multimap<std::string, MultipartFile> MultipartFiles;
119
120struct Request {
121 std::string version;
122 std::string method;
123 std::string target;
124 std::string path;
125 Headers headers;
126 std::string body;
127 Params params;
128 MultipartFiles files;
129 Match matches;
130
131 Progress progress;
132
133 bool has_header(const char* key) const;
134 std::string get_header_value(const char* key) const;
135 void set_header(const char* key, const char* val);
136
137 bool has_param(const char* key) const;
138 std::string get_param_value(const char* key) const;
139
140 bool has_file(const char* key) const;
141 MultipartFile get_file_value(const char* key) const;
142};
143
144struct Response {
145 std::string version;
146 int status;
147 Headers headers;
148 std::string body;
149
150 bool has_header(const char* key) const;
151 std::string get_header_value(const char* key) const;
152 void set_header(const char* key, const char* val);
153
154 void set_redirect(const char* uri);
155 void set_content(const char* s, size_t n, const char* content_type);
156 void set_content(const std::string& s, const char* content_type);
157
158 Response() : status(-1) {}
159};
160
161class Stream {
162public:
163 virtual ~Stream() {}
164 virtual int read(char* ptr, size_t size) = 0;
165 virtual int write(const char* ptr, size_t size1) = 0;
166 virtual int write(const char* ptr) = 0;
167 virtual std::string get_remote_addr() = 0;
168
169 template <typename ...Args>
170 void write_format(const char* fmt, const Args& ...args);
171};
172
173class SocketStream : public Stream {
174public:
175 SocketStream(socket_t sock);
176 virtual ~SocketStream();
177
178 virtual int read(char* ptr, size_t size);
179 virtual int write(const char* ptr, size_t size);
180 virtual int write(const char* ptr);
181 virtual std::string get_remote_addr();
182
183private:
184 socket_t sock_;
185};
186
187class Server {
188public:
189 typedef std::function<void (const Request&, Response&)> Handler;
190 typedef std::function<void (const Request&, const Response&)> Logger;
191
192 Server();
193
194 virtual ~Server();
195
196 virtual bool is_valid() const;
197
198 Server& Get(const char* pattern, Handler handler);
199 Server& Post(const char* pattern, Handler handler);
200
201 Server& Put(const char* pattern, Handler handler);
202 Server& Delete(const char* pattern, Handler handler);
203 Server& Options(const char* pattern, Handler handler);
204
205 bool set_base_dir(const char* path);
206
207 void set_error_handler(Handler handler);
208 void set_logger(Logger logger);
209
210 void set_keep_alive_max_count(size_t count);
211
212 int bind_to_any_port(const char* host, int socket_flags = 0);
213 bool listen_after_bind();
214
215 bool listen(const char* host, int port, int socket_flags = 0);
216
217 bool is_running() const;
218 void stop();
219
220protected:
221 bool process_request(Stream& strm, bool last_connection, bool& connection_close);
222
223 size_t keep_alive_max_count_;
224
225private:
226 typedef std::vector<std::pair<std::regex, Handler>> Handlers;
227
228 socket_t create_server_socket(const char* host, int port, int socket_flags) const;
229 int bind_internal(const char* host, int port, int socket_flags);
230 bool listen_internal();
231
232 bool routing(Request& req, Response& res);
233 bool handle_file_request(Request& req, Response& res);
234 bool dispatch_request(Request& req, Response& res, Handlers& handlers);
235
236 bool parse_request_line(const char* s, Request& req);
237 void write_response(Stream& strm, bool last_connection, const Request& req, Response& res);
238
239 virtual bool read_and_close_socket(socket_t sock);
240
241 bool is_running_;
242 socket_t svr_sock_;
243 std::string base_dir_;
244 Handlers get_handlers_;
245 Handlers post_handlers_;
246 Handlers put_handlers_;
247 Handlers delete_handlers_;
248 Handlers options_handlers_;
249 Handler error_handler_;
250 Logger logger_;
251
252 // TODO: Use thread pool...
253 std::mutex running_threads_mutex_;
254 int running_threads_;
255};
256
257class Client {
258public:
259 Client(
260 const char* host,
261 int port = 80,
262 size_t timeout_sec = 300);
263
264 virtual ~Client();
265
266 virtual bool is_valid() const;
267
268 std::shared_ptr<Response> Get(const char* path, Progress progress = nullptr);
269 std::shared_ptr<Response> Get(const char* path, const Headers& headers, Progress progress = nullptr);
270
271 std::shared_ptr<Response> Head(const char* path);
272 std::shared_ptr<Response> Head(const char* path, const Headers& headers);
273
274 std::shared_ptr<Response> Post(const char* path, const std::string& body, const char* content_type);
275 std::shared_ptr<Response> Post(const char* path, const Headers& headers, const std::string& body, const char* content_type);
276
277 std::shared_ptr<Response> Post(const char* path, const Params& params);
278 std::shared_ptr<Response> Post(const char* path, const Headers& headers, const Params& params);
279
280 std::shared_ptr<Response> Put(const char* path, const std::string& body, const char* content_type);
281 std::shared_ptr<Response> Put(const char* path, const Headers& headers, const std::string& body, const char* content_type);
282
283 std::shared_ptr<Response> Delete(const char* path);
284 std::shared_ptr<Response> Delete(const char* path, const Headers& headers);
285
286 std::shared_ptr<Response> Options(const char* path);
287 std::shared_ptr<Response> Options(const char* path, const Headers& headers);
288
289 bool send(Request& req, Response& res);
290
291protected:
292 bool process_request(Stream& strm, Request& req, Response& res, bool& connection_close);
293
294 const std::string host_;
295 const int port_;
296 size_t timeout_sec_;
297 const std::string host_and_port_;
298
299private:
300 socket_t create_client_socket() const;
301 bool read_response_line(Stream& strm, Response& res);
302 void write_request(Stream& strm, Request& req);
303
304 virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res);
305};
306
307#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
308class SSLSocketStream : public Stream {
309public:
310 SSLSocketStream(socket_t sock, SSL* ssl);
311 virtual ~SSLSocketStream();
312
313 virtual int read(char* ptr, size_t size);
314 virtual int write(const char* ptr, size_t size);
315 virtual int write(const char* ptr);
316 virtual std::string get_remote_addr();
317
318private:
319 socket_t sock_;
320 SSL* ssl_;
321};
322
323class SSLServer : public Server {
324public:
325 SSLServer(
326 const char* cert_path, const char* private_key_path);
327
328 virtual ~SSLServer();
329
330 virtual bool is_valid() const;
331
332private:
333 virtual bool read_and_close_socket(socket_t sock);
334
335 SSL_CTX* ctx_;
336 std::mutex ctx_mutex_;
337};
338
339class SSLClient : public Client {
340public:
341 SSLClient(
342 const char* host,
343 int port = 80,
344 size_t timeout_sec = 300);
345
346 virtual ~SSLClient();
347
348 virtual bool is_valid() const;
349
350private:
351 virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res);
352
353 SSL_CTX* ctx_;
354 std::mutex ctx_mutex_;
355};
356#endif
357
358/*
359 * Implementation
360 */
361namespace detail {
362
363template <class Fn>
364void split(const char* b, const char* e, char d, Fn fn)
365{
366 int i = 0;
367 int beg = 0;
368
369 while (e ? (b + i != e) : (b[i] != '\0')) {
370 if (b[i] == d) {
371 fn(&b[beg], &b[i]);
372 beg = i + 1;
373 }
374 i++;
375 }
376
377 if (i) {
378 fn(&b[beg], &b[i]);
379 }
380}
381
382// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
383// to store data. The call can set memory on stack for performance.
384class stream_line_reader {
385public:
386 stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size)
387 : strm_(strm)
388 , fixed_buffer_(fixed_buffer)
389 , fixed_buffer_size_(fixed_buffer_size) {
390 }
391
392 const char* ptr() const {
393 if (glowable_buffer_.empty()) {
394 return fixed_buffer_;
395 } else {
396 return glowable_buffer_.data();
397 }
398 }
399
400 bool getline() {
401 fixed_buffer_used_size_ = 0;
402 glowable_buffer_.clear();
403
404 for (size_t i = 0; ; i++) {
405 char byte;
406 auto n = strm_.read(&byte, 1);
407
408 if (n < 0) {
409 return false;
410 } else if (n == 0) {
411 if (i == 0) {
412 return false;
413 } else {
414 break;
415 }
416 }
417
418 append(byte);
419
420 if (byte == '\n') {
421 break;
422 }
423 }
424
425 return true;
426 }
427
428private:
429 void append(char c) {
430 if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
431 fixed_buffer_[fixed_buffer_used_size_++] = c;
432 fixed_buffer_[fixed_buffer_used_size_] = '\0';
433 } else {
434 if (glowable_buffer_.empty()) {
435 assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
436 glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
437 }
438 glowable_buffer_ += c;
439 }
440 }
441
442 Stream& strm_;
443 char* fixed_buffer_;
444 const size_t fixed_buffer_size_;
445 size_t fixed_buffer_used_size_;
446 std::string glowable_buffer_;
447};
448
449inline int close_socket(socket_t sock)
450{
451#ifdef _WIN32
452 return closesocket(sock);
453#else
454 return close(sock);
455#endif
456}
457
458inline int select_read(socket_t sock, size_t sec, size_t usec)
459{
460 fd_set fds;
461 FD_ZERO(&fds);
462 FD_SET(sock, &fds);
463
464 timeval tv;
465 tv.tv_sec = sec;
466 tv.tv_usec = usec;
467
468 return select(sock + 1, &fds, NULL, NULL, &tv);
469}
470
471inline bool wait_until_socket_is_ready(socket_t sock, size_t sec, size_t usec)
472{
473 fd_set fdsr;
474 FD_ZERO(&fdsr);
475 FD_SET(sock, &fdsr);
476
477 auto fdsw = fdsr;
478 auto fdse = fdsr;
479
480 timeval tv;
481 tv.tv_sec = sec;
482 tv.tv_usec = usec;
483
484 if (select(sock + 1, &fdsr, &fdsw, &fdse, &tv) < 0) {
485 return false;
486 } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) {
487 int error = 0;
488 socklen_t len = sizeof(error);
489 if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0 || error) {
490 return false;
491 }
492 } else {
493 return false;
494 }
495
496 return true;
497}
498
499template <typename T>
500inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, T callback)
501{
502 bool ret = false;
503
504 if (keep_alive_max_count > 0) {
505 auto count = keep_alive_max_count;
506 while (count > 0 &&
507 detail::select_read(sock,
508 CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
509 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) {
510 SocketStream strm(sock);
511 auto last_connection = count == 1;
512 auto connection_close = false;
513
514 ret = callback(strm, last_connection, connection_close);
515 if (!ret || connection_close) {
516 break;
517 }
518
519 count--;
520 }
521 } else {
522 SocketStream strm(sock);
523 auto dummy_connection_close = false;
524 ret = callback(strm, true, dummy_connection_close);
525 }
526
527 close_socket(sock);
528 return ret;
529}
530
531inline int shutdown_socket(socket_t sock)
532{
533#ifdef _WIN32
534 return shutdown(sock, SD_BOTH);
535#else
536 return shutdown(sock, SHUT_RDWR);
537#endif
538}
539
540template <typename Fn>
541socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0)
542{
543#ifdef _WIN32
544#define SO_SYNCHRONOUS_NONALERT 0x20
545#define SO_OPENTYPE 0x7008
546
547 int opt = SO_SYNCHRONOUS_NONALERT;
548 setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt));
549#endif
550
551 // Get address info
552 struct addrinfo hints;
553 struct addrinfo *result;
554
555 memset(&hints, 0, sizeof(struct addrinfo));
556 hints.ai_family = AF_UNSPEC;
557 hints.ai_socktype = SOCK_STREAM;
558 hints.ai_flags = socket_flags;
559 hints.ai_protocol = 0;
560
561 auto service = std::to_string(port);
562
563 if (getaddrinfo(host, service.c_str(), &hints, &result)) {
564 return INVALID_SOCKET;
565 }
566
567 for (auto rp = result; rp; rp = rp->ai_next) {
568 // Create a socket
569 auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
570 if (sock == INVALID_SOCKET) {
571 continue;
572 }
573
574 // Make 'reuse address' option available
575 int yes = 1;
576 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
577
578 // bind or connect
579 if (fn(sock, *rp)) {
580 freeaddrinfo(result);
581 return sock;
582 }
583
584 close_socket(sock);
585 }
586
587 freeaddrinfo(result);
588 return INVALID_SOCKET;
589}
590
591inline void set_nonblocking(socket_t sock, bool nonblocking)
592{
593#ifdef _WIN32
594 auto flags = nonblocking ? 1UL : 0UL;
595 ioctlsocket(sock, FIONBIO, &flags);
596#else
597 auto flags = fcntl(sock, F_GETFL, 0);
598 fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
599#endif
600}
601
602inline bool is_connection_error()
603{
604#ifdef _WIN32
605 return WSAGetLastError() != WSAEWOULDBLOCK;
606#else
607 return errno != EINPROGRESS;
608#endif
609}
610
611inline std::string get_remote_addr(socket_t sock) {
612 struct sockaddr_storage addr;
613 socklen_t len = sizeof(addr);
614
615 if (!getpeername(sock, (struct sockaddr*)&addr, &len)) {
616 char ipstr[NI_MAXHOST];
617
618 if (!getnameinfo((struct sockaddr*)&addr, len,
619 ipstr, sizeof(ipstr), nullptr, 0, NI_NUMERICHOST)) {
620 return ipstr;
621 }
622 }
623
624 return std::string();
625}
626
627inline bool is_file(const std::string& path)
628{
629 struct stat st;
630 return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
631}
632
633inline bool is_dir(const std::string& path)
634{
635 struct stat st;
636 return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
637}
638
639inline bool is_valid_path(const std::string& path) {
640 size_t level = 0;
641 size_t i = 0;
642
643 // Skip slash
644 while (i < path.size() && path[i] == '/') {
645 i++;
646 }
647
648 while (i < path.size()) {
649 // Read component
650 auto beg = i;
651 while (i < path.size() && path[i] != '/') {
652 i++;
653 }
654
655 auto len = i - beg;
656 assert(len > 0);
657
658 if (!path.compare(beg, len, ".")) {
659 ;
660 } else if (!path.compare(beg, len, "..")) {
661 if (level == 0) {
662 return false;
663 }
664 level--;
665 } else {
666 level++;
667 }
668
669 // Skip slash
670 while (i < path.size() && path[i] == '/') {
671 i++;
672 }
673 }
674
675 return true;
676}
677
678inline void read_file(const std::string& path, std::string& out)
679{
680 std::ifstream fs(path, std::ios_base::binary);
681 fs.seekg(0, std::ios_base::end);
682 auto size = fs.tellg();
683 fs.seekg(0);
684 out.resize(static_cast<size_t>(size));
685 fs.read(&out[0], size);
686}
687
688inline std::string file_extension(const std::string& path)
689{
690 std::smatch m;
691 auto pat = std::regex("\\.([a-zA-Z0-9]+)$");
692 if (std::regex_search(path, m, pat)) {
693 return m[1].str();
694 }
695 return std::string();
696}
697
698inline const char* find_content_type(const std::string& path)
699{
700 auto ext = file_extension(path);
701 if (ext == "txt") {
702 return "text/plain";
703 } else if (ext == "html") {
704 return "text/html";
705 } else if (ext == "css") {
706 return "text/css";
707 } else if (ext == "jpeg" || ext == "jpg") {
708 return "image/jpg";
709 } else if (ext == "png") {
710 return "image/png";
711 } else if (ext == "gif") {
712 return "image/gif";
713 } else if (ext == "svg") {
714 return "image/svg+xml";
715 } else if (ext == "ico") {
716 return "image/x-icon";
717 } else if (ext == "json") {
718 return "application/json";
719 } else if (ext == "pdf") {
720 return "application/pdf";
721 } else if (ext == "js") {
722 return "application/javascript";
723 } else if (ext == "xml") {
724 return "application/xml";
725 } else if (ext == "xhtml") {
726 return "application/xhtml+xml";
727 }
728 return nullptr;
729}
730
731inline const char* status_message(int status)
732{
733 switch (status) {
734 case 200: return "OK";
735 case 301: return "Moved Permanently";
736 case 302: return "Found";
737 case 303: return "See Other";
738 case 304: return "Not Modified";
739 case 400: return "Bad Request";
740 case 403: return "Forbidden";
741 case 404: return "Not Found";
742 case 415: return "Unsupported Media Type";
743 default:
744 case 500: return "Internal Server Error";
745 }
746}
747
748inline const char* get_header_value(const Headers& headers, const char* key, const char* def)
749{
750 auto it = headers.find(key);
751 if (it != headers.end()) {
752 return it->second.c_str();
753 }
754 return def;
755}
756
757inline int get_header_value_int(const Headers& headers, const char* key, int def)
758{
759 auto it = headers.find(key);
760 if (it != headers.end()) {
761 return std::stoi(it->second);
762 }
763 return def;
764}
765
766inline bool read_headers(Stream& strm, Headers& headers)
767{
768 static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)");
769
770 const auto bufsiz = 2048;
771 char buf[bufsiz];
772
773 stream_line_reader reader(strm, buf, bufsiz);
774
775 for (;;) {
776 if (!reader.getline()) {
777 return false;
778 }
779 if (!strcmp(reader.ptr(), "\r\n")) {
780 break;
781 }
782 std::cmatch m;
783 if (std::regex_match(reader.ptr(), m, re)) {
784 auto key = std::string(m[1]);
785 auto val = std::string(m[2]);
786 headers.emplace(key, val);
787 }
788 }
789
790 return true;
791}
792
793inline bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress)
794{
795 out.assign(len, 0);
796 size_t r = 0;
797 while (r < len){
798 auto n = strm.read(&out[r], len - r);
799 if (n <= 0) {
800 return false;
801 }
802
803 r += n;
804
805 if (progress) {
806 progress(r, len);
807 }
808 }
809
810 return true;
811}
812
813inline bool read_content_without_length(Stream& strm, std::string& out)
814{
815 for (;;) {
816 char byte;
817 auto n = strm.read(&byte, 1);
818 if (n < 0) {
819 return false;
820 } else if (n == 0) {
821 return true;
822 }
823 out += byte;
824 }
825
826 return true;
827}
828
829inline bool read_content_chunked(Stream& strm, std::string& out)
830{
831 const auto bufsiz = 16;
832 char buf[bufsiz];
833
834 stream_line_reader reader(strm, buf, bufsiz);
835
836 if (!reader.getline()) {
837 return false;
838 }
839
840 auto chunk_len = std::stoi(reader.ptr(), 0, 16);
841
842 while (chunk_len > 0){
843 std::string chunk;
844 if (!read_content_with_length(strm, chunk, chunk_len, nullptr)) {
845 return false;
846 }
847
848 if (!reader.getline()) {
849 return false;
850 }
851
852 if (strcmp(reader.ptr(), "\r\n")) {
853 break;
854 }
855
856 out += chunk;
857
858 if (!reader.getline()) {
859 return false;
860 }
861
862 chunk_len = std::stoi(reader.ptr(), 0, 16);
863 }
864
865 if (chunk_len == 0) {
866 // Reader terminator after chunks
867 if (!reader.getline() || strcmp(reader.ptr(), "\r\n"))
868 return false;
869 }
870
871 return true;
872}
873
874template <typename T>
875bool read_content(Stream& strm, T& x, Progress progress = Progress())
876{
877 auto len = get_header_value_int(x.headers, "Content-Length", 0);
878
879 if (len) {
880 return read_content_with_length(strm, x.body, len, progress);
881 } else {
882 const auto& encoding = get_header_value(x.headers, "Transfer-Encoding", "");
883
884 if (!strcasecmp(encoding, "chunked")) {
885 return read_content_chunked(strm, x.body);
886 } else {
887 return read_content_without_length(strm, x.body);
888 }
889 }
890
891 return true;
892}
893
894template <typename T>
895inline void write_headers(Stream& strm, const T& info)
896{
897 for (const auto& x: info.headers) {
898 strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
899 }
900 strm.write("\r\n");
901}
902
903inline std::string encode_url(const std::string& s)
904{
905 std::string result;
906
907 for (auto i = 0; s[i]; i++) {
908 switch (s[i]) {
909 case ' ': result += "+"; break;
910 case '\'': result += "%27"; break;
911 case ',': result += "%2C"; break;
912 case ':': result += "%3A"; break;
913 case ';': result += "%3B"; break;
914 default:
915 if (s[i] < 0) {
916 result += '%';
917 char hex[4];
918 size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", (unsigned char)s[i]);
919 assert(len == 2);
920 result.append(hex, len);
921 } else {
922 result += s[i];
923 }
924 break;
925 }
926 }
927
928 return result;
929}
930
931inline bool is_hex(char c, int& v)
932{
933 if (0x20 <= c && isdigit(c)) {
934 v = c - '0';
935 return true;
936 } else if ('A' <= c && c <= 'F') {
937 v = c - 'A' + 10;
938 return true;
939 } else if ('a' <= c && c <= 'f') {
940 v = c - 'a' + 10;
941 return true;
942 }
943 return false;
944}
945
946inline bool from_hex_to_i(const std::string& s, size_t i, size_t cnt, int& val)
947{
948 if (i >= s.size()) {
949 return false;
950 }
951
952 val = 0;
953 for (; cnt; i++, cnt--) {
954 if (!s[i]) {
955 return false;
956 }
957 int v = 0;
958 if (is_hex(s[i], v)) {
959 val = val * 16 + v;
960 } else {
961 return false;
962 }
963 }
964 return true;
965}
966
967inline size_t to_utf8(int code, char* buff)
968{
969 if (code < 0x0080) {
970 buff[0] = (code & 0x7F);
971 return 1;
972 } else if (code < 0x0800) {
973 buff[0] = (0xC0 | ((code >> 6) & 0x1F));
974 buff[1] = (0x80 | (code & 0x3F));
975 return 2;
976 } else if (code < 0xD800) {
977 buff[0] = (0xE0 | ((code >> 12) & 0xF));
978 buff[1] = (0x80 | ((code >> 6) & 0x3F));
979 buff[2] = (0x80 | (code & 0x3F));
980 return 3;
981 } else if (code < 0xE000) { // D800 - DFFF is invalid...
982 return 0;
983 } else if (code < 0x10000) {
984 buff[0] = (0xE0 | ((code >> 12) & 0xF));
985 buff[1] = (0x80 | ((code >> 6) & 0x3F));
986 buff[2] = (0x80 | (code & 0x3F));
987 return 3;
988 } else if (code < 0x110000) {
989 buff[0] = (0xF0 | ((code >> 18) & 0x7));
990 buff[1] = (0x80 | ((code >> 12) & 0x3F));
991 buff[2] = (0x80 | ((code >> 6) & 0x3F));
992 buff[3] = (0x80 | (code & 0x3F));
993 return 4;
994 }
995
996 // NOTREACHED
997 return 0;
998}
999
1000inline std::string decode_url(const std::string& s)
1001{
1002 std::string result;
1003
1004 for (size_t i = 0; i < s.size(); i++) {
1005 if (s[i] == '%' && i + 1 < s.size()) {
1006 if (s[i + 1] == 'u') {
1007 int val = 0;
1008 if (from_hex_to_i(s, i + 2, 4, val)) {
1009 // 4 digits Unicode codes
1010 char buff[4];
1011 size_t len = to_utf8(val, buff);
1012 if (len > 0) {
1013 result.append(buff, len);
1014 }
1015 i += 5; // 'u0000'
1016 } else {
1017 result += s[i];
1018 }
1019 } else {
1020 int val = 0;
1021 if (from_hex_to_i(s, i + 1, 2, val)) {
1022 // 2 digits hex codes
1023 result += val;
1024 i += 2; // '00'
1025 } else {
1026 result += s[i];
1027 }
1028 }
1029 } else if (s[i] == '+') {
1030 result += ' ';
1031 } else {
1032 result += s[i];
1033 }
1034 }
1035
1036 return result;
1037}
1038
1039inline void parse_query_text(const std::string& s, Params& params)
1040{
1041 split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) {
1042 std::string key;
1043 std::string val;
1044 split(b, e, '=', [&](const char* b, const char* e) {
1045 if (key.empty()) {
1046 key.assign(b, e);
1047 } else {
1048 val.assign(b, e);
1049 }
1050 });
1051 params.emplace(key, decode_url(val));
1052 });
1053}
1054
1055inline bool parse_multipart_boundary(const std::string& content_type, std::string& boundary)
1056{
1057 auto pos = content_type.find("boundary=");
1058 if (pos == std::string::npos) {
1059 return false;
1060 }
1061
1062 boundary = content_type.substr(pos + 9);
1063 return true;
1064}
1065
1066inline bool parse_multipart_formdata(
1067 const std::string& boundary, const std::string& body, MultipartFiles& files)
1068{
1069 static std::string dash = "--";
1070 static std::string crlf = "\r\n";
1071
1072 static std::regex re_content_type(
1073 "Content-Type: (.*?)", std::regex_constants::icase);
1074
1075 static std::regex re_content_disposition(
1076 "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?",
1077 std::regex_constants::icase);
1078
1079 auto dash_boundary = dash + boundary;
1080
1081 auto pos = body.find(dash_boundary);
1082 if (pos != 0) {
1083 return false;
1084 }
1085
1086 pos += dash_boundary.size();
1087
1088 auto next_pos = body.find(crlf, pos);
1089 if (next_pos == std::string::npos) {
1090 return false;
1091 }
1092
1093 pos = next_pos + crlf.size();
1094
1095 while (pos < body.size()) {
1096 next_pos = body.find(crlf, pos);
1097 if (next_pos == std::string::npos) {
1098 return false;
1099 }
1100
1101 std::string name;
1102 MultipartFile file;
1103
1104 auto header = body.substr(pos, (next_pos - pos));
1105
1106 while (pos != next_pos) {
1107 std::smatch m;
1108 if (std::regex_match(header, m, re_content_type)) {
1109 file.content_type = m[1];
1110 } else if (std::regex_match(header, m, re_content_disposition)) {
1111 name = m[1];
1112 file.filename = m[2];
1113 }
1114
1115 pos = next_pos + crlf.size();
1116
1117 next_pos = body.find(crlf, pos);
1118 if (next_pos == std::string::npos) {
1119 return false;
1120 }
1121
1122 header = body.substr(pos, (next_pos - pos));
1123 }
1124
1125 pos = next_pos + crlf.size();
1126
1127 next_pos = body.find(crlf + dash_boundary, pos);
1128
1129 if (next_pos == std::string::npos) {
1130 return false;
1131 }
1132
1133 file.offset = pos;
1134 file.length = next_pos - pos;
1135
1136 pos = next_pos + crlf.size() + dash_boundary.size();
1137
1138 next_pos = body.find(crlf, pos);
1139 if (next_pos == std::string::npos) {
1140 return false;
1141 }
1142
1143 files.emplace(name, file);
1144
1145 pos = next_pos + crlf.size();
1146 }
1147
1148 return true;
1149}
1150
1151inline std::string to_lower(const char* beg, const char* end)
1152{
1153 std::string out;
1154 auto it = beg;
1155 while (it != end) {
1156 out += ::tolower(*it);
1157 it++;
1158 }
1159 return out;
1160}
1161
1162inline void make_range_header_core(std::string&) {}
1163
1164template<typename uint64_t>
1165inline void make_range_header_core(std::string& field, uint64_t value)
1166{
1167 if (!field.empty()) {
1168 field += ", ";
1169 }
1170 field += std::to_string(value) + "-";
1171}
1172
1173template<typename uint64_t, typename... Args>
1174inline void make_range_header_core(std::string& field, uint64_t value1, uint64_t value2, Args... args)
1175{
1176 if (!field.empty()) {
1177 field += ", ";
1178 }
1179 field += std::to_string(value1) + "-" + std::to_string(value2);
1180 make_range_header_core(field, args...);
1181}
1182
1183#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1184inline bool can_compress(const std::string& content_type) {
1185 return !content_type.find("text/") ||
1186 content_type == "image/svg+xml" ||
1187 content_type == "application/javascript" ||
1188 content_type == "application/json" ||
1189 content_type == "application/xml" ||
1190 content_type == "application/xhtml+xml";
1191}
1192
1193inline void compress(std::string& content)
1194{
1195 z_stream strm;
1196 strm.zalloc = Z_NULL;
1197 strm.zfree = Z_NULL;
1198 strm.opaque = Z_NULL;
1199
1200 auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
1201 if (ret != Z_OK) {
1202 return;
1203 }
1204
1205 strm.avail_in = content.size();
1206 strm.next_in = (Bytef *)content.data();
1207
1208 std::string compressed;
1209
1210 const auto bufsiz = 16384;
1211 char buff[bufsiz];
1212 do {
1213 strm.avail_out = bufsiz;
1214 strm.next_out = (Bytef *)buff;
1215 deflate(&strm, Z_FINISH);
1216 compressed.append(buff, bufsiz - strm.avail_out);
1217 } while (strm.avail_out == 0);
1218
1219 content.swap(compressed);
1220
1221 deflateEnd(&strm);
1222}
1223
1224inline void decompress(std::string& content)
1225{
1226 z_stream strm;
1227 strm.zalloc = Z_NULL;
1228 strm.zfree = Z_NULL;
1229 strm.opaque = Z_NULL;
1230
1231 // 15 is the value of wbits, which should be at the maximum possible value to ensure
1232 // that any gzip stream can be decoded. The offset of 16 specifies that the stream
1233 // to decompress will be formatted with a gzip wrapper.
1234 auto ret = inflateInit2(&strm, 16 + 15);
1235 if (ret != Z_OK) {
1236 return;
1237 }
1238
1239 strm.avail_in = content.size();
1240 strm.next_in = (Bytef *)content.data();
1241
1242 std::string decompressed;
1243
1244 const auto bufsiz = 16384;
1245 char buff[bufsiz];
1246 do {
1247 strm.avail_out = bufsiz;
1248 strm.next_out = (Bytef *)buff;
1249 inflate(&strm, Z_NO_FLUSH);
1250 decompressed.append(buff, bufsiz - strm.avail_out);
1251 } while (strm.avail_out == 0);
1252
1253 content.swap(decompressed);
1254
1255 inflateEnd(&strm);
1256}
1257#endif
1258
1259#ifdef _WIN32
1260class WSInit {
1261public:
1262 WSInit() {
1263 WSADATA wsaData;
1264 WSAStartup(0x0002, &wsaData);
1265 }
1266
1267 ~WSInit() {
1268 WSACleanup();
1269 }
1270};
1271
1272static WSInit wsinit_;
1273#endif
1274
1275} // namespace detail
1276
1277// Header utilities
1278template<typename uint64_t, typename... Args>
1279inline std::pair<std::string, std::string> make_range_header(uint64_t value, Args... args)
1280{
1281 std::string field;
1282 detail::make_range_header_core(field, value, args...);
1283 field.insert(0, "bytes=");
1284 return std::make_pair("Range", field);
1285}
1286
1287// Request implementation
1288inline bool Request::has_header(const char* key) const
1289{
1290 return headers.find(key) != headers.end();
1291}
1292
1293inline std::string Request::get_header_value(const char* key) const
1294{
1295 return detail::get_header_value(headers, key, "");
1296}
1297
1298inline void Request::set_header(const char* key, const char* val)
1299{
1300 headers.emplace(key, val);
1301}
1302
1303inline bool Request::has_param(const char* key) const
1304{
1305 return params.find(key) != params.end();
1306}
1307
1308inline std::string Request::get_param_value(const char* key) const
1309{
1310 auto it = params.find(key);
1311 if (it != params.end()) {
1312 return it->second;
1313 }
1314 return std::string();
1315}
1316
1317inline bool Request::has_file(const char* key) const
1318{
1319 return files.find(key) != files.end();
1320}
1321
1322inline MultipartFile Request::get_file_value(const char* key) const
1323{
1324 auto it = files.find(key);
1325 if (it != files.end()) {
1326 return it->second;
1327 }
1328 return MultipartFile();
1329}
1330
1331// Response implementation
1332inline bool Response::has_header(const char* key) const
1333{
1334 return headers.find(key) != headers.end();
1335}
1336
1337inline std::string Response::get_header_value(const char* key) const
1338{
1339 return detail::get_header_value(headers, key, "");
1340}
1341
1342inline void Response::set_header(const char* key, const char* val)
1343{
1344 headers.emplace(key, val);
1345}
1346
1347inline void Response::set_redirect(const char* url)
1348{
1349 set_header("Location", url);
1350 status = 302;
1351}
1352
1353inline void Response::set_content(const char* s, size_t n, const char* content_type)
1354{
1355 body.assign(s, n);
1356 set_header("Content-Type", content_type);
1357}
1358
1359inline void Response::set_content(const std::string& s, const char* content_type)
1360{
1361 body = s;
1362 set_header("Content-Type", content_type);
1363}
1364
1365// Rstream implementation
1366template <typename ...Args>
1367inline void Stream::write_format(const char* fmt, const Args& ...args)
1368{
1369 const auto bufsiz = 2048;
1370 char buf[bufsiz];
1371
1372#if defined(_MSC_VER) && _MSC_VER < 1900
1373 auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...);
1374#else
1375 auto n = snprintf(buf, bufsiz - 1, fmt, args...);
1376#endif
1377 if (n > 0) {
1378 if (n >= bufsiz - 1) {
1379 std::vector<char> glowable_buf(bufsiz);
1380
1381 while (n >= static_cast<int>(glowable_buf.size() - 1)) {
1382 glowable_buf.resize(glowable_buf.size() * 2);
1383#if defined(_MSC_VER) && _MSC_VER < 1900
1384 n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...);
1385#else
1386 n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
1387#endif
1388 }
1389 write(&glowable_buf[0], n);
1390 } else {
1391 write(buf, n);
1392 }
1393 }
1394}
1395
1396// Socket stream implementation
1397inline SocketStream::SocketStream(socket_t sock): sock_(sock)
1398{
1399}
1400
1401inline SocketStream::~SocketStream()
1402{
1403}
1404
1405inline int SocketStream::read(char* ptr, size_t size)
1406{
1407 return recv(sock_, ptr, size, 0);
1408}
1409
1410inline int SocketStream::write(const char* ptr, size_t size)
1411{
1412 return send(sock_, ptr, size, 0);
1413}
1414
1415inline int SocketStream::write(const char* ptr)
1416{
1417 return write(ptr, strlen(ptr));
1418}
1419
1420inline std::string SocketStream::get_remote_addr() {
1421 return detail::get_remote_addr(sock_);
1422}
1423
1424// HTTP server implementation
1425inline Server::Server()
1426 : keep_alive_max_count_(5)
1427 , is_running_(false)
1428 , svr_sock_(INVALID_SOCKET)
1429 , running_threads_(0)
1430{
1431#ifndef _WIN32
1432 signal(SIGPIPE, SIG_IGN);
1433#endif
1434}
1435
1436inline Server::~Server()
1437{
1438}
1439
1440inline Server& Server::Get(const char* pattern, Handler handler)
1441{
1442 get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1443 return *this;
1444}
1445
1446inline Server& Server::Post(const char* pattern, Handler handler)
1447{
1448 post_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1449 return *this;
1450}
1451
1452inline Server& Server::Put(const char* pattern, Handler handler)
1453{
1454 put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1455 return *this;
1456}
1457
1458inline Server& Server::Delete(const char* pattern, Handler handler)
1459{
1460 delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1461 return *this;
1462}
1463
1464inline Server& Server::Options(const char* pattern, Handler handler)
1465{
1466 options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
1467 return *this;
1468}
1469
1470inline bool Server::set_base_dir(const char* path)
1471{
1472 if (detail::is_dir(path)) {
1473 base_dir_ = path;
1474 return true;
1475 }
1476 return false;
1477}
1478
1479inline void Server::set_error_handler(Handler handler)
1480{
1481 error_handler_ = handler;
1482}
1483
1484inline void Server::set_logger(Logger logger)
1485{
1486 logger_ = logger;
1487}
1488
1489inline void Server::set_keep_alive_max_count(size_t count)
1490{
1491 keep_alive_max_count_ = count;
1492}
1493
1494inline int Server::bind_to_any_port(const char* host, int socket_flags)
1495{
1496 return bind_internal(host, 0, socket_flags);
1497}
1498
1499inline bool Server::listen_after_bind() {
1500 return listen_internal();
1501}
1502
1503inline bool Server::listen(const char* host, int port, int socket_flags)
1504{
1505 if (bind_internal(host, port, socket_flags) < 0)
1506 return false;
1507 return listen_internal();
1508}
1509
1510inline bool Server::is_running() const
1511{
1512 return is_running_;
1513}
1514
1515inline void Server::stop()
1516{
1517 if (is_running_) {
1518 assert(svr_sock_ != INVALID_SOCKET);
1519 detail::shutdown_socket(svr_sock_);
1520 detail::close_socket(svr_sock_);
1521 svr_sock_ = INVALID_SOCKET;
1522 }
1523}
1524
1525inline bool Server::parse_request_line(const char* s, Request& req)
1526{
1527 static std::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) (([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n");
1528
1529 std::cmatch m;
1530 if (std::regex_match(s, m, re)) {
1531 req.version = std::string(m[4]);
1532 req.method = std::string(m[1]);
1533 req.target = std::string(m[2]);
1534 req.path = detail::decode_url(m[3]);
1535
1536 // Parse query text
1537 auto len = std::distance(m[4].first, m[4].second);
1538 if (len > 0) {
1539 detail::parse_query_text(m[4], req.params);
1540 }
1541
1542 return true;
1543 }
1544
1545 return false;
1546}
1547
1548inline void Server::write_response(Stream& strm, bool last_connection, const Request& req, Response& res)
1549{
1550 assert(res.status != -1);
1551
1552 if (400 <= res.status && error_handler_) {
1553 error_handler_(req, res);
1554 }
1555
1556 // Response line
1557 strm.write_format("HTTP/1.1 %d %s\r\n",
1558 res.status,
1559 detail::status_message(res.status));
1560
1561 // Headers
1562 if (last_connection ||
1563 req.version == "HTTP/1.0" ||
1564 req.get_header_value("Connection") == "close") {
1565 res.set_header("Connection", "close");
1566 }
1567
1568 if (!res.body.empty()) {
1569#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1570 // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0
1571 const auto& encodings = req.get_header_value("Accept-Encoding");
1572 if (encodings.find("gzip") != std::string::npos &&
1573 detail::can_compress(res.get_header_value("Content-Type"))) {
1574 detail::compress(res.body);
1575 res.set_header("Content-Encoding", "gzip");
1576 }
1577#endif
1578
1579 if (!res.has_header("Content-Type")) {
1580 res.set_header("Content-Type", "text/plain");
1581 }
1582
1583 auto length = std::to_string(res.body.size());
1584 res.set_header("Content-Length", length.c_str());
1585 }
1586
1587 detail::write_headers(strm, res);
1588
1589 // Body
1590 if (!res.body.empty() && req.method != "HEAD") {
1591 strm.write(res.body.c_str(), res.body.size());
1592 }
1593
1594 // Log
1595 if (logger_) {
1596 logger_(req, res);
1597 }
1598}
1599
1600inline bool Server::handle_file_request(Request& req, Response& res)
1601{
1602 if (!base_dir_.empty() && detail::is_valid_path(req.path)) {
1603 std::string path = base_dir_ + req.path;
1604
1605 if (!path.empty() && path.back() == '/') {
1606 path += "index.html";
1607 }
1608
1609 if (detail::is_file(path)) {
1610 detail::read_file(path, res.body);
1611 auto type = detail::find_content_type(path);
1612 if (type) {
1613 res.set_header("Content-Type", type);
1614 }
1615 res.status = 200;
1616 return true;
1617 }
1618 }
1619
1620 return false;
1621}
1622
1623inline socket_t Server::create_server_socket(const char* host, int port, int socket_flags) const
1624{
1625 return detail::create_socket(host, port,
1626 [](socket_t sock, struct addrinfo& ai) -> bool {
1627 if (::bind(sock, ai.ai_addr, ai.ai_addrlen)) {
1628 return false;
1629 }
1630 if (::listen(sock, 5)) { // Listen through 5 channels
1631 return false;
1632 }
1633 return true;
1634 }, socket_flags);
1635}
1636
1637inline int Server::bind_internal(const char* host, int port, int socket_flags)
1638{
1639 if (!is_valid()) {
1640 return -1;
1641 }
1642
1643 svr_sock_ = create_server_socket(host, port, socket_flags);
1644 if (svr_sock_ == INVALID_SOCKET) {
1645 return -1;
1646 }
1647
1648 if (port == 0) {
1649 struct sockaddr_storage address;
1650 socklen_t len = sizeof(address);
1651 if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&address), &len) == -1) {
1652 return -1;
1653 }
1654 if (address.ss_family == AF_INET) {
1655 return ntohs(reinterpret_cast<struct sockaddr_in*>(&address)->sin_port);
1656 } else if (address.ss_family == AF_INET6) {
1657 return ntohs(reinterpret_cast<struct sockaddr_in6*>(&address)->sin6_port);
1658 } else {
1659 return -1;
1660 }
1661 } else {
1662 return port;
1663 }
1664}
1665
1666inline bool Server::listen_internal()
1667{
1668 auto ret = true;
1669
1670 is_running_ = true;
1671
1672 for (;;) {
1673 auto val = detail::select_read(svr_sock_, 0, 100000);
1674
1675 if (val == 0) { // Timeout
1676 if (svr_sock_ == INVALID_SOCKET) {
1677 // The server socket was closed by 'stop' method.
1678 break;
1679 }
1680 continue;
1681 }
1682
1683 socket_t sock = accept(svr_sock_, NULL, NULL);
1684
1685 if (sock == INVALID_SOCKET) {
1686 if (svr_sock_ != INVALID_SOCKET) {
1687 detail::close_socket(svr_sock_);
1688 ret = false;
1689 } else {
1690 ; // The server socket was closed by user.
1691 }
1692 break;
1693 }
1694
1695 // TODO: Use thread pool...
1696 std::thread([=]() {
1697 {
1698 std::lock_guard<std::mutex> guard(running_threads_mutex_);
1699 running_threads_++;
1700 }
1701
1702 read_and_close_socket(sock);
1703
1704 {
1705 std::lock_guard<std::mutex> guard(running_threads_mutex_);
1706 running_threads_--;
1707 }
1708 }).detach();
1709 }
1710
1711 // TODO: Use thread pool...
1712 for (;;) {
1713 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1714 std::lock_guard<std::mutex> guard(running_threads_mutex_);
1715 if (!running_threads_) {
1716 break;
1717 }
1718 }
1719
1720 is_running_ = false;
1721
1722 return ret;
1723}
1724
1725inline bool Server::routing(Request& req, Response& res)
1726{
1727 if (req.method == "GET" && handle_file_request(req, res)) {
1728 return true;
1729 }
1730
1731 if (req.method == "GET" || req.method == "HEAD") {
1732 return dispatch_request(req, res, get_handlers_);
1733 } else if (req.method == "POST") {
1734 return dispatch_request(req, res, post_handlers_);
1735 } else if (req.method == "PUT") {
1736 return dispatch_request(req, res, put_handlers_);
1737 } else if (req.method == "DELETE") {
1738 return dispatch_request(req, res, delete_handlers_);
1739 } else if (req.method == "OPTIONS") {
1740 return dispatch_request(req, res, options_handlers_);
1741 }
1742 return false;
1743}
1744
1745inline bool Server::dispatch_request(Request& req, Response& res, Handlers& handlers)
1746{
1747 for (const auto& x: handlers) {
1748 const auto& pattern = x.first;
1749 const auto& handler = x.second;
1750
1751 if (std::regex_match(req.path, req.matches, pattern)) {
1752 handler(req, res);
1753 return true;
1754 }
1755 }
1756 return false;
1757}
1758
1759inline bool Server::process_request(Stream& strm, bool last_connection, bool& connection_close)
1760{
1761 const auto bufsiz = 2048;
1762 char buf[bufsiz];
1763
1764 detail::stream_line_reader reader(strm, buf, bufsiz);
1765
1766 // Connection has been closed on client
1767 if (!reader.getline()) {
1768 return false;
1769 }
1770
1771 Request req;
1772 Response res;
1773
1774 res.version = "HTTP/1.1";
1775
1776 // Request line and headers
1777 if (!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) {
1778 res.status = 400;
1779 write_response(strm, last_connection, req, res);
1780 return true;
1781 }
1782
1783 auto ret = true;
1784 if (req.get_header_value("Connection") == "close") {
1785 // ret = false;
1786 connection_close = true;
1787 }
1788
1789 req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str());
1790
1791 // Body
1792 if (req.method == "POST" || req.method == "PUT") {
1793 if (!detail::read_content(strm, req)) {
1794 res.status = 400;
1795 write_response(strm, last_connection, req, res);
1796 return ret;
1797 }
1798
1799 const auto& content_type = req.get_header_value("Content-Type");
1800
1801 if (req.get_header_value("Content-Encoding") == "gzip") {
1802#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1803 detail::decompress(req.body);
1804#else
1805 res.status = 415;
1806 write_response(strm, last_connection, req, res);
1807 return ret;
1808#endif
1809 }
1810
1811 if (!content_type.find("application/x-www-form-urlencoded")) {
1812 detail::parse_query_text(req.body, req.params);
1813 } else if(!content_type.find("multipart/form-data")) {
1814 std::string boundary;
1815 if (!detail::parse_multipart_boundary(content_type, boundary) ||
1816 !detail::parse_multipart_formdata(boundary, req.body, req.files)) {
1817 res.status = 400;
1818 write_response(strm, last_connection, req, res);
1819 return ret;
1820 }
1821 }
1822 }
1823
1824 if (routing(req, res)) {
1825 if (res.status == -1) {
1826 res.status = 200;
1827 }
1828 } else {
1829 res.status = 404;
1830 }
1831
1832 write_response(strm, last_connection, req, res);
1833 return ret;
1834}
1835
1836inline bool Server::is_valid() const
1837{
1838 return true;
1839}
1840
1841inline bool Server::read_and_close_socket(socket_t sock)
1842{
1843 return detail::read_and_close_socket(
1844 sock,
1845 keep_alive_max_count_,
1846 [this](Stream& strm, bool last_connection, bool& connection_close) {
1847 return process_request(strm, last_connection, connection_close);
1848 });
1849}
1850
1851// HTTP client implementation
1852inline Client::Client(
1853 const char* host, int port, size_t timeout_sec)
1854 : host_(host)
1855 , port_(port)
1856 , timeout_sec_(timeout_sec)
1857 , host_and_port_(host_ + ":" + std::to_string(port_))
1858{
1859}
1860
1861inline Client::~Client()
1862{
1863}
1864
1865inline bool Client::is_valid() const
1866{
1867 return true;
1868}
1869
1870inline socket_t Client::create_client_socket() const
1871{
1872 return detail::create_socket(host_.c_str(), port_,
1873 [=](socket_t sock, struct addrinfo& ai) -> bool {
1874 detail::set_nonblocking(sock, true);
1875
1876 auto ret = connect(sock, ai.ai_addr, ai.ai_addrlen);
1877 if (ret < 0) {
1878 if (detail::is_connection_error() ||
1879 !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) {
1880 detail::close_socket(sock);
1881 return false;
1882 }
1883 }
1884
1885 detail::set_nonblocking(sock, false);
1886 return true;
1887 });
1888}
1889
1890inline bool Client::read_response_line(Stream& strm, Response& res)
1891{
1892 const auto bufsiz = 2048;
1893 char buf[bufsiz];
1894
1895 detail::stream_line_reader reader(strm, buf, bufsiz);
1896
1897 if (!reader.getline()) {
1898 return false;
1899 }
1900
1901 const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .+\r\n");
1902
1903 std::cmatch m;
1904 if (std::regex_match(reader.ptr(), m, re)) {
1905 res.version = std::string(m[1]);
1906 res.status = std::stoi(std::string(m[2]));
1907 }
1908
1909 return true;
1910}
1911
1912inline bool Client::send(Request& req, Response& res)
1913{
1914 if (req.path.empty()) {
1915 return false;
1916 }
1917
1918 auto sock = create_client_socket();
1919 if (sock == INVALID_SOCKET) {
1920 return false;
1921 }
1922
1923 return read_and_close_socket(sock, req, res);
1924}
1925
1926inline void Client::write_request(Stream& strm, Request& req)
1927{
1928 auto path = detail::encode_url(req.path);
1929
1930 // Request line
1931 strm.write_format("%s %s HTTP/1.1\r\n",
1932 req.method.c_str(),
1933 path.c_str());
1934
1935 // Headers
1936 req.set_header("Host", host_and_port_.c_str());
1937
1938 if (!req.has_header("Accept")) {
1939 req.set_header("Accept", "*/*");
1940 }
1941
1942 if (!req.has_header("User-Agent")) {
1943 req.set_header("User-Agent", "cpp-httplib/0.2");
1944 }
1945
1946 // TODO: Support KeepAlive connection
1947 // if (!req.has_header("Connection")) {
1948 req.set_header("Connection", "close");
1949 // }
1950
1951 if (!req.body.empty()) {
1952 if (!req.has_header("Content-Type")) {
1953 req.set_header("Content-Type", "text/plain");
1954 }
1955
1956 auto length = std::to_string(req.body.size());
1957 req.set_header("Content-Length", length.c_str());
1958 }
1959
1960 detail::write_headers(strm, req);
1961
1962 // Body
1963 if (!req.body.empty()) {
1964 if (req.get_header_value("Content-Type") == "application/x-www-form-urlencoded") {
1965 auto str = detail::encode_url(req.body);
1966 strm.write(str.c_str(), str.size());
1967 } else {
1968 strm.write(req.body.c_str(), req.body.size());
1969 }
1970 }
1971}
1972
1973inline bool Client::process_request(Stream& strm, Request& req, Response& res, bool& connection_close)
1974{
1975 // Send request
1976 write_request(strm, req);
1977
1978 // Receive response and headers
1979 if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) {
1980 return false;
1981 }
1982
1983 if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") {
1984 connection_close = true;
1985 }
1986
1987 // Body
1988 if (req.method != "HEAD") {
1989 if (!detail::read_content(strm, res, req.progress)) {
1990 return false;
1991 }
1992
1993 if (res.get_header_value("Content-Encoding") == "gzip") {
1994#ifdef CPPHTTPLIB_ZLIB_SUPPORT
1995 detail::decompress(res.body);
1996#else
1997 return false;
1998#endif
1999 }
2000 }
2001
2002 return true;
2003}
2004
2005inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res)
2006{
2007 return detail::read_and_close_socket(
2008 sock,
2009 0,
2010 [&](Stream& strm, bool /*last_connection*/, bool& connection_close) {
2011 return process_request(strm, req, res, connection_close);
2012 });
2013}
2014
2015inline std::shared_ptr<Response> Client::Get(const char* path, Progress progress)
2016{
2017 return Get(path, Headers(), progress);
2018}
2019
2020inline std::shared_ptr<Response> Client::Get(const char* path, const Headers& headers, Progress progress)
2021{
2022 Request req;
2023 req.method = "GET";
2024 req.path = path;
2025 req.headers = headers;
2026 req.progress = progress;
2027
2028 auto res = std::make_shared<Response>();
2029
2030 return send(req, *res) ? res : nullptr;
2031}
2032
2033inline std::shared_ptr<Response> Client::Head(const char* path)
2034{
2035 return Head(path, Headers());
2036}
2037
2038inline std::shared_ptr<Response> Client::Head(const char* path, const Headers& headers)
2039{
2040 Request req;
2041 req.method = "HEAD";
2042 req.headers = headers;
2043 req.path = path;
2044
2045 auto res = std::make_shared<Response>();
2046
2047 return send(req, *res) ? res : nullptr;
2048}
2049
2050inline std::shared_ptr<Response> Client::Post(
2051 const char* path, const std::string& body, const char* content_type)
2052{
2053 return Post(path, Headers(), body, content_type);
2054}
2055
2056inline std::shared_ptr<Response> Client::Post(
2057 const char* path, const Headers& headers, const std::string& body, const char* content_type)
2058{
2059 Request req;
2060 req.method = "POST";
2061 req.headers = headers;
2062 req.path = path;
2063
2064 req.headers.emplace("Content-Type", content_type);
2065 req.body = body;
2066
2067 auto res = std::make_shared<Response>();
2068
2069 return send(req, *res) ? res : nullptr;
2070}
2071
2072inline std::shared_ptr<Response> Client::Post(const char* path, const Params& params)
2073{
2074 return Post(path, Headers(), params);
2075}
2076
2077inline std::shared_ptr<Response> Client::Post(const char* path, const Headers& headers, const Params& params)
2078{
2079 std::string query;
2080 for (auto it = params.begin(); it != params.end(); ++it) {
2081 if (it != params.begin()) {
2082 query += "&";
2083 }
2084 query += it->first;
2085 query += "=";
2086 query += it->second;
2087 }
2088
2089 return Post(path, headers, query, "application/x-www-form-urlencoded");
2090}
2091
2092inline std::shared_ptr<Response> Client::Put(
2093 const char* path, const std::string& body, const char* content_type)
2094{
2095 return Put(path, Headers(), body, content_type);
2096}
2097
2098inline std::shared_ptr<Response> Client::Put(
2099 const char* path, const Headers& headers, const std::string& body, const char* content_type)
2100{
2101 Request req;
2102 req.method = "PUT";
2103 req.headers = headers;
2104 req.path = path;
2105
2106 req.headers.emplace("Content-Type", content_type);
2107 req.body = body;
2108
2109 auto res = std::make_shared<Response>();
2110
2111 return send(req, *res) ? res : nullptr;
2112}
2113
2114inline std::shared_ptr<Response> Client::Delete(const char* path)
2115{
2116 return Delete(path, Headers());
2117}
2118
2119inline std::shared_ptr<Response> Client::Delete(const char* path, const Headers& headers)
2120{
2121 Request req;
2122 req.method = "DELETE";
2123 req.path = path;
2124 req.headers = headers;
2125
2126 auto res = std::make_shared<Response>();
2127
2128 return send(req, *res) ? res : nullptr;
2129}
2130
2131inline std::shared_ptr<Response> Client::Options(const char* path)
2132{
2133 return Options(path, Headers());
2134}
2135
2136inline std::shared_ptr<Response> Client::Options(const char* path, const Headers& headers)
2137{
2138 Request req;
2139 req.method = "OPTIONS";
2140 req.path = path;
2141 req.headers = headers;
2142
2143 auto res = std::make_shared<Response>();
2144
2145 return send(req, *res) ? res : nullptr;
2146}
2147
2148/*
2149 * SSL Implementation
2150 */
2151#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2152namespace detail {
2153
2154template <typename U, typename V, typename T>
2155inline bool read_and_close_socket_ssl(
2156 socket_t sock, size_t keep_alive_max_count,
2157 // TODO: OpenSSL 1.0.2 occasionally crashes...
2158 // The upcoming 1.1.0 is going to be thread safe.
2159 SSL_CTX* ctx, std::mutex& ctx_mutex,
2160 U SSL_connect_or_accept, V setup,
2161 T callback)
2162{
2163 SSL* ssl = nullptr;
2164 {
2165 std::lock_guard<std::mutex> guard(ctx_mutex);
2166
2167 ssl = SSL_new(ctx);
2168 if (!ssl) {
2169 return false;
2170 }
2171 }
2172
2173 auto bio = BIO_new_socket(sock, BIO_NOCLOSE);
2174 SSL_set_bio(ssl, bio, bio);
2175
2176 setup(ssl);
2177
2178 SSL_connect_or_accept(ssl);
2179
2180 bool ret = false;
2181
2182 if (keep_alive_max_count > 0) {
2183 auto count = keep_alive_max_count;
2184 while (count > 0 &&
2185 detail::select_read(sock,
2186 CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
2187 CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) {
2188 SSLSocketStream strm(sock, ssl);
2189 auto last_connection = count == 1;
2190 auto connection_close = false;
2191
2192 ret = callback(strm, last_connection, connection_close);
2193 if (!ret || connection_close) {
2194 break;
2195 }
2196
2197 count--;
2198 }
2199 } else {
2200 SSLSocketStream strm(sock, ssl);
2201 auto dummy_connection_close = false;
2202 ret = callback(strm, true, dummy_connection_close);
2203 }
2204
2205 SSL_shutdown(ssl);
2206
2207 {
2208 std::lock_guard<std::mutex> guard(ctx_mutex);
2209 SSL_free(ssl);
2210 }
2211
2212 close_socket(sock);
2213
2214 return ret;
2215}
2216
2217class SSLInit {
2218public:
2219 SSLInit() {
2220 SSL_load_error_strings();
2221 SSL_library_init();
2222 }
2223};
2224
2225static SSLInit sslinit_;
2226
2227} // namespace detail
2228
2229// SSL socket stream implementation
2230inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl)
2231 : sock_(sock), ssl_(ssl)
2232{
2233}
2234
2235inline SSLSocketStream::~SSLSocketStream()
2236{
2237}
2238
2239inline int SSLSocketStream::read(char* ptr, size_t size)
2240{
2241 return SSL_read(ssl_, ptr, size);
2242}
2243
2244inline int SSLSocketStream::write(const char* ptr, size_t size)
2245{
2246 return SSL_write(ssl_, ptr, size);
2247}
2248
2249inline int SSLSocketStream::write(const char* ptr)
2250{
2251 return write(ptr, strlen(ptr));
2252}
2253
2254inline std::string SSLSocketStream::get_remote_addr() {
2255 return detail::get_remote_addr(sock_);
2256}
2257
2258// SSL HTTP server implementation
2259inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path)
2260{
2261 ctx_ = SSL_CTX_new(SSLv23_server_method());
2262
2263 if (ctx_) {
2264 SSL_CTX_set_options(ctx_,
2265 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
2266 SSL_OP_NO_COMPRESSION |
2267 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
2268
2269 // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
2270 // SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
2271 // EC_KEY_free(ecdh);
2272
2273 if (SSL_CTX_use_certificate_file(ctx_, cert_path, SSL_FILETYPE_PEM) != 1 ||
2274 SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) {
2275 SSL_CTX_free(ctx_);
2276 ctx_ = nullptr;
2277 }
2278 }
2279}
2280
2281inline SSLServer::~SSLServer()
2282{
2283 if (ctx_) {
2284 SSL_CTX_free(ctx_);
2285 }
2286}
2287
2288inline bool SSLServer::is_valid() const
2289{
2290 return ctx_;
2291}
2292
2293inline bool SSLServer::read_and_close_socket(socket_t sock)
2294{
2295 return detail::read_and_close_socket_ssl(
2296 sock,
2297 keep_alive_max_count_,
2298 ctx_, ctx_mutex_,
2299 SSL_accept,
2300 [](SSL* /*ssl*/) {},
2301 [this](Stream& strm, bool last_connection, bool& connection_close) {
2302 return process_request(strm, last_connection, connection_close);
2303 });
2304}
2305
2306// SSL HTTP client implementation
2307inline SSLClient::SSLClient(const char* host, int port, size_t timeout_sec)
2308 : Client(host, port, timeout_sec)
2309{
2310 ctx_ = SSL_CTX_new(SSLv23_client_method());
2311}
2312
2313inline SSLClient::~SSLClient()
2314{
2315 if (ctx_) {
2316 SSL_CTX_free(ctx_);
2317 }
2318}
2319
2320inline bool SSLClient::is_valid() const
2321{
2322 return ctx_;
2323}
2324
2325inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Response& res)
2326{
2327 return is_valid() && detail::read_and_close_socket_ssl(
2328 sock, 0,
2329 ctx_, ctx_mutex_,
2330 SSL_connect,
2331 [&](SSL* ssl) {
2332 SSL_set_tlsext_host_name(ssl, host_.c_str());
2333 },
2334 [&](Stream& strm, bool /*last_connection*/, bool& connection_close) {
2335 return process_request(strm, req, res, connection_close);
2336 });
2337}
2338#endif
2339
2340} // namespace httplib
2341
2342#endif
2343
2344// vim: et ts=4 sw=4 cin cino={1s ff=unix