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