diff options
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 | ||
| 65 | typedef __int64 ssize_t; | ||
| 66 | #else | ||
| 67 | typedef 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 | ||
| 41 | typedef SOCKET socket_t; | 103 | typedef 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 | ||
| 53 | typedef int socket_t; | 123 | typedef 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> | ||
| 155 | inline 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 | /* | 165 | namespace httplib { |
| 78 | * Configuration | ||
| 79 | */ | ||
| 80 | #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 | ||
| 81 | #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 | ||
| 82 | |||
| 83 | namespace httplib | ||
| 84 | { | ||
| 85 | 166 | ||
| 86 | namespace detail { | 167 | namespace detail { |
| 87 | 168 | ||
| 88 | struct ci { | 169 | struct 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 | ||
| 101 | enum class HttpVersion { v1_0 = 0, v1_1 }; | 179 | enum class HttpVersion { v1_0 = 0, v1_1 }; |
| 102 | 180 | ||
| 103 | typedef std::multimap<std::string, std::string, detail::ci> Headers; | 181 | typedef std::multimap<std::string, std::string, detail::ci> Headers; |
| 182 | |||
| 183 | typedef std::multimap<std::string, std::string> Params; | ||
| 184 | typedef std::smatch Match; | ||
| 185 | |||
| 186 | typedef std::function<void(const char *data, size_t data_len)> DataSink; | ||
| 104 | 187 | ||
| 105 | template<typename uint64_t, typename... Args> | 188 | typedef std::function<void()> Done; |
| 106 | std::pair<std::string, std::string> make_range_header(uint64_t value, Args... args); | ||
| 107 | 189 | ||
| 108 | typedef std::multimap<std::string, std::string> Params; | 190 | typedef std::function<void(size_t offset, size_t length, DataSink sink, |
| 109 | typedef std::smatch Match; | 191 | Done done)> |
| 110 | typedef std::function<void (uint64_t current, uint64_t total)> Progress; | 192 | ContentProvider; |
| 193 | |||
| 194 | typedef std::function<bool(const char *data, size_t data_length, size_t offset, | ||
| 195 | uint64_t content_length)> | ||
| 196 | ContentReceiver; | ||
| 197 | |||
| 198 | typedef std::function<bool(uint64_t current, uint64_t total)> Progress; | ||
| 199 | |||
| 200 | struct Response; | ||
| 201 | typedef std::function<bool(const Response &response)> ResponseHandler; | ||
| 111 | 202 | ||
| 112 | struct MultipartFile { | 203 | struct 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 | }; |
| 118 | typedef std::multimap<std::string, MultipartFile> MultipartFiles; | 209 | typedef std::multimap<std::string, MultipartFile> MultipartFiles; |
| 119 | 210 | ||
| 211 | struct MultipartFormData { | ||
| 212 | std::string name; | ||
| 213 | std::string content; | ||
| 214 | std::string filename; | ||
| 215 | std::string content_type; | ||
| 216 | }; | ||
| 217 | typedef std::vector<MultipartFormData> MultipartFormDataItems; | ||
| 218 | |||
| 219 | typedef std::pair<ssize_t, ssize_t> Range; | ||
| 220 | typedef std::vector<Range> Ranges; | ||
| 221 | |||
| 120 | struct Request { | 222 | struct 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 | ||
| 144 | struct Response { | 260 | struct 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 | ||
| 161 | class Stream { | 298 | class Stream { |
| 162 | public: | 299 | public: |
| 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 | ||
| 173 | class SocketStream : public Stream { | 311 | class SocketStream : public Stream { |
| 174 | public: | 312 | public: |
| 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 | |||
| 322 | private: | ||
| 323 | socket_t sock_; | ||
| 324 | }; | ||
| 325 | |||
| 326 | class BufferStream : public Stream { | ||
| 327 | public: | ||
| 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 | |||
| 339 | private: | ||
| 340 | std::string buffer; | ||
| 341 | }; | ||
| 342 | |||
| 343 | class TaskQueue { | ||
| 344 | public: | ||
| 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); | 352 | class ThreadPool : public TaskQueue { |
| 180 | virtual int write(const char* ptr); | 353 | public: |
| 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 | ||
| 183 | private: | 386 | private: |
| 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 | ||
| 423 | class Threads : public TaskQueue { | ||
| 424 | public: | ||
| 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 | |||
| 452 | private: | ||
| 453 | std::mutex running_threads_mutex_; | ||
| 454 | int running_threads_; | ||
| 185 | }; | 455 | }; |
| 456 | #endif | ||
| 186 | 457 | ||
| 187 | class Server { | 458 | class Server { |
| 188 | public: | 459 | public: |
| 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 | ||
| 220 | protected: | 496 | protected: |
| 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 | ||
| 225 | private: | 504 | private: |
| 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 | ||
| 257 | class Client { | 539 | class Client { |
| 258 | public: | 540 | public: |
| 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 ¶ms); | ||
| 590 | |||
| 591 | std::shared_ptr<Response> Post(const char *path, const Headers &headers, | ||
| 592 | const Params ¶ms); | ||
| 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 | ||
| 291 | protected: | 638 | protected: |
| 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 | ||
| 299 | private: | 649 | private: |
| 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 | ||
| 664 | inline 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 | |||
| 673 | inline void Get(std::vector<Request> &requests, const char *path) { | ||
| 674 | Get(requests, path, Headers()); | ||
| 675 | } | ||
| 676 | |||
| 677 | inline 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 | |||
| 689 | inline 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 |
| 308 | class SSLSocketStream : public Stream { | 695 | class SSLSocketStream : public Stream { |
| 309 | public: | 696 | public: |
| 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 | ||
| 318 | private: | 706 | private: |
| 319 | socket_t sock_; | 707 | socket_t sock_; |
| 320 | SSL* ssl_; | 708 | SSL *ssl_; |
| 321 | }; | 709 | }; |
| 322 | 710 | ||
| 323 | class SSLServer : public Server { | 711 | class SSLServer : public Server { |
| 324 | public: | 712 | public: |
| 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 | ||
| 332 | private: | 721 | private: |
| 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 | ||
| 339 | class SSLClient : public Client { | 728 | class SSLClient : public Client { |
| 340 | public: | 729 | public: |
| 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 | ||
| 350 | private: | 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 | |||
| 746 | private: | ||
| 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 | */ |
| 361 | namespace detail { | 772 | namespace detail { |
| 362 | 773 | ||
| 363 | template <class Fn> | 774 | inline bool is_hex(char c, int &v) { |
| 364 | void 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')) { | 788 | inline 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 | |||
| 805 | inline 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 | |||
| 815 | inline 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 | ||
| 849 | inline 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) { | 877 | inline 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 | |||
| 882 | inline 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 | |||
| 887 | inline 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 | |||
| 924 | inline 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 | |||
| 933 | inline 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 | |||
| 940 | template <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. |
| 384 | class stream_line_reader { | 957 | class stream_line_reader { |
| 385 | public: | 958 | public: |
| 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 | ||
| 428 | private: | 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_; | 1005 | private: |
| 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 | ||
| 449 | inline int close_socket(socket_t sock) | 1026 | inline 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 | ||
| 458 | inline int select_read(socket_t sock, size_t sec, size_t usec) | 1034 | inline 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 | ||
| 471 | inline 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; | 1056 | inline 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 | ||
| 499 | template <typename T> | 1095 | template <typename T> |
| 500 | inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, T callback) | 1096 | inline 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 | ||
| 531 | inline int shutdown_socket(socket_t sock) | 1127 | inline 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 | ||
| 540 | template <typename Fn> | 1135 | template <typename Fn> |
| 541 | socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0) | 1136 | socket_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 | ||
| 591 | inline void set_nonblocking(socket_t sock, bool nonblocking) | 1197 | inline 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 | ||
| 602 | inline bool is_connection_error() | 1208 | inline 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 | ||
| 611 | inline std::string get_remote_addr(socket_t sock) { | 1216 | inline 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 | |||
| 1232 | inline 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 | |||
| 1264 | inline 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]; | 1286 | inline 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, | 1294 | inline 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 | ||
| 627 | inline 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 | ||
| 633 | inline 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 | ||
| 639 | inline bool is_valid_path(const std::string& path) { | 1328 | class decompressor { |
| 640 | size_t level = 0; | 1329 | public: |
| 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 | ||
| 678 | inline 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 | ||
| 688 | inline std::string file_extension(const std::string& path) | 1372 | private: |
| 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 | } | 1378 | inline bool has_header(const Headers &headers, const char *key) { |
| 695 | return std::string(); | 1379 | return headers.find(key) != headers.end(); |
| 696 | } | ||
| 697 | |||
| 698 | inline 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 | |||
| 731 | inline 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 | ||
| 748 | inline const char* get_header_value(const Headers& headers, const char* key, const char* def) | 1382 | inline 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 | ||
| 757 | inline int get_header_value_int(const Headers& headers, const char* key, int def) | 1390 | inline 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 | ||
| 766 | inline bool read_headers(Stream& strm, Headers& headers) | 1399 | inline 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 | ||
| 793 | inline bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress) | 1421 | typedef 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; | 1424 | inline 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 | ||
| 813 | inline 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 | ||
| 829 | inline bool read_content_chunked(Stream& strm, std::string& out) | 1447 | inline 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()) { | 1458 | inline 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){ | 1473 | inline 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 | ||
| 874 | template <typename T> | 1492 | if (!reader.getline()) { return false; } |
| 875 | bool 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 | |||
| 1505 | inline bool is_chunked_transfer_encoding(const Headers &headers) { | ||
| 1506 | return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), | ||
| 1507 | "chunked"); | ||
| 892 | } | 1508 | } |
| 893 | 1509 | ||
| 894 | template <typename T> | 1510 | template <typename T> |
| 895 | inline void write_headers(Stream& strm, const T& info) | 1511 | bool 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 | |||
| 903 | inline 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 | ||
| 931 | inline 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 | ||
| 946 | inline 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 | ||
| 967 | inline size_t to_utf8(int code, char* buff) | 1562 | template <typename T> |
| 968 | { | 1563 | inline 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) { | 1583 | inline 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 | |||
| 1601 | inline 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 | ||
| 1000 | inline std::string decode_url(const std::string& s) | 1629 | template <typename T> |
| 1001 | { | 1630 | inline 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 { | 1648 | inline 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 | |||
| 1679 | inline 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 | ||
| 1039 | inline void parse_query_text(const std::string& s, Params& params) | 1715 | inline void parse_query_text(const std::string &s, Params ¶ms) { |
| 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 | ||
| 1055 | inline bool parse_multipart_boundary(const std::string& content_type, std::string& boundary) | 1730 | inline 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 | ||
| 1066 | inline bool parse_multipart_formdata( | 1739 | inline 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 | ||
| 1151 | inline std::string to_lower(const char* beg, const char* end) | 1812 | inline 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 | |||
| 1846 | inline 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 | ||
| 1162 | inline void make_range_header_core(std::string&) {} | 1856 | inline std::string make_multipart_data_boundary() { |
| 1857 | static const char data[] = | ||
| 1858 | "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | ||
| 1163 | 1859 | ||
| 1164 | template<typename uint64_t> | 1860 | std::random_device seed_gen; |
| 1165 | inline 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 | ||
| 1173 | template<typename uint64_t, typename... Args> | 1872 | inline std::pair<size_t, size_t> |
| 1174 | inline void make_range_header_core(std::string& field, uint64_t value1, uint64_t value2, Args... args) | 1873 | get_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 | 1891 | inline std::string make_content_range_header_field(size_t offset, size_t length, |
| 1184 | inline 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 | } | |
| 1193 | inline 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); | 1902 | template <typename SToken, typename CToken, typename Content> |
| 1201 | if (ret != Z_OK) { | 1903 | bool 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); | 1937 | inline 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 | ||
| 1224 | inline void decompress(std::string& content) | 1954 | inline size_t |
| 1225 | { | 1955 | get_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; | 1972 | inline 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; | 1986 | inline std::pair<size_t, size_t> |
| 1245 | char buff[bufsiz]; | 1987 | get_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 |
| 1260 | class WSInit { | 1997 | class WSInit { |
| 1261 | public: | 1998 | public: |
| 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 | ||
| 1272 | static WSInit wsinit_; | 2007 | static WSInit wsinit_; |
| @@ -1275,876 +2010,1273 @@ static WSInit wsinit_; | |||
| 1275 | } // namespace detail | 2010 | } // namespace detail |
| 1276 | 2011 | ||
| 1277 | // Header utilities | 2012 | // Header utilities |
| 1278 | template<typename uint64_t, typename... Args> | 2013 | inline std::pair<std::string, std::string> make_range_header(Ranges ranges) { |
| 1279 | inline 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 | |||
| 2026 | inline std::pair<std::string, std::string> | ||
| 2027 | make_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 |
| 1288 | inline bool Request::has_header(const char* key) const | 2034 | inline 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 | ||
| 1293 | inline std::string Request::get_header_value(const char* key) const | 2038 | inline 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 | ||
| 1298 | inline void Request::set_header(const char* key, const char* val) | 2042 | inline 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 | ||
| 1303 | inline bool Request::has_param(const char* key) const | 2047 | inline 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 | ||
| 1308 | inline std::string Request::get_param_value(const char* key) const | 2051 | inline 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 | ||
| 1317 | inline bool Request::has_file(const char* key) const | 2055 | inline 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 | ||
| 1322 | inline MultipartFile Request::get_file_value(const char* key) const | 2059 | inline 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 | |
| 2066 | inline 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 | |||
| 2071 | inline bool Request::has_file(const char *key) const { | ||
| 2072 | return files.find(key) != files.end(); | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | inline 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 |
| 1332 | inline bool Response::has_header(const char* key) const | 2082 | inline 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 | |||
| 2086 | inline 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 | |||
| 2091 | inline 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 | ||
| 1337 | inline std::string Response::get_header_value(const char* key) const | 2096 | inline 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 | ||
| 1342 | inline void Response::set_header(const char* key, const char* val) | 2100 | inline 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 | ||
| 1347 | inline void Response::set_redirect(const char* url) | 2104 | inline 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 | ||
| 1353 | inline void Response::set_content(const char* s, size_t n, const char* content_type) | 2109 | inline 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 | ||
| 1359 | inline void Response::set_content(const std::string& s, const char* content_type) | 2115 | inline 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 | |||
| 2121 | inline 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 | |||
| 2132 | inline 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 |
| 1366 | template <typename ...Args> | 2142 | template <typename... Args> |
| 1367 | inline void Stream::write_format(const char* fmt, const Args& ...args) | 2143 | inline 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 |
| 1397 | inline SocketStream::SocketStream(socket_t sock): sock_(sock) | 2173 | inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} |
| 1398 | { | 2174 | |
| 2175 | inline SocketStream::~SocketStream() {} | ||
| 2176 | |||
| 2177 | inline 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 | |||
| 2185 | inline int SocketStream::write(const char *ptr, size_t size) { | ||
| 2186 | return send(sock_, ptr, static_cast<int>(size), 0); | ||
| 1399 | } | 2187 | } |
| 1400 | 2188 | ||
| 1401 | inline SocketStream::~SocketStream() | 2189 | inline int SocketStream::write(const char *ptr) { |
| 1402 | { | 2190 | return write(ptr, strlen(ptr)); |
| 1403 | } | 2191 | } |
| 1404 | 2192 | ||
| 1405 | inline int SocketStream::read(char* ptr, size_t size) | 2193 | inline 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 | ||
| 1410 | inline int SocketStream::write(const char* ptr, size_t size) | 2197 | inline 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 | ||
| 1415 | inline int SocketStream::write(const char* ptr) | 2201 | // Buffer stream implementation |
| 1416 | { | 2202 | inline 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 | ||
| 1420 | inline std::string SocketStream::get_remote_addr() { | 2210 | inline 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 | ||
| 2215 | inline int BufferStream::write(const char *ptr) { | ||
| 2216 | return write(ptr, strlen(ptr)); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | inline int BufferStream::write(const std::string &s) { | ||
| 2220 | return write(s.data(), s.size()); | ||
| 2221 | } | ||
| 2222 | |||
| 2223 | inline std::string BufferStream::get_remote_addr() const { return ""; } | ||
| 2224 | |||
| 2225 | inline const std::string &BufferStream::get_buffer() const { return buffer; } | ||
| 2226 | |||
| 1424 | // HTTP server implementation | 2227 | // HTTP server implementation |
| 1425 | inline Server::Server() | 2228 | inline 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 | ||
| 1436 | inline Server::~Server() | 2244 | inline Server::~Server() {} |
| 1437 | { | ||
| 1438 | } | ||
| 1439 | 2245 | ||
| 1440 | inline Server& Server::Get(const char* pattern, Handler handler) | 2246 | inline 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 | ||
| 1446 | inline Server& Server::Post(const char* pattern, Handler handler) | 2251 | inline 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 | ||
| 1452 | inline Server& Server::Put(const char* pattern, Handler handler) | 2256 | inline 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 | ||
| 1458 | inline Server& Server::Delete(const char* pattern, Handler handler) | 2261 | inline 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 | ||
| 1464 | inline Server& Server::Options(const char* pattern, Handler handler) | 2266 | inline 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 | ||
| 1470 | inline bool Server::set_base_dir(const char* path) | 2271 | inline 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 | ||
| 1479 | inline void Server::set_error_handler(Handler handler) | 2276 | inline 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 | ||
| 1484 | inline void Server::set_logger(Logger logger) | 2284 | inline void Server::set_file_request_handler(Handler handler) { |
| 1485 | { | 2285 | file_request_handler_ = handler; |
| 1486 | logger_ = logger; | ||
| 1487 | } | 2286 | } |
| 1488 | 2287 | ||
| 1489 | inline void Server::set_keep_alive_max_count(size_t count) | 2288 | inline void Server::set_error_handler(Handler handler) { |
| 1490 | { | 2289 | error_handler_ = handler; |
| 1491 | keep_alive_max_count_ = count; | ||
| 1492 | } | 2290 | } |
| 1493 | 2291 | ||
| 1494 | inline int Server::bind_to_any_port(const char* host, int socket_flags) | 2292 | inline void Server::set_logger(Logger logger) { logger_ = logger; } |
| 1495 | { | 2293 | |
| 1496 | return bind_internal(host, 0, socket_flags); | 2294 | inline void Server::set_keep_alive_max_count(size_t count) { |
| 2295 | keep_alive_max_count_ = count; | ||
| 1497 | } | 2296 | } |
| 1498 | 2297 | ||
| 1499 | inline bool Server::listen_after_bind() { | 2298 | inline void Server::set_payload_max_length(size_t length) { |
| 1500 | return listen_internal(); | 2299 | payload_max_length_ = length; |
| 1501 | } | 2300 | } |
| 1502 | 2301 | ||
| 1503 | inline bool Server::listen(const char* host, int port, int socket_flags) | 2302 | inline 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 | ||
| 1510 | inline bool Server::is_running() const | 2306 | inline bool Server::listen_after_bind() { return listen_internal(); } |
| 1511 | { | 2307 | |
| 1512 | return is_running_; | 2308 | inline 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 | ||
| 1515 | inline void Server::stop() | 2313 | inline bool Server::is_running() const { return is_running_; } |
| 1516 | { | 2314 | |
| 1517 | if (is_running_) { | 2315 | inline 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 | ||
| 1525 | inline bool Server::parse_request_line(const char* s, Request& req) | 2324 | inline 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 | ||
| 1548 | inline 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_) { | 2345 | inline 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 | 2467 | inline bool |
| 1595 | if (logger_) { | 2468 | Server::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 | ||
| 1600 | inline bool Server::handle_file_request(Request& req, Response& res) | 2500 | inline 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 | ||
| 1623 | inline socket_t Server::create_server_socket(const char* host, int port, int socket_flags) const | 2519 | inline 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 | |||
| 1637 | inline 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 | |||
| 2535 | inline 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 | ||
| 1666 | inline bool Server::listen_internal() | 2561 | inline 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(); | 2608 | inline 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 | |||
| 2629 | inline 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 | |||
| 2643 | inline bool | ||
| 2644 | Server::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 | ||
| 1725 | inline bool Server::routing(Request& req, Response& res) | 2731 | inline 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") { | 2733 | inline 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 | ||
| 1745 | inline bool Server::dispatch_request(Request& req, Response& res, Handlers& handlers) | 2742 | // HTTP client implementation |
| 1746 | { | 2743 | inline 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); | 2749 | inline Client::~Client() {} |
| 1753 | return true; | 2750 | |
| 2751 | inline bool Client::is_valid() const { return true; } | ||
| 2752 | |||
| 2753 | inline 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 | ||
| 1759 | inline 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); | 2772 | inline 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()); | 2791 | inline 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)) { | 2810 | inline 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 | ||
| 1836 | inline 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 | ||
| 1841 | inline 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)); } |
| 1852 | inline 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 | ||
| 1861 | inline Client::~Client() | 2835 | return ret; |
| 1862 | { | 2836 | })) { |
| 1863 | } | 2837 | return false; |
| 2838 | } | ||
| 2839 | } | ||
| 1864 | 2840 | ||
| 1865 | inline bool Client::is_valid() const | 2841 | return true; |
| 1866 | { | ||
| 1867 | return true; | ||
| 1868 | } | 2842 | } |
| 1869 | 2843 | ||
| 1870 | inline socket_t Client::create_client_socket() const | 2844 | inline 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 | ||
| 1890 | inline 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"); | 2884 | inline 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 | ||
| 1912 | inline 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 | ||
| 1926 | inline void Client::write_request(Stream& strm, Request& req) | 2944 | inline 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()); | 2994 | inline 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); | 3003 | inline bool Client::is_ssl() const { return false; } |
| 1961 | 3004 | ||
| 1962 | // Body | 3005 | inline 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 | ||
| 1973 | inline bool Client::process_request(Stream& strm, Request& req, Response& res, bool& connection_close) | 3010 | inline 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 | 3015 | inline 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") { | 3021 | inline std::shared_ptr<Response> |
| 1984 | connection_close = true; | 3022 | Client::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") { | 3033 | inline 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; | 3039 | inline 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 | ||
| 2005 | inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res) | 3045 | inline 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 | ||
| 2015 | inline std::shared_ptr<Response> Client::Get(const char* path, Progress progress) | 3052 | inline 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 | ||
| 2020 | inline std::shared_ptr<Response> Client::Get(const char* path, const Headers& headers, Progress progress) | 3059 | inline 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>(); | 3067 | inline 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 | ||
| 2033 | inline std::shared_ptr<Response> Client::Head(const char* path) | 3084 | inline std::shared_ptr<Response> Client::Head(const char *path) { |
| 2034 | { | 3085 | return Head(path, Headers()); |
| 2035 | return Head(path, Headers()); | ||
| 2036 | } | 3086 | } |
| 2037 | 3087 | ||
| 2038 | inline std::shared_ptr<Response> Client::Head(const char* path, const Headers& headers) | 3088 | inline 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 | ||
| 2050 | inline std::shared_ptr<Response> Client::Post( | 3100 | inline 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 | ||
| 2056 | inline std::shared_ptr<Response> Client::Post( | 3106 | inline 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 | ||
| 2072 | inline std::shared_ptr<Response> Client::Post(const char* path, const Params& params) | 3123 | inline std::shared_ptr<Response> Client::Post(const char *path, |
| 2073 | { | 3124 | const Params ¶ms) { |
| 2074 | return Post(path, Headers(), params); | 3125 | return Post(path, Headers(), params); |
| 2075 | } | 3126 | } |
| 2076 | 3127 | ||
| 2077 | inline std::shared_ptr<Response> Client::Post(const char* path, const Headers& headers, const Params& params) | 3128 | inline std::shared_ptr<Response> |
| 2078 | { | 3129 | Client::Post(const char *path, const Headers &headers, const Params ¶ms) { |
| 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 | |||
| 3141 | inline std::shared_ptr<Response> | ||
| 3142 | Client::Post(const char *path, const MultipartFormDataItems &items) { | ||
| 3143 | return Post(path, Headers(), items); | ||
| 3144 | } | ||
| 3145 | |||
| 3146 | inline std::shared_ptr<Response> | ||
| 3147 | Client::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 | |||
| 3180 | inline 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 | |||
| 3186 | inline 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 | ||
| 2092 | inline std::shared_ptr<Response> Client::Put( | 3203 | inline 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 | ||
| 2098 | inline std::shared_ptr<Response> Client::Put( | 3209 | inline 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 | ||
| 2114 | inline std::shared_ptr<Response> Client::Delete(const char* path) | 3226 | inline 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 | ||
| 2119 | inline std::shared_ptr<Response> Client::Delete(const char* path, const Headers& headers) | 3230 | inline 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 | |
| 3236 | inline 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>(); | 3241 | inline 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 | ||
| 2131 | inline std::shared_ptr<Response> Client::Options(const char* path) | 3258 | inline std::shared_ptr<Response> Client::Options(const char *path) { |
| 2132 | { | 3259 | return Options(path, Headers()); |
| 2133 | return Options(path, Headers()); | ||
| 2134 | } | 3260 | } |
| 2135 | 3261 | ||
| 2136 | inline std::shared_ptr<Response> Client::Options(const char* path, const Headers& headers) | 3262 | inline 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; | 3274 | inline void Client::set_keep_alive_max_count(size_t count) { |
| 3275 | keep_alive_max_count_ = count; | ||
| 2146 | } | 3276 | } |
| 2147 | 3277 | ||
| 3278 | inline 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 | |||
| 2152 | namespace detail { | 3284 | namespace detail { |
| 2153 | 3285 | ||
| 2154 | template <typename U, typename V, typename T> | 3286 | template <typename U, typename V, typename T> |
| 2155 | inline bool read_and_close_socket_ssl( | 3287 | inline 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 | ||
| 2217 | class SSLInit { | 3355 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
| 3356 | static std::shared_ptr<std::vector<std::mutex>> openSSL_locks_; | ||
| 3357 | |||
| 3358 | class SSLThreadLocks { | ||
| 2218 | public: | 3359 | public: |
| 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 | |||
| 3368 | private: | ||
| 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 | |||
| 3382 | class SSLInit { | ||
| 3383 | public: | ||
| 3384 | SSLInit() { | ||
| 3385 | SSL_load_error_strings(); | ||
| 3386 | SSL_library_init(); | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | ~SSLInit() { ERR_free_strings(); } | ||
| 3390 | |||
| 3391 | private: | ||
| 3392 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | ||
| 3393 | SSLThreadLocks thread_init_; | ||
| 3394 | #endif | ||
| 2223 | }; | 3395 | }; |
| 2224 | 3396 | ||
| 2225 | static SSLInit sslinit_; | 3397 | static 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 |
| 2230 | inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl) | 3402 | inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) |
| 2231 | : sock_(sock), ssl_(ssl) | 3403 | : sock_(sock), ssl_(ssl) {} |
| 2232 | { | ||
| 2233 | } | ||
| 2234 | 3404 | ||
| 2235 | inline SSLSocketStream::~SSLSocketStream() | 3405 | inline SSLSocketStream::~SSLSocketStream() {} |
| 2236 | { | 3406 | |
| 3407 | inline 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 | ||
| 2239 | inline int SSLSocketStream::read(char* ptr, size_t size) | 3416 | inline 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 | ||
| 2244 | inline int SSLSocketStream::write(const char* ptr, size_t size) | 3420 | inline int SSLSocketStream::write(const char *ptr) { |
| 2245 | { | 3421 | return write(ptr, strlen(ptr)); |
| 2246 | return SSL_write(ssl_, ptr, size); | ||
| 2247 | } | 3422 | } |
| 2248 | 3423 | ||
| 2249 | inline int SSLSocketStream::write(const char* ptr) | 3424 | inline 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 | ||
| 2254 | inline std::string SSLSocketStream::get_remote_addr() { | 3428 | inline 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 |
| 2259 | inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path) | 3433 | inline 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 | |||
| 3471 | inline SSLServer::~SSLServer() { | ||
| 3472 | if (ctx_) { SSL_CTX_free(ctx_); } | ||
| 3473 | } | ||
| 3474 | |||
| 3475 | inline bool SSLServer::is_valid() const { return ctx_; } | ||
| 3476 | |||
| 3477 | inline 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 | ||
| 2281 | inline SSLServer::~SSLServer() | 3488 | // SSL HTTP client implementation |
| 2282 | { | 3489 | inline 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 | ||
| 2288 | inline bool SSLServer::is_valid() const | 3510 | inline SSLClient::~SSLClient() { |
| 2289 | { | 3511 | if (ctx_) { SSL_CTX_free(ctx_); } |
| 2290 | return ctx_; | ||
| 2291 | } | 3512 | } |
| 2292 | 3513 | ||
| 2293 | inline bool SSLServer::read_and_close_socket(socket_t sock) | 3514 | inline bool SSLClient::is_valid() const { return ctx_; } |
| 2294 | { | 3515 | |
| 2295 | return detail::read_and_close_socket_ssl( | 3516 | inline 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 | 3522 | inline void SSLClient::enable_server_certificate_verification(bool enabled) { |
| 2307 | inline 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 | ||
| 2313 | inline SSLClient::~SSLClient() | 3526 | inline 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 | ||
| 2320 | inline bool SSLClient::is_valid() const | 3530 | inline SSL_CTX* SSLClient::ssl_context() const noexcept { |
| 2321 | { | 3531 | return ctx_; |
| 2322 | return ctx_; | ||
| 2323 | } | 3532 | } |
| 2324 | 3533 | ||
| 2325 | inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Response& res) | 3534 | inline 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 | |||
| 3586 | inline bool SSLClient::is_ssl() const { return true; } | ||
| 3587 | |||
| 3588 | inline 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 | |||
| 3614 | inline bool | ||
| 3615 | SSLClient::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 | |||
| 3672 | inline 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 | |||
| 3686 | inline 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 | ||
| 525 | if (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 | ) | ||
| 540 | endif() | ||
| 541 | |||
| 525 | create_target_directory_groups(core) | 542 | create_target_directory_groups(core) |
| 526 | 543 | ||
| 527 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 544 | target_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 | ||
| 155 | void ARM_Unicorn::Run() { | 156 | void 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 | ||
| 167 | MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); | 169 | MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); |
| 168 | 170 | ||
| 169 | void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | 171 | void 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) { | |||
| 378 | template <size_t size> | 379 | template <size_t size> |
| 379 | static std::array<u8, size> operator^(const std::array<u8, size>& lhs, | 380 | static 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 | ||
| 205 | FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, | 205 | FileSys::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 | ||
| 149 | s32 KIP::GetMainThreadPriority() const { | 149 | s32 KIP::GetMainThreadPriority() const { |
| 150 | return header.main_thread_priority; | 150 | return static_cast<s32>(header.main_thread_priority); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | u32 KIP::GetMainThreadStackSize() const { | 153 | u32 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 | ||
| 54 | void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, | 54 | void 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. |
| 470 | static u8 CalculateChecksum(const u8* buffer, std::size_t length) { | 470 | static 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 | ||
| 38 | void GlobalScheduler::UnloadThread(s32 core) { | 38 | void 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 | ||
| 43 | void GlobalScheduler::SelectThread(u32 core) { | 43 | void 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 | ||
| 295 | void GlobalScheduler::Suggest(u32 priority, u32 core, Thread* thread) { | 295 | void 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 | ||
| 299 | void GlobalScheduler::Unsuggest(u32 priority, u32 core, Thread* thread) { | 299 | void 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 | ||
| 303 | void GlobalScheduler::Schedule(u32 priority, u32 core, Thread* thread) { | 303 | void 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 | ||
| 308 | void GlobalScheduler::SchedulePrepend(u32 priority, u32 core, Thread* thread) { | 308 | void 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 | ||
| 313 | void GlobalScheduler::Reschedule(u32 priority, u32 core, Thread* thread) { | 313 | void 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 | ||
| 318 | void GlobalScheduler::Unschedule(u32 priority, u32 core, Thread* thread) { | 318 | void 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 | ||
| 360 | Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, u32 core_id) | 360 | Scheduler::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 | ||
| 363 | Scheduler::~Scheduler() = default; | 363 | Scheduler::~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 | ||
| 147 | private: | 140 | private: |
| 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 | ||
| 164 | class Scheduler final { | 164 | class Scheduler final { |
| 165 | public: | 165 | public: |
| 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 | ||
| 80 | static 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 | |||
| 92 | void Thread::ResumeFromWait() { | 80 | void 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 | ||
| 402 | ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | 390 | ResultCode 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) { | |||
| 147 | constexpr ResultCode RESULT_SUCCESS(0); | 147 | constexpr 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 | */ | ||
| 155 | constexpr 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 { | |||
| 183 | public: | 191 | public: |
| 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 { | |||
| 31 | static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); | 31 | static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); |
| 32 | 32 | ||
| 33 | // TODO(ogniK): Get actual error codes | 33 | // TODO(ogniK): Get actual error codes |
| 34 | constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); | 34 | constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1)); |
| 35 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); | 35 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); |
| 36 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); | 36 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); |
| 37 | 37 | ||
| 38 | constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/"; | 38 | constexpr 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() { | |||
| 337 | void WebBrowser::InitializeShop() { | 337 | void 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 | ||
| 195 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, | 195 | ResultVal<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 | ||
| 791 | void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { | 791 | void 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 | ||
| 18 | namespace Service::NFP { | 18 | namespace Service::NFP { |
| 19 | |||
| 20 | namespace ErrCodes { | 19 | namespace ErrCodes { |
| 21 | [[maybe_unused]] constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, | ||
| 22 | -1); // TODO(ogniK): Find the actual error code | ||
| 23 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); | 20 | constexpr 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 | ||