| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | #ifndef CPPHTTPLIB_HTTPLIB_H
|
| | #define CPPHTTPLIB_HTTPLIB_H
|
| |
|
| | #define CPPHTTPLIB_VERSION "0.35.0"
|
| | #define CPPHTTPLIB_VERSION_NUM "0x002300"
|
| |
|
| | |
| | |
| |
|
| |
|
| | #if defined(_WIN32) && !defined(_WIN64)
|
| | #if defined(_MSC_VER)
|
| | #pragma message( \
|
| | "cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler.")
|
| | #else
|
| | #warning \
|
| | "cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler."
|
| | #endif
|
| | #elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ < 8
|
| | #warning \
|
| | "cpp-httplib doesn't support 32-bit platforms. Please use a 64-bit compiler."
|
| | #elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ < 8
|
| | #warning \
|
| | "cpp-httplib doesn't support platforms where size_t is less than 64 bits."
|
| | #endif
|
| |
|
| | #ifdef _WIN32
|
| | #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
|
| | #error \
|
| | "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later."
|
| | #endif
|
| | #endif
|
| |
|
| | |
| | |
| |
|
| |
|
| | #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
|
| | #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
|
| | #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
|
| | #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
|
| | #define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
|
| | #define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
|
| | #define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
|
| | #define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
|
| | #define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_EXPECT_100_THRESHOLD
|
| | #define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
|
| | #define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
|
| | #define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024)
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND
|
| | #define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
|
| | #define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
|
| | #ifdef _WIN32
|
| | #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
|
| | #else
|
| | #define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
|
| | #endif
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
|
| | #define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
|
| | #define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_HEADER_MAX_COUNT
|
| | #define CPPHTTPLIB_HEADER_MAX_COUNT 100
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
|
| | #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
|
| | #define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
|
| | #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (100 * 1024 * 1024)
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
|
| | #define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_RANGE_MAX_COUNT
|
| | #define CPPHTTPLIB_RANGE_MAX_COUNT 1024
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_TCP_NODELAY
|
| | #define CPPHTTPLIB_TCP_NODELAY false
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_IPV6_V6ONLY
|
| | #define CPPHTTPLIB_IPV6_V6ONLY false
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_RECV_BUFSIZ
|
| | #define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_SEND_BUFSIZ
|
| | #define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
|
| | #define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_THREAD_POOL_COUNT
|
| | #define CPPHTTPLIB_THREAD_POOL_COUNT \
|
| | ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
|
| | ? std::thread::hardware_concurrency() - 1 \
|
| | : 0))
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_THREAD_POOL_MAX_COUNT
|
| | #define CPPHTTPLIB_THREAD_POOL_MAX_COUNT (CPPHTTPLIB_THREAD_POOL_COUNT * 4)
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT
|
| | #define CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT 3
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_RECV_FLAGS
|
| | #define CPPHTTPLIB_RECV_FLAGS 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_SEND_FLAGS
|
| | #define CPPHTTPLIB_SEND_FLAGS 0
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_LISTEN_BACKLOG
|
| | #define CPPHTTPLIB_LISTEN_BACKLOG 5
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_MAX_LINE_LENGTH
|
| | #define CPPHTTPLIB_MAX_LINE_LENGTH 32768
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH
|
| | #define CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH 16777216
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 300
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND
|
| | #define CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND 5
|
| | #endif
|
| |
|
| | #ifndef CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND
|
| | #define CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND 30
|
| | #endif
|
| |
|
| | |
| | |
| |
|
| |
|
| | #ifdef _WIN32
|
| | #ifndef _CRT_SECURE_NO_WARNINGS
|
| | #define _CRT_SECURE_NO_WARNINGS
|
| | #endif
|
| |
|
| | #ifndef _CRT_NONSTDC_NO_DEPRECATE
|
| | #define _CRT_NONSTDC_NO_DEPRECATE
|
| | #endif
|
| |
|
| | #if defined(_MSC_VER)
|
| | #if _MSC_VER < 1900
|
| | #error Sorry, Visual Studio versions prior to 2015 are not supported
|
| | #endif
|
| |
|
| | #pragma comment(lib, "ws2_32.lib")
|
| |
|
| | #ifndef _SSIZE_T_DEFINED
|
| | using ssize_t = __int64;
|
| | #define _SSIZE_T_DEFINED
|
| | #endif
|
| | #endif
|
| |
|
| | #ifndef S_ISREG
|
| | #define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
|
| | #endif
|
| |
|
| | #ifndef S_ISDIR
|
| | #define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
|
| | #endif
|
| |
|
| | #ifndef NOMINMAX
|
| | #define NOMINMAX
|
| | #endif
|
| |
|
| | #include <io.h>
|
| | #include <winsock2.h>
|
| | #include <ws2tcpip.h>
|
| |
|
| | #if defined(__has_include)
|
| | #if __has_include(<afunix.h>)
|
| |
|
| | #include <afunix.h>
|
| | #define CPPHTTPLIB_HAVE_AFUNIX_H 1
|
| | #endif
|
| | #endif
|
| |
|
| | #ifndef WSA_FLAG_NO_HANDLE_INHERIT
|
| | #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
|
| | #endif
|
| |
|
| | using nfds_t = unsigned long;
|
| | using socket_t = SOCKET;
|
| | using socklen_t = int;
|
| |
|
| | #else
|
| |
|
| | #include <arpa/inet.h>
|
| | #if !defined(_AIX) && !defined(__MVS__)
|
| | #include <ifaddrs.h>
|
| | #endif
|
| | #ifdef __MVS__
|
| | #include <strings.h>
|
| | #ifndef NI_MAXHOST
|
| | #define NI_MAXHOST 1025
|
| | #endif
|
| | #endif
|
| | #include <net/if.h>
|
| | #include <netdb.h>
|
| | #include <netinet/in.h>
|
| | #ifdef __linux__
|
| | #include <resolv.h>
|
| | #undef _res
|
| | #endif
|
| | #include <csignal>
|
| | #include <netinet/tcp.h>
|
| | #include <poll.h>
|
| | #include <pthread.h>
|
| | #include <sys/mman.h>
|
| | #include <sys/socket.h>
|
| | #include <sys/un.h>
|
| | #include <unistd.h>
|
| |
|
| | using socket_t = int;
|
| | #ifndef INVALID_SOCKET
|
| | #define INVALID_SOCKET (-1)
|
| | #endif
|
| | #endif
|
| |
|
| | #if defined(__APPLE__)
|
| | #include <TargetConditionals.h>
|
| | #endif
|
| |
|
| | #include <algorithm>
|
| | #include <array>
|
| | #include <atomic>
|
| | #include <cassert>
|
| | #include <cctype>
|
| | #include <chrono>
|
| | #include <climits>
|
| | #include <condition_variable>
|
| | #include <cstdlib>
|
| | #include <cstring>
|
| | #include <errno.h>
|
| | #include <exception>
|
| | #include <fcntl.h>
|
| | #include <fstream>
|
| | #include <functional>
|
| | #include <iomanip>
|
| | #include <iostream>
|
| | #include <list>
|
| | #include <map>
|
| | #include <memory>
|
| | #include <mutex>
|
| | #include <random>
|
| | #include <regex>
|
| | #include <set>
|
| | #include <sstream>
|
| | #include <string>
|
| | #include <sys/stat.h>
|
| | #include <system_error>
|
| | #include <thread>
|
| | #include <unordered_map>
|
| | #include <unordered_set>
|
| | #include <utility>
|
| | #if __cplusplus >= 201703L
|
| | #include <any>
|
| | #endif
|
| |
|
| |
|
| |
|
| | #if defined(__APPLE__) && \
|
| | !defined(CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES) && \
|
| | (defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \
|
| | defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || \
|
| | defined(CPPHTTPLIB_WOLFSSL_SUPPORT))
|
| | #ifndef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
| | #define CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
| | #endif
|
| | #endif
|
| |
|
| |
|
| |
|
| | #if defined(_WIN32) && \
|
| | !defined(CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE)
|
| | #define CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
| | #endif
|
| |
|
| | #if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) || \
|
| | defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
|
| | #if TARGET_OS_MAC
|
| | #include <CFNetwork/CFHost.h>
|
| | #include <CoreFoundation/CoreFoundation.h>
|
| | #endif
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
| | #ifdef _WIN32
|
| | #include <wincrypt.h>
|
| |
|
| |
|
| |
|
| | #undef X509_NAME
|
| | #undef X509_CERT_PAIR
|
| | #undef X509_EXTENSIONS
|
| | #undef PKCS7_SIGNER_INFO
|
| |
|
| | #ifdef _MSC_VER
|
| | #pragma comment(lib, "crypt32.lib")
|
| | #endif
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
| | #if TARGET_OS_MAC
|
| | #include <Security/Security.h>
|
| | #endif
|
| | #endif
|
| |
|
| | #include <openssl/err.h>
|
| | #include <openssl/evp.h>
|
| | #include <openssl/ssl.h>
|
| | #include <openssl/x509v3.h>
|
| |
|
| | #if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
|
| | #include <openssl/applink.c>
|
| | #endif
|
| |
|
| | #include <iostream>
|
| | #include <sstream>
|
| |
|
| | #if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
|
| | #if OPENSSL_VERSION_NUMBER < 0x1010107f
|
| | #error Please use OpenSSL or a current version of BoringSSL
|
| | #endif
|
| | #define SSL_get1_peer_certificate SSL_get_peer_certificate
|
| | #elif OPENSSL_VERSION_NUMBER < 0x30000000L
|
| | #error Sorry, OpenSSL versions prior to 3.0.0 are not supported
|
| | #endif
|
| |
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
|
| | #include <mbedtls/ctr_drbg.h>
|
| | #include <mbedtls/entropy.h>
|
| | #include <mbedtls/error.h>
|
| | #include <mbedtls/md5.h>
|
| | #include <mbedtls/net_sockets.h>
|
| | #include <mbedtls/oid.h>
|
| | #include <mbedtls/pk.h>
|
| | #include <mbedtls/sha1.h>
|
| | #include <mbedtls/sha256.h>
|
| | #include <mbedtls/sha512.h>
|
| | #include <mbedtls/ssl.h>
|
| | #include <mbedtls/x509_crt.h>
|
| | #ifdef _WIN32
|
| | #include <wincrypt.h>
|
| | #ifdef _MSC_VER
|
| | #pragma comment(lib, "crypt32.lib")
|
| | #endif
|
| | #endif
|
| | #ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
| | #if TARGET_OS_MAC
|
| | #include <Security/Security.h>
|
| | #endif
|
| | #endif
|
| |
|
| |
|
| | #if MBEDTLS_VERSION_MAJOR >= 3
|
| | #define CPPHTTPLIB_MBEDTLS_V3
|
| | #endif
|
| |
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
|
| | #include <wolfssl/options.h>
|
| |
|
| | #include <wolfssl/openssl/x509v3.h>
|
| |
|
| |
|
| | #ifndef WOLFSSL_GEN_EMAIL
|
| | #define WOLFSSL_GEN_EMAIL 1
|
| | #endif
|
| | #ifndef WOLFSSL_GEN_DNS
|
| | #define WOLFSSL_GEN_DNS 2
|
| | #endif
|
| | #ifndef WOLFSSL_GEN_URI
|
| | #define WOLFSSL_GEN_URI 6
|
| | #endif
|
| | #ifndef WOLFSSL_GEN_IPADD
|
| | #define WOLFSSL_GEN_IPADD 7
|
| | #endif
|
| |
|
| | #include <wolfssl/ssl.h>
|
| | #include <wolfssl/wolfcrypt/hash.h>
|
| | #include <wolfssl/wolfcrypt/md5.h>
|
| | #include <wolfssl/wolfcrypt/sha256.h>
|
| | #include <wolfssl/wolfcrypt/sha512.h>
|
| | #ifdef _WIN32
|
| | #include <wincrypt.h>
|
| | #ifdef _MSC_VER
|
| | #pragma comment(lib, "crypt32.lib")
|
| | #endif
|
| | #endif
|
| | #ifdef CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
| | #if TARGET_OS_MAC
|
| | #include <Security/Security.h>
|
| | #endif
|
| | #endif
|
| | #endif
|
| |
|
| |
|
| | #if defined(CPPHTTPLIB_OPENSSL_SUPPORT) || \
|
| | defined(CPPHTTPLIB_MBEDTLS_SUPPORT) || defined(CPPHTTPLIB_WOLFSSL_SUPPORT)
|
| | #define CPPHTTPLIB_SSL_ENABLED
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
| | #include <zlib.h>
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
| | #include <brotli/decode.h>
|
| | #include <brotli/encode.h>
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
| | #include <zstd.h>
|
| | #endif
|
| |
|
| | |
| | |
| |
|
| | namespace httplib {
|
| |
|
| | namespace ws {
|
| | class WebSocket;
|
| | }
|
| |
|
| | namespace detail {
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | template <class T, class... Args>
|
| | typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
|
| | make_unique(Args &&...args) {
|
| | return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
| | }
|
| |
|
| | template <class T>
|
| | typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
|
| | make_unique(std::size_t n) {
|
| | typedef typename std::remove_extent<T>::type RT;
|
| | return std::unique_ptr<T>(new RT[n]);
|
| | }
|
| |
|
| | namespace case_ignore {
|
| |
|
| | inline unsigned char to_lower(int c) {
|
| | const static unsigned char table[256] = {
|
| | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
| | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
| | 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
|
| | 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
|
| | 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
|
| | 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
|
| | 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
|
| | 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
|
| | 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
|
| | 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
|
| | 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
|
| | 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
|
| | 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
|
| | 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
|
| | 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
|
| | 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
|
| | 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
|
| | 255,
|
| | };
|
| | return table[(unsigned char)(char)c];
|
| | }
|
| |
|
| | inline bool equal(const std::string &a, const std::string &b) {
|
| | return a.size() == b.size() &&
|
| | std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
|
| | return to_lower(ca) == to_lower(cb);
|
| | });
|
| | }
|
| |
|
| | struct equal_to {
|
| | bool operator()(const std::string &a, const std::string &b) const {
|
| | return equal(a, b);
|
| | }
|
| | };
|
| |
|
| | struct hash {
|
| | size_t operator()(const std::string &key) const {
|
| | return hash_core(key.data(), key.size(), 0);
|
| | }
|
| |
|
| | size_t hash_core(const char *s, size_t l, size_t h) const {
|
| | return (l == 0) ? h
|
| | : hash_core(s + 1, l - 1,
|
| |
|
| |
|
| | (((std::numeric_limits<size_t>::max)() >> 6) &
|
| | h * 33) ^
|
| | static_cast<unsigned char>(to_lower(*s)));
|
| | }
|
| | };
|
| |
|
| | template <typename T>
|
| | using unordered_set = std::unordered_set<T, detail::case_ignore::hash,
|
| | detail::case_ignore::equal_to>;
|
| |
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| | struct scope_exit {
|
| | explicit scope_exit(std::function<void(void)> &&f)
|
| | : exit_function(std::move(f)), execute_on_destruction{true} {}
|
| |
|
| | scope_exit(scope_exit &&rhs) noexcept
|
| | : exit_function(std::move(rhs.exit_function)),
|
| | execute_on_destruction{rhs.execute_on_destruction} {
|
| | rhs.release();
|
| | }
|
| |
|
| | ~scope_exit() {
|
| | if (execute_on_destruction) { this->exit_function(); }
|
| | }
|
| |
|
| | void release() { this->execute_on_destruction = false; }
|
| |
|
| | private:
|
| | scope_exit(const scope_exit &) = delete;
|
| | void operator=(const scope_exit &) = delete;
|
| | scope_exit &operator=(scope_exit &&) = delete;
|
| |
|
| | std::function<void(void)> exit_function;
|
| | bool execute_on_destruction;
|
| | };
|
| |
|
| |
|
| |
|
| | template <typename T> struct from_chars_result {
|
| | const char *ptr;
|
| | std::errc ec;
|
| | };
|
| |
|
| | template <typename T>
|
| | inline from_chars_result<T> from_chars(const char *first, const char *last,
|
| | T &value, int base = 10) {
|
| | value = 0;
|
| | const char *p = first;
|
| | bool negative = false;
|
| |
|
| | if (p != last && *p == '-') {
|
| | negative = true;
|
| | ++p;
|
| | }
|
| | if (p == last) { return {first, std::errc::invalid_argument}; }
|
| |
|
| | T result = 0;
|
| | for (; p != last; ++p) {
|
| | char c = *p;
|
| | int digit = -1;
|
| | if ('0' <= c && c <= '9') {
|
| | digit = c - '0';
|
| | } else if ('a' <= c && c <= 'z') {
|
| | digit = c - 'a' + 10;
|
| | } else if ('A' <= c && c <= 'Z') {
|
| | digit = c - 'A' + 10;
|
| | } else {
|
| | break;
|
| | }
|
| |
|
| | if (digit < 0 || digit >= base) { break; }
|
| | if (result > ((std::numeric_limits<T>::max)() - digit) / base) {
|
| | return {p, std::errc::result_out_of_range};
|
| | }
|
| | result = result * base + digit;
|
| | }
|
| |
|
| | if (p == first || (negative && p == first + 1)) {
|
| | return {first, std::errc::invalid_argument};
|
| | }
|
| |
|
| | value = negative ? -result : result;
|
| | return {p, std::errc{}};
|
| | }
|
| |
|
| |
|
| | inline from_chars_result<double> from_chars(const char *first, const char *last,
|
| | double &value) {
|
| | std::string s(first, last);
|
| | char *endptr = nullptr;
|
| | errno = 0;
|
| | value = std::strtod(s.c_str(), &endptr);
|
| | if (endptr == s.c_str()) { return {first, std::errc::invalid_argument}; }
|
| | if (errno == ERANGE) {
|
| | return {first + (endptr - s.c_str()), std::errc::result_out_of_range};
|
| | }
|
| | return {first + (endptr - s.c_str()), std::errc{}};
|
| | }
|
| |
|
| | }
|
| |
|
| | enum SSLVerifierResponse {
|
| |
|
| | NoDecisionMade,
|
| |
|
| | CertificateAccepted,
|
| |
|
| | CertificateRejected
|
| | };
|
| |
|
| | enum StatusCode {
|
| |
|
| | Continue_100 = 100,
|
| | SwitchingProtocol_101 = 101,
|
| | Processing_102 = 102,
|
| | EarlyHints_103 = 103,
|
| |
|
| |
|
| | OK_200 = 200,
|
| | Created_201 = 201,
|
| | Accepted_202 = 202,
|
| | NonAuthoritativeInformation_203 = 203,
|
| | NoContent_204 = 204,
|
| | ResetContent_205 = 205,
|
| | PartialContent_206 = 206,
|
| | MultiStatus_207 = 207,
|
| | AlreadyReported_208 = 208,
|
| | IMUsed_226 = 226,
|
| |
|
| |
|
| | MultipleChoices_300 = 300,
|
| | MovedPermanently_301 = 301,
|
| | Found_302 = 302,
|
| | SeeOther_303 = 303,
|
| | NotModified_304 = 304,
|
| | UseProxy_305 = 305,
|
| | unused_306 = 306,
|
| | TemporaryRedirect_307 = 307,
|
| | PermanentRedirect_308 = 308,
|
| |
|
| |
|
| | BadRequest_400 = 400,
|
| | Unauthorized_401 = 401,
|
| | PaymentRequired_402 = 402,
|
| | Forbidden_403 = 403,
|
| | NotFound_404 = 404,
|
| | MethodNotAllowed_405 = 405,
|
| | NotAcceptable_406 = 406,
|
| | ProxyAuthenticationRequired_407 = 407,
|
| | RequestTimeout_408 = 408,
|
| | Conflict_409 = 409,
|
| | Gone_410 = 410,
|
| | LengthRequired_411 = 411,
|
| | PreconditionFailed_412 = 412,
|
| | PayloadTooLarge_413 = 413,
|
| | UriTooLong_414 = 414,
|
| | UnsupportedMediaType_415 = 415,
|
| | RangeNotSatisfiable_416 = 416,
|
| | ExpectationFailed_417 = 417,
|
| | ImATeapot_418 = 418,
|
| | MisdirectedRequest_421 = 421,
|
| | UnprocessableContent_422 = 422,
|
| | Locked_423 = 423,
|
| | FailedDependency_424 = 424,
|
| | TooEarly_425 = 425,
|
| | UpgradeRequired_426 = 426,
|
| | PreconditionRequired_428 = 428,
|
| | TooManyRequests_429 = 429,
|
| | RequestHeaderFieldsTooLarge_431 = 431,
|
| | UnavailableForLegalReasons_451 = 451,
|
| |
|
| |
|
| | InternalServerError_500 = 500,
|
| | NotImplemented_501 = 501,
|
| | BadGateway_502 = 502,
|
| | ServiceUnavailable_503 = 503,
|
| | GatewayTimeout_504 = 504,
|
| | HttpVersionNotSupported_505 = 505,
|
| | VariantAlsoNegotiates_506 = 506,
|
| | InsufficientStorage_507 = 507,
|
| | LoopDetected_508 = 508,
|
| | NotExtended_510 = 510,
|
| | NetworkAuthenticationRequired_511 = 511,
|
| | };
|
| |
|
| | using Headers =
|
| | std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
|
| | detail::case_ignore::equal_to>;
|
| |
|
| | using Params = std::multimap<std::string, std::string>;
|
| | using Match = std::smatch;
|
| |
|
| | using DownloadProgress = std::function<bool(size_t current, size_t total)>;
|
| | using UploadProgress = std::function<bool(size_t current, size_t total)>;
|
| |
|
| |
|
| | #if __cplusplus >= 201703L
|
| |
|
| | using any = std::any;
|
| | using bad_any_cast = std::bad_any_cast;
|
| |
|
| | template <typename T> T any_cast(const any &a) { return std::any_cast<T>(a); }
|
| | template <typename T> T any_cast(any &a) { return std::any_cast<T>(a); }
|
| | template <typename T> T any_cast(any &&a) {
|
| | return std::any_cast<T>(std::move(a));
|
| | }
|
| | template <typename T> const T *any_cast(const any *a) noexcept {
|
| | return std::any_cast<T>(a);
|
| | }
|
| | template <typename T> T *any_cast(any *a) noexcept {
|
| | return std::any_cast<T>(a);
|
| | }
|
| |
|
| | #else
|
| |
|
| | class bad_any_cast : public std::bad_cast {
|
| | public:
|
| | const char *what() const noexcept override { return "bad any_cast"; }
|
| | };
|
| |
|
| | namespace detail {
|
| |
|
| | using any_type_id = const void *;
|
| |
|
| |
|
| |
|
| |
|
| | template <typename T> any_type_id any_typeid() noexcept {
|
| | static const char id = 0;
|
| | return &id;
|
| | }
|
| |
|
| | struct any_storage {
|
| | virtual ~any_storage() = default;
|
| | virtual std::unique_ptr<any_storage> clone() const = 0;
|
| | virtual any_type_id type_id() const noexcept = 0;
|
| | };
|
| |
|
| | template <typename T> struct any_value final : any_storage {
|
| | T value;
|
| | template <typename U> explicit any_value(U &&v) : value(std::forward<U>(v)) {}
|
| | std::unique_ptr<any_storage> clone() const override {
|
| | return std::unique_ptr<any_storage>(new any_value<T>(value));
|
| | }
|
| | any_type_id type_id() const noexcept override { return any_typeid<T>(); }
|
| | };
|
| |
|
| | }
|
| |
|
| | class any {
|
| | std::unique_ptr<detail::any_storage> storage_;
|
| |
|
| | public:
|
| | any() noexcept = default;
|
| | any(const any &o) : storage_(o.storage_ ? o.storage_->clone() : nullptr) {}
|
| | any(any &&) noexcept = default;
|
| | any &operator=(const any &o) {
|
| | storage_ = o.storage_ ? o.storage_->clone() : nullptr;
|
| | return *this;
|
| | }
|
| | any &operator=(any &&) noexcept = default;
|
| |
|
| | template <
|
| | typename T, typename D = typename std::decay<T>::type,
|
| | typename std::enable_if<!std::is_same<D, any>::value, int>::type = 0>
|
| | any(T &&v) : storage_(new detail::any_value<D>(std::forward<T>(v))) {}
|
| |
|
| | template <
|
| | typename T, typename D = typename std::decay<T>::type,
|
| | typename std::enable_if<!std::is_same<D, any>::value, int>::type = 0>
|
| | any &operator=(T &&v) {
|
| | storage_.reset(new detail::any_value<D>(std::forward<T>(v)));
|
| | return *this;
|
| | }
|
| |
|
| | bool has_value() const noexcept { return storage_ != nullptr; }
|
| | void reset() noexcept { storage_.reset(); }
|
| |
|
| | template <typename T> friend T *any_cast(any *a) noexcept;
|
| | template <typename T> friend const T *any_cast(const any *a) noexcept;
|
| | };
|
| |
|
| | template <typename T> T *any_cast(any *a) noexcept {
|
| | if (!a || !a->storage_) { return nullptr; }
|
| | if (a->storage_->type_id() != detail::any_typeid<T>()) { return nullptr; }
|
| | return &static_cast<detail::any_value<T> *>(a->storage_.get())->value;
|
| | }
|
| |
|
| | template <typename T> const T *any_cast(const any *a) noexcept {
|
| | if (!a || !a->storage_) { return nullptr; }
|
| | if (a->storage_->type_id() != detail::any_typeid<T>()) { return nullptr; }
|
| | return &static_cast<const detail::any_value<T> *>(a->storage_.get())->value;
|
| | }
|
| |
|
| | template <typename T> T any_cast(const any &a) {
|
| | using U =
|
| | typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
| | const U *p = any_cast<U>(&a);
|
| | #ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
| | if (!p) { throw bad_any_cast{}; }
|
| | #else
|
| | if (!p) { std::abort(); }
|
| | #endif
|
| | return static_cast<T>(*p);
|
| | }
|
| |
|
| | template <typename T> T any_cast(any &a) {
|
| | using U =
|
| | typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
| | U *p = any_cast<U>(&a);
|
| | #ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
| | if (!p) { throw bad_any_cast{}; }
|
| | #else
|
| | if (!p) { std::abort(); }
|
| | #endif
|
| | return static_cast<T>(*p);
|
| | }
|
| |
|
| | template <typename T> T any_cast(any &&a) {
|
| | using U =
|
| | typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
| | U *p = any_cast<U>(&a);
|
| | #ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
| | if (!p) { throw bad_any_cast{}; }
|
| | #else
|
| | if (!p) { std::abort(); }
|
| | #endif
|
| | return static_cast<T>(std::move(*p));
|
| | }
|
| |
|
| | #endif
|
| |
|
| | struct Response;
|
| | using ResponseHandler = std::function<bool(const Response &response)>;
|
| |
|
| | struct FormData {
|
| | std::string name;
|
| | std::string content;
|
| | std::string filename;
|
| | std::string content_type;
|
| | Headers headers;
|
| | };
|
| |
|
| | struct FormField {
|
| | std::string name;
|
| | std::string content;
|
| | Headers headers;
|
| | };
|
| | using FormFields = std::multimap<std::string, FormField>;
|
| |
|
| | using FormFiles = std::multimap<std::string, FormData>;
|
| |
|
| | struct MultipartFormData {
|
| | FormFields fields;
|
| | FormFiles files;
|
| |
|
| |
|
| | std::string get_field(const std::string &key, size_t id = 0) const;
|
| | std::vector<std::string> get_fields(const std::string &key) const;
|
| | bool has_field(const std::string &key) const;
|
| | size_t get_field_count(const std::string &key) const;
|
| |
|
| |
|
| | FormData get_file(const std::string &key, size_t id = 0) const;
|
| | std::vector<FormData> get_files(const std::string &key) const;
|
| | bool has_file(const std::string &key) const;
|
| | size_t get_file_count(const std::string &key) const;
|
| | };
|
| |
|
| | struct UploadFormData {
|
| | std::string name;
|
| | std::string content;
|
| | std::string filename;
|
| | std::string content_type;
|
| | };
|
| | using UploadFormDataItems = std::vector<UploadFormData>;
|
| |
|
| | class DataSink {
|
| | public:
|
| | DataSink() : os(&sb_), sb_(*this) {}
|
| |
|
| | DataSink(const DataSink &) = delete;
|
| | DataSink &operator=(const DataSink &) = delete;
|
| | DataSink(DataSink &&) = delete;
|
| | DataSink &operator=(DataSink &&) = delete;
|
| |
|
| | std::function<bool(const char *data, size_t data_len)> write;
|
| | std::function<bool()> is_writable;
|
| | std::function<void()> done;
|
| | std::function<void(const Headers &trailer)> done_with_trailer;
|
| | std::ostream os;
|
| |
|
| | private:
|
| | class data_sink_streambuf final : public std::streambuf {
|
| | public:
|
| | explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
|
| |
|
| | protected:
|
| | std::streamsize xsputn(const char *s, std::streamsize n) override {
|
| | sink_.write(s, static_cast<size_t>(n));
|
| | return n;
|
| | }
|
| |
|
| | private:
|
| | DataSink &sink_;
|
| | };
|
| |
|
| | data_sink_streambuf sb_;
|
| | };
|
| |
|
| | using ContentProvider =
|
| | std::function<bool(size_t offset, size_t length, DataSink &sink)>;
|
| |
|
| | using ContentProviderWithoutLength =
|
| | std::function<bool(size_t offset, DataSink &sink)>;
|
| |
|
| | using ContentProviderResourceReleaser = std::function<void(bool success)>;
|
| |
|
| | struct FormDataProvider {
|
| | std::string name;
|
| | ContentProviderWithoutLength provider;
|
| | std::string filename;
|
| | std::string content_type;
|
| | };
|
| | using FormDataProviderItems = std::vector<FormDataProvider>;
|
| |
|
| | inline FormDataProvider
|
| | make_file_provider(const std::string &name, const std::string &filepath,
|
| | const std::string &filename = std::string(),
|
| | const std::string &content_type = std::string()) {
|
| | FormDataProvider fdp;
|
| | fdp.name = name;
|
| | fdp.filename = filename.empty() ? filepath : filename;
|
| | fdp.content_type = content_type;
|
| | fdp.provider = [filepath](size_t offset, DataSink &sink) -> bool {
|
| | std::ifstream f(filepath, std::ios::binary);
|
| | if (!f) { return false; }
|
| | if (offset > 0) {
|
| | f.seekg(static_cast<std::streamoff>(offset));
|
| | if (!f.good()) {
|
| | sink.done();
|
| | return true;
|
| | }
|
| | }
|
| | char buf[8192];
|
| | f.read(buf, sizeof(buf));
|
| | auto n = static_cast<size_t>(f.gcount());
|
| | if (n > 0) { return sink.write(buf, n); }
|
| | sink.done();
|
| | return true;
|
| | };
|
| | return fdp;
|
| | }
|
| |
|
| | inline std::pair<size_t, ContentProvider>
|
| | make_file_body(const std::string &filepath) {
|
| | std::ifstream f(filepath, std::ios::binary | std::ios::ate);
|
| | if (!f) { return {0, ContentProvider{}}; }
|
| | auto size = static_cast<size_t>(f.tellg());
|
| |
|
| | ContentProvider provider = [filepath](size_t offset, size_t length,
|
| | DataSink &sink) -> bool {
|
| | std::ifstream f(filepath, std::ios::binary);
|
| | if (!f) { return false; }
|
| | f.seekg(static_cast<std::streamoff>(offset));
|
| | if (!f.good()) { return false; }
|
| | char buf[8192];
|
| | while (length > 0) {
|
| | auto to_read = (std::min)(sizeof(buf), length);
|
| | f.read(buf, static_cast<std::streamsize>(to_read));
|
| | auto n = static_cast<size_t>(f.gcount());
|
| | if (n == 0) { break; }
|
| | if (!sink.write(buf, n)) { return false; }
|
| | length -= n;
|
| | }
|
| | return true;
|
| | };
|
| | return {size, std::move(provider)};
|
| | }
|
| |
|
| | using ContentReceiverWithProgress = std::function<bool(
|
| | const char *data, size_t data_length, size_t offset, size_t total_length)>;
|
| |
|
| | using ContentReceiver =
|
| | std::function<bool(const char *data, size_t data_length)>;
|
| |
|
| | using FormDataHeader = std::function<bool(const FormData &file)>;
|
| |
|
| | class ContentReader {
|
| | public:
|
| | using Reader = std::function<bool(ContentReceiver receiver)>;
|
| | using FormDataReader =
|
| | std::function<bool(FormDataHeader header, ContentReceiver receiver)>;
|
| |
|
| | ContentReader(Reader reader, FormDataReader multipart_reader)
|
| | : reader_(std::move(reader)),
|
| | formdata_reader_(std::move(multipart_reader)) {}
|
| |
|
| | bool operator()(FormDataHeader header, ContentReceiver receiver) const {
|
| | return formdata_reader_(std::move(header), std::move(receiver));
|
| | }
|
| |
|
| | bool operator()(ContentReceiver receiver) const {
|
| | return reader_(std::move(receiver));
|
| | }
|
| |
|
| | Reader reader_;
|
| | FormDataReader formdata_reader_;
|
| | };
|
| |
|
| | using Range = std::pair<ssize_t, ssize_t>;
|
| | using Ranges = std::vector<Range>;
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| |
|
| | namespace tls {
|
| |
|
| |
|
| | using ctx_t = void *;
|
| | using session_t = void *;
|
| | using const_session_t = const void *;
|
| | using cert_t = void *;
|
| | using ca_store_t = void *;
|
| |
|
| |
|
| | enum class Version {
|
| | TLS1_2 = 0x0303,
|
| | TLS1_3 = 0x0304,
|
| | };
|
| |
|
| |
|
| | enum class SanType { DNS, IP, EMAIL, URI, OTHER };
|
| |
|
| |
|
| | struct SanEntry {
|
| | SanType type;
|
| | std::string value;
|
| | };
|
| |
|
| |
|
| | struct VerifyContext {
|
| | session_t session;
|
| | cert_t cert;
|
| | int depth;
|
| | bool preverify_ok;
|
| | long error_code;
|
| | const char *error_string;
|
| |
|
| |
|
| | std::string subject_cn() const;
|
| | std::string issuer_name() const;
|
| | bool check_hostname(const char *hostname) const;
|
| | std::vector<SanEntry> sans() const;
|
| | bool validity(time_t ¬_before, time_t ¬_after) const;
|
| | std::string serial() const;
|
| | };
|
| |
|
| | using VerifyCallback = std::function<bool(const VerifyContext &ctx)>;
|
| |
|
| |
|
| | enum class ErrorCode : int {
|
| | Success = 0,
|
| | WantRead,
|
| | WantWrite,
|
| | PeerClosed,
|
| | Fatal,
|
| | SyscallError,
|
| | CertVerifyFailed,
|
| | HostnameMismatch,
|
| | };
|
| |
|
| |
|
| | struct TlsError {
|
| | ErrorCode code = ErrorCode::Fatal;
|
| | uint64_t backend_code = 0;
|
| | int sys_errno = 0;
|
| |
|
| |
|
| | static std::string verify_error_to_string(long error_code);
|
| | };
|
| |
|
| |
|
| | class PeerCert {
|
| | public:
|
| | PeerCert();
|
| | PeerCert(PeerCert &&other) noexcept;
|
| | PeerCert &operator=(PeerCert &&other) noexcept;
|
| | ~PeerCert();
|
| |
|
| | PeerCert(const PeerCert &) = delete;
|
| | PeerCert &operator=(const PeerCert &) = delete;
|
| |
|
| | explicit operator bool() const;
|
| | std::string subject_cn() const;
|
| | std::string issuer_name() const;
|
| | bool check_hostname(const char *hostname) const;
|
| | std::vector<SanEntry> sans() const;
|
| | bool validity(time_t ¬_before, time_t ¬_after) const;
|
| | std::string serial() const;
|
| |
|
| | private:
|
| | explicit PeerCert(cert_t cert);
|
| | cert_t cert_ = nullptr;
|
| | friend PeerCert get_peer_cert_from_session(const_session_t session);
|
| | };
|
| |
|
| |
|
| | using ContextSetupCallback = std::function<bool(ctx_t ctx)>;
|
| |
|
| | }
|
| | #endif
|
| |
|
| | struct Request {
|
| | std::string method;
|
| | std::string path;
|
| | std::string matched_route;
|
| | Params params;
|
| | Headers headers;
|
| | Headers trailers;
|
| | std::string body;
|
| |
|
| | std::string remote_addr;
|
| | int remote_port = -1;
|
| | std::string local_addr;
|
| | int local_port = -1;
|
| |
|
| |
|
| | std::string version;
|
| | std::string target;
|
| | MultipartFormData form;
|
| | Ranges ranges;
|
| | Match matches;
|
| | std::unordered_map<std::string, std::string> path_params;
|
| | std::function<bool()> is_connection_closed = []() { return true; };
|
| |
|
| |
|
| | std::vector<std::string> accept_content_types;
|
| | ResponseHandler response_handler;
|
| | ContentReceiverWithProgress content_receiver;
|
| | DownloadProgress download_progress;
|
| | UploadProgress upload_progress;
|
| |
|
| | bool has_header(const std::string &key) const;
|
| | std::string get_header_value(const std::string &key, const char *def = "",
|
| | size_t id = 0) const;
|
| | size_t get_header_value_u64(const std::string &key, size_t def = 0,
|
| | size_t id = 0) const;
|
| | size_t get_header_value_count(const std::string &key) const;
|
| | void set_header(const std::string &key, const std::string &val);
|
| |
|
| | bool has_trailer(const std::string &key) const;
|
| | std::string get_trailer_value(const std::string &key, size_t id = 0) const;
|
| | size_t get_trailer_value_count(const std::string &key) const;
|
| |
|
| | bool has_param(const std::string &key) const;
|
| | std::string get_param_value(const std::string &key, size_t id = 0) const;
|
| | size_t get_param_value_count(const std::string &key) const;
|
| |
|
| | bool is_multipart_form_data() const;
|
| |
|
| |
|
| | size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
|
| | size_t content_length_ = 0;
|
| | ContentProvider content_provider_;
|
| | bool is_chunked_content_provider_ = false;
|
| | size_t authorization_count_ = 0;
|
| | std::chrono::time_point<std::chrono::steady_clock> start_time_ =
|
| | (std::chrono::steady_clock::time_point::min)();
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | tls::const_session_t ssl = nullptr;
|
| | tls::PeerCert peer_cert() const;
|
| | std::string sni() const;
|
| | #endif
|
| | };
|
| |
|
| | struct Response {
|
| | std::string version;
|
| | int status = -1;
|
| | std::string reason;
|
| | Headers headers;
|
| | Headers trailers;
|
| | std::string body;
|
| | std::string location;
|
| |
|
| |
|
| |
|
| | std::map<std::string, any> user_data;
|
| |
|
| | bool has_header(const std::string &key) const;
|
| | std::string get_header_value(const std::string &key, const char *def = "",
|
| | size_t id = 0) const;
|
| | size_t get_header_value_u64(const std::string &key, size_t def = 0,
|
| | size_t id = 0) const;
|
| | size_t get_header_value_count(const std::string &key) const;
|
| | void set_header(const std::string &key, const std::string &val);
|
| |
|
| | bool has_trailer(const std::string &key) const;
|
| | std::string get_trailer_value(const std::string &key, size_t id = 0) const;
|
| | size_t get_trailer_value_count(const std::string &key) const;
|
| |
|
| | void set_redirect(const std::string &url, int status = StatusCode::Found_302);
|
| | void set_content(const char *s, size_t n, const std::string &content_type);
|
| | void set_content(const std::string &s, const std::string &content_type);
|
| | void set_content(std::string &&s, const std::string &content_type);
|
| |
|
| | void set_content_provider(
|
| | size_t length, const std::string &content_type, ContentProvider provider,
|
| | ContentProviderResourceReleaser resource_releaser = nullptr);
|
| |
|
| | void set_content_provider(
|
| | const std::string &content_type, ContentProviderWithoutLength provider,
|
| | ContentProviderResourceReleaser resource_releaser = nullptr);
|
| |
|
| | void set_chunked_content_provider(
|
| | const std::string &content_type, ContentProviderWithoutLength provider,
|
| | ContentProviderResourceReleaser resource_releaser = nullptr);
|
| |
|
| | void set_file_content(const std::string &path,
|
| | const std::string &content_type);
|
| | void set_file_content(const std::string &path);
|
| |
|
| | Response() = default;
|
| | Response(const Response &) = default;
|
| | Response &operator=(const Response &) = default;
|
| | Response(Response &&) = default;
|
| | Response &operator=(Response &&) = default;
|
| | ~Response() {
|
| | if (content_provider_resource_releaser_) {
|
| | content_provider_resource_releaser_(content_provider_success_);
|
| | }
|
| | }
|
| |
|
| |
|
| | size_t content_length_ = 0;
|
| | ContentProvider content_provider_;
|
| | ContentProviderResourceReleaser content_provider_resource_releaser_;
|
| | bool is_chunked_content_provider_ = false;
|
| | bool content_provider_success_ = false;
|
| | std::string file_content_path_;
|
| | std::string file_content_content_type_;
|
| | };
|
| |
|
| | enum class Error {
|
| | Success = 0,
|
| | Unknown,
|
| | Connection,
|
| | BindIPAddress,
|
| | Read,
|
| | Write,
|
| | ExceedRedirectCount,
|
| | Canceled,
|
| | SSLConnection,
|
| | SSLLoadingCerts,
|
| | SSLServerVerification,
|
| | SSLServerHostnameVerification,
|
| | UnsupportedMultipartBoundaryChars,
|
| | Compression,
|
| | ConnectionTimeout,
|
| | ProxyConnection,
|
| | ConnectionClosed,
|
| | Timeout,
|
| | ResourceExhaustion,
|
| | TooManyFormDataFiles,
|
| | ExceedMaxPayloadSize,
|
| | ExceedUriMaxLength,
|
| | ExceedMaxSocketDescriptorCount,
|
| | InvalidRequestLine,
|
| | InvalidHTTPMethod,
|
| | InvalidHTTPVersion,
|
| | InvalidHeaders,
|
| | MultipartParsing,
|
| | OpenFile,
|
| | Listen,
|
| | GetSockName,
|
| | UnsupportedAddressFamily,
|
| | HTTPParsing,
|
| | InvalidRangeHeader,
|
| |
|
| |
|
| | SSLPeerCouldBeClosed_,
|
| | };
|
| |
|
| | std::string to_string(Error error);
|
| |
|
| | std::ostream &operator<<(std::ostream &os, const Error &obj);
|
| |
|
| | class Stream {
|
| | public:
|
| | virtual ~Stream() = default;
|
| |
|
| | virtual bool is_readable() const = 0;
|
| | virtual bool wait_readable() const = 0;
|
| | virtual bool wait_writable() const = 0;
|
| | virtual bool is_peer_alive() const { return wait_writable(); }
|
| |
|
| | virtual ssize_t read(char *ptr, size_t size) = 0;
|
| | virtual ssize_t write(const char *ptr, size_t size) = 0;
|
| | virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
|
| | virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
|
| | virtual socket_t socket() const = 0;
|
| |
|
| | virtual time_t duration() const = 0;
|
| |
|
| | virtual void set_read_timeout(time_t sec, time_t usec = 0) {
|
| | (void)sec;
|
| | (void)usec;
|
| | }
|
| |
|
| | ssize_t write(const char *ptr);
|
| | ssize_t write(const std::string &s);
|
| |
|
| | Error get_error() const { return error_; }
|
| |
|
| | protected:
|
| | Error error_ = Error::Success;
|
| | };
|
| |
|
| | class TaskQueue {
|
| | public:
|
| | TaskQueue() = default;
|
| | virtual ~TaskQueue() = default;
|
| |
|
| | virtual bool enqueue(std::function<void()> fn) = 0;
|
| | virtual void shutdown() = 0;
|
| |
|
| | virtual void on_idle() {}
|
| | };
|
| |
|
| | class ThreadPool final : public TaskQueue {
|
| | public:
|
| | explicit ThreadPool(size_t n, size_t max_n = 0, size_t mqr = 0);
|
| | ThreadPool(const ThreadPool &) = delete;
|
| | ~ThreadPool() override = default;
|
| |
|
| | bool enqueue(std::function<void()> fn) override;
|
| | void shutdown() override;
|
| |
|
| | private:
|
| | void worker(bool is_dynamic);
|
| | void move_to_finished(std::thread::id id);
|
| | void cleanup_finished_threads();
|
| |
|
| | size_t base_thread_count_;
|
| | size_t max_thread_count_;
|
| | size_t max_queued_requests_;
|
| | size_t idle_thread_count_;
|
| |
|
| | bool shutdown_;
|
| |
|
| | std::list<std::function<void()>> jobs_;
|
| | std::vector<std::thread> threads_;
|
| | std::list<std::thread> dynamic_threads_;
|
| | std::vector<std::thread>
|
| | finished_threads_;
|
| |
|
| | std::condition_variable cond_;
|
| | std::mutex mutex_;
|
| | };
|
| |
|
| | using Logger = std::function<void(const Request &, const Response &)>;
|
| |
|
| |
|
| | enum class Error;
|
| | using ErrorLogger = std::function<void(const Error &, const Request *)>;
|
| |
|
| | using SocketOptions = std::function<void(socket_t sock)>;
|
| |
|
| | void default_socket_options(socket_t sock);
|
| |
|
| | const char *status_message(int status);
|
| |
|
| | std::string to_string(Error error);
|
| |
|
| | std::ostream &operator<<(std::ostream &os, const Error &obj);
|
| |
|
| | std::string get_bearer_token_auth(const Request &req);
|
| |
|
| | namespace detail {
|
| |
|
| | class MatcherBase {
|
| | public:
|
| | MatcherBase(std::string pattern) : pattern_(std::move(pattern)) {}
|
| | virtual ~MatcherBase() = default;
|
| |
|
| | const std::string &pattern() const { return pattern_; }
|
| |
|
| |
|
| | virtual bool match(Request &request) const = 0;
|
| |
|
| | private:
|
| | std::string pattern_;
|
| | };
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | class PathParamsMatcher final : public MatcherBase {
|
| | public:
|
| | PathParamsMatcher(const std::string &pattern);
|
| |
|
| | bool match(Request &request) const override;
|
| |
|
| | private:
|
| |
|
| |
|
| |
|
| | static constexpr char separator = '/';
|
| |
|
| |
|
| |
|
| |
|
| | std::vector<std::string> static_fragments_;
|
| |
|
| |
|
| | std::vector<std::string> param_names_;
|
| | };
|
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | class RegexMatcher final : public MatcherBase {
|
| | public:
|
| | RegexMatcher(const std::string &pattern)
|
| | : MatcherBase(pattern), regex_(pattern) {}
|
| |
|
| | bool match(Request &request) const override;
|
| |
|
| | private:
|
| | std::regex regex_;
|
| | };
|
| |
|
| | int close_socket(socket_t sock);
|
| |
|
| | ssize_t write_headers(Stream &strm, const Headers &headers);
|
| |
|
| | bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,
|
| | time_t usec);
|
| |
|
| | }
|
| |
|
| | class Server {
|
| | public:
|
| | using Handler = std::function<void(const Request &, Response &)>;
|
| |
|
| | using ExceptionHandler =
|
| | std::function<void(const Request &, Response &, std::exception_ptr ep)>;
|
| |
|
| | enum class HandlerResponse {
|
| | Handled,
|
| | Unhandled,
|
| | };
|
| | using HandlerWithResponse =
|
| | std::function<HandlerResponse(const Request &, Response &)>;
|
| |
|
| | using HandlerWithContentReader = std::function<void(
|
| | const Request &, Response &, const ContentReader &content_reader)>;
|
| |
|
| | using Expect100ContinueHandler =
|
| | std::function<int(const Request &, Response &)>;
|
| |
|
| | using WebSocketHandler =
|
| | std::function<void(const Request &, ws::WebSocket &)>;
|
| | using SubProtocolSelector =
|
| | std::function<std::string(const std::vector<std::string> &protocols)>;
|
| |
|
| | Server();
|
| |
|
| | virtual ~Server();
|
| |
|
| | virtual bool is_valid() const;
|
| |
|
| | Server &Get(const std::string &pattern, Handler handler);
|
| | Server &Post(const std::string &pattern, Handler handler);
|
| | Server &Post(const std::string &pattern, HandlerWithContentReader handler);
|
| | Server &Put(const std::string &pattern, Handler handler);
|
| | Server &Put(const std::string &pattern, HandlerWithContentReader handler);
|
| | Server &Patch(const std::string &pattern, Handler handler);
|
| | Server &Patch(const std::string &pattern, HandlerWithContentReader handler);
|
| | Server &Delete(const std::string &pattern, Handler handler);
|
| | Server &Delete(const std::string &pattern, HandlerWithContentReader handler);
|
| | Server &Options(const std::string &pattern, Handler handler);
|
| |
|
| | Server &WebSocket(const std::string &pattern, WebSocketHandler handler);
|
| | Server &WebSocket(const std::string &pattern, WebSocketHandler handler,
|
| | SubProtocolSelector sub_protocol_selector);
|
| |
|
| | bool set_base_dir(const std::string &dir,
|
| | const std::string &mount_point = std::string());
|
| | bool set_mount_point(const std::string &mount_point, const std::string &dir,
|
| | Headers headers = Headers());
|
| | bool remove_mount_point(const std::string &mount_point);
|
| | Server &set_file_extension_and_mimetype_mapping(const std::string &ext,
|
| | const std::string &mime);
|
| | Server &set_default_file_mimetype(const std::string &mime);
|
| | Server &set_file_request_handler(Handler handler);
|
| |
|
| | template <class ErrorHandlerFunc>
|
| | Server &set_error_handler(ErrorHandlerFunc &&handler) {
|
| | return set_error_handler_core(
|
| | std::forward<ErrorHandlerFunc>(handler),
|
| | std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
|
| | }
|
| |
|
| | Server &set_exception_handler(ExceptionHandler handler);
|
| |
|
| | Server &set_pre_routing_handler(HandlerWithResponse handler);
|
| | Server &set_post_routing_handler(Handler handler);
|
| |
|
| | Server &set_pre_request_handler(HandlerWithResponse handler);
|
| |
|
| | Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);
|
| | Server &set_logger(Logger logger);
|
| | Server &set_pre_compression_logger(Logger logger);
|
| | Server &set_error_logger(ErrorLogger error_logger);
|
| |
|
| | Server &set_address_family(int family);
|
| | Server &set_tcp_nodelay(bool on);
|
| | Server &set_ipv6_v6only(bool on);
|
| | Server &set_socket_options(SocketOptions socket_options);
|
| |
|
| | Server &set_default_headers(Headers headers);
|
| | Server &
|
| | set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
|
| |
|
| | Server &set_trusted_proxies(const std::vector<std::string> &proxies);
|
| |
|
| | Server &set_keep_alive_max_count(size_t count);
|
| | Server &set_keep_alive_timeout(time_t sec);
|
| |
|
| | Server &set_read_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | Server &set_write_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | Server &set_idle_interval(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | Server &set_payload_max_length(size_t length);
|
| |
|
| | bool bind_to_port(const std::string &host, int port, int socket_flags = 0);
|
| | int bind_to_any_port(const std::string &host, int socket_flags = 0);
|
| | bool listen_after_bind();
|
| |
|
| | bool listen(const std::string &host, int port, int socket_flags = 0);
|
| |
|
| | bool is_running() const;
|
| | void wait_until_ready() const;
|
| | void stop();
|
| | void decommission();
|
| |
|
| | std::function<TaskQueue *(void)> new_task_queue;
|
| |
|
| | protected:
|
| | bool process_request(Stream &strm, const std::string &remote_addr,
|
| | int remote_port, const std::string &local_addr,
|
| | int local_port, bool close_connection,
|
| | bool &connection_closed,
|
| | const std::function<void(Request &)> &setup_request,
|
| | bool *websocket_upgraded = nullptr);
|
| |
|
| | std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
|
| |
|
| | std::vector<std::string> trusted_proxies_;
|
| |
|
| | size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
|
| | time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
|
| | time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
|
| | time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
|
| | time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
|
| | time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
|
| | time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
|
| | time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
|
| | size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
|
| |
|
| | private:
|
| | using Handlers =
|
| | std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;
|
| | using HandlersForContentReader =
|
| | std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,
|
| | HandlerWithContentReader>>;
|
| |
|
| | static std::unique_ptr<detail::MatcherBase>
|
| | make_matcher(const std::string &pattern);
|
| |
|
| | Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
|
| | Server &set_error_handler_core(Handler handler, std::false_type);
|
| |
|
| | socket_t create_server_socket(const std::string &host, int port,
|
| | int socket_flags,
|
| | SocketOptions socket_options) const;
|
| | int bind_internal(const std::string &host, int port, int socket_flags);
|
| | bool listen_internal();
|
| |
|
| | bool routing(Request &req, Response &res, Stream &strm);
|
| | bool handle_file_request(Request &req, Response &res);
|
| | bool check_if_not_modified(const Request &req, Response &res,
|
| | const std::string &etag, time_t mtime) const;
|
| | bool check_if_range(Request &req, const std::string &etag,
|
| | time_t mtime) const;
|
| | bool dispatch_request(Request &req, Response &res,
|
| | const Handlers &handlers) const;
|
| | bool dispatch_request_for_content_reader(
|
| | Request &req, Response &res, ContentReader content_reader,
|
| | const HandlersForContentReader &handlers) const;
|
| |
|
| | bool parse_request_line(const char *s, Request &req) const;
|
| | void apply_ranges(const Request &req, Response &res,
|
| | std::string &content_type, std::string &boundary) const;
|
| | bool write_response(Stream &strm, bool close_connection, Request &req,
|
| | Response &res);
|
| | bool write_response_with_content(Stream &strm, bool close_connection,
|
| | const Request &req, Response &res);
|
| | bool write_response_core(Stream &strm, bool close_connection,
|
| | const Request &req, Response &res,
|
| | bool need_apply_ranges);
|
| | bool write_content_with_provider(Stream &strm, const Request &req,
|
| | Response &res, const std::string &boundary,
|
| | const std::string &content_type);
|
| | bool read_content(Stream &strm, Request &req, Response &res);
|
| | bool read_content_with_content_receiver(Stream &strm, Request &req,
|
| | Response &res,
|
| | ContentReceiver receiver,
|
| | FormDataHeader multipart_header,
|
| | ContentReceiver multipart_receiver);
|
| | bool read_content_core(Stream &strm, Request &req, Response &res,
|
| | ContentReceiver receiver,
|
| | FormDataHeader multipart_header,
|
| | ContentReceiver multipart_receiver) const;
|
| |
|
| | virtual bool process_and_close_socket(socket_t sock);
|
| |
|
| | void output_log(const Request &req, const Response &res) const;
|
| | void output_pre_compression_log(const Request &req,
|
| | const Response &res) const;
|
| | void output_error_log(const Error &err, const Request *req) const;
|
| |
|
| | std::atomic<bool> is_running_{false};
|
| | std::atomic<bool> is_decommissioned{false};
|
| |
|
| | struct MountPointEntry {
|
| | std::string mount_point;
|
| | std::string base_dir;
|
| | Headers headers;
|
| | };
|
| | std::vector<MountPointEntry> base_dirs_;
|
| | std::map<std::string, std::string> file_extension_and_mimetype_map_;
|
| | std::string default_file_mimetype_ = "application/octet-stream";
|
| | Handler file_request_handler_;
|
| |
|
| | Handlers get_handlers_;
|
| | Handlers post_handlers_;
|
| | HandlersForContentReader post_handlers_for_content_reader_;
|
| | Handlers put_handlers_;
|
| | HandlersForContentReader put_handlers_for_content_reader_;
|
| | Handlers patch_handlers_;
|
| | HandlersForContentReader patch_handlers_for_content_reader_;
|
| | Handlers delete_handlers_;
|
| | HandlersForContentReader delete_handlers_for_content_reader_;
|
| | Handlers options_handlers_;
|
| |
|
| | struct WebSocketHandlerEntry {
|
| | std::unique_ptr<detail::MatcherBase> matcher;
|
| | WebSocketHandler handler;
|
| | SubProtocolSelector sub_protocol_selector;
|
| | };
|
| | using WebSocketHandlers = std::vector<WebSocketHandlerEntry>;
|
| | WebSocketHandlers websocket_handlers_;
|
| |
|
| | HandlerWithResponse error_handler_;
|
| | ExceptionHandler exception_handler_;
|
| | HandlerWithResponse pre_routing_handler_;
|
| | Handler post_routing_handler_;
|
| | HandlerWithResponse pre_request_handler_;
|
| | Expect100ContinueHandler expect_100_continue_handler_;
|
| |
|
| | mutable std::mutex logger_mutex_;
|
| | Logger logger_;
|
| | Logger pre_compression_logger_;
|
| | ErrorLogger error_logger_;
|
| |
|
| | int address_family_ = AF_UNSPEC;
|
| | bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
| | bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
|
| | SocketOptions socket_options_ = default_socket_options;
|
| |
|
| | Headers default_headers_;
|
| | std::function<ssize_t(Stream &, Headers &)> header_writer_ =
|
| | detail::write_headers;
|
| | };
|
| |
|
| | class Result {
|
| | public:
|
| | Result() = default;
|
| | Result(std::unique_ptr<Response> &&res, Error err,
|
| | Headers &&request_headers = Headers{})
|
| | : res_(std::move(res)), err_(err),
|
| | request_headers_(std::move(request_headers)) {}
|
| |
|
| | operator bool() const { return res_ != nullptr; }
|
| | bool operator==(std::nullptr_t) const { return res_ == nullptr; }
|
| | bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
|
| | const Response &value() const { return *res_; }
|
| | Response &value() { return *res_; }
|
| | const Response &operator*() const { return *res_; }
|
| | Response &operator*() { return *res_; }
|
| | const Response *operator->() const { return res_.get(); }
|
| | Response *operator->() { return res_.get(); }
|
| |
|
| |
|
| | Error error() const { return err_; }
|
| |
|
| |
|
| | bool has_request_header(const std::string &key) const;
|
| | std::string get_request_header_value(const std::string &key,
|
| | const char *def = "",
|
| | size_t id = 0) const;
|
| | size_t get_request_header_value_u64(const std::string &key, size_t def = 0,
|
| | size_t id = 0) const;
|
| | size_t get_request_header_value_count(const std::string &key) const;
|
| |
|
| | private:
|
| | std::unique_ptr<Response> res_;
|
| | Error err_ = Error::Unknown;
|
| | Headers request_headers_;
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | public:
|
| | Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
|
| | int ssl_error)
|
| | : res_(std::move(res)), err_(err),
|
| | request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}
|
| | Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,
|
| | int ssl_error, unsigned long ssl_backend_error)
|
| | : res_(std::move(res)), err_(err),
|
| | request_headers_(std::move(request_headers)), ssl_error_(ssl_error),
|
| | ssl_backend_error_(ssl_backend_error) {}
|
| |
|
| | int ssl_error() const { return ssl_error_; }
|
| | unsigned long ssl_backend_error() const { return ssl_backend_error_; }
|
| |
|
| | private:
|
| | int ssl_error_ = 0;
|
| | unsigned long ssl_backend_error_ = 0;
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
| | public:
|
| | [[deprecated("Use ssl_backend_error() instead")]]
|
| | unsigned long ssl_openssl_error() const {
|
| | return ssl_backend_error_;
|
| | }
|
| | #endif
|
| | };
|
| |
|
| | struct ClientConnection {
|
| | socket_t sock = INVALID_SOCKET;
|
| |
|
| | bool is_open() const { return sock != INVALID_SOCKET; }
|
| |
|
| | ClientConnection() = default;
|
| |
|
| | ~ClientConnection();
|
| |
|
| | ClientConnection(const ClientConnection &) = delete;
|
| | ClientConnection &operator=(const ClientConnection &) = delete;
|
| |
|
| | ClientConnection(ClientConnection &&other) noexcept
|
| | : sock(other.sock)
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | ,
|
| | session(other.session)
|
| | #endif
|
| | {
|
| | other.sock = INVALID_SOCKET;
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | other.session = nullptr;
|
| | #endif
|
| | }
|
| |
|
| | ClientConnection &operator=(ClientConnection &&other) noexcept {
|
| | if (this != &other) {
|
| | sock = other.sock;
|
| | other.sock = INVALID_SOCKET;
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | session = other.session;
|
| | other.session = nullptr;
|
| | #endif
|
| | }
|
| | return *this;
|
| | }
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | tls::session_t session = nullptr;
|
| | #endif
|
| | };
|
| |
|
| | namespace detail {
|
| |
|
| | struct ChunkedDecoder;
|
| |
|
| | struct BodyReader {
|
| | Stream *stream = nullptr;
|
| | bool has_content_length = false;
|
| | size_t content_length = 0;
|
| | size_t payload_max_length = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
|
| | size_t bytes_read = 0;
|
| | bool chunked = false;
|
| | bool eof = false;
|
| | std::unique_ptr<ChunkedDecoder> chunked_decoder;
|
| | Error last_error = Error::Success;
|
| |
|
| | ssize_t read(char *buf, size_t len);
|
| | bool has_error() const { return last_error != Error::Success; }
|
| | };
|
| |
|
| | inline ssize_t read_body_content(Stream *stream, BodyReader &br, char *buf,
|
| | size_t len) {
|
| | (void)stream;
|
| | return br.read(buf, len);
|
| | }
|
| |
|
| | class decompressor;
|
| |
|
| | }
|
| |
|
| | class ClientImpl {
|
| | public:
|
| | explicit ClientImpl(const std::string &host);
|
| |
|
| | explicit ClientImpl(const std::string &host, int port);
|
| |
|
| | explicit ClientImpl(const std::string &host, int port,
|
| | const std::string &client_cert_path,
|
| | const std::string &client_key_path);
|
| |
|
| | virtual ~ClientImpl();
|
| |
|
| | virtual bool is_valid() const;
|
| |
|
| | struct StreamHandle {
|
| | std::unique_ptr<Response> response;
|
| | Error error = Error::Success;
|
| |
|
| | StreamHandle() = default;
|
| | StreamHandle(const StreamHandle &) = delete;
|
| | StreamHandle &operator=(const StreamHandle &) = delete;
|
| | StreamHandle(StreamHandle &&) = default;
|
| | StreamHandle &operator=(StreamHandle &&) = default;
|
| | ~StreamHandle() = default;
|
| |
|
| | bool is_valid() const {
|
| | return response != nullptr && error == Error::Success;
|
| | }
|
| |
|
| | ssize_t read(char *buf, size_t len);
|
| | void parse_trailers_if_needed();
|
| | Error get_read_error() const { return body_reader_.last_error; }
|
| | bool has_read_error() const { return body_reader_.has_error(); }
|
| |
|
| | bool trailers_parsed_ = false;
|
| |
|
| | private:
|
| | friend class ClientImpl;
|
| |
|
| | ssize_t read_with_decompression(char *buf, size_t len);
|
| |
|
| | std::unique_ptr<ClientConnection> connection_;
|
| | std::unique_ptr<Stream> socket_stream_;
|
| | Stream *stream_ = nullptr;
|
| | detail::BodyReader body_reader_;
|
| |
|
| | std::unique_ptr<detail::decompressor> decompressor_;
|
| | std::string decompress_buffer_;
|
| | size_t decompress_offset_ = 0;
|
| | size_t decompressed_bytes_read_ = 0;
|
| | };
|
| |
|
| |
|
| | Result Get(const std::string &path, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Params ¶ms, const Headers &headers, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Head(const std::string &path);
|
| | Result Head(const std::string &path, const Headers &headers);
|
| |
|
| | Result Post(const std::string &path);
|
| | Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Params ¶ms);
|
| | Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers);
|
| | Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const Params ¶ms);
|
| | Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Put(const std::string &path);
|
| | Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Params ¶ms);
|
| | Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers);
|
| | Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const Params ¶ms);
|
| | Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Patch(const std::string &path);
|
| | Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Params ¶ms);
|
| | Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const Params ¶ms);
|
| | Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Delete(const std::string &path, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Params ¶ms, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, const Params ¶ms, DownloadProgress progress = nullptr);
|
| |
|
| | Result Options(const std::string &path);
|
| | Result Options(const std::string &path, const Headers &headers);
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | StreamHandle open_stream(const std::string &method, const std::string &path,
|
| | const Params ¶ms = {},
|
| | const Headers &headers = {},
|
| | const std::string &body = {},
|
| | const std::string &content_type = {});
|
| |
|
| | bool send(Request &req, Response &res, Error &error);
|
| | Result send(const Request &req);
|
| |
|
| | void stop();
|
| |
|
| | std::string host() const;
|
| | int port() const;
|
| |
|
| | size_t is_socket_open() const;
|
| | socket_t socket() const;
|
| |
|
| | void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
| |
|
| | void set_default_headers(Headers headers);
|
| |
|
| | void
|
| | set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
|
| |
|
| | void set_address_family(int family);
|
| | void set_tcp_nodelay(bool on);
|
| | void set_ipv6_v6only(bool on);
|
| | void set_socket_options(SocketOptions socket_options);
|
| |
|
| | void set_connection_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | void
|
| | set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_read_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_write_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_max_timeout(time_t msec);
|
| | template <class Rep, class Period>
|
| | void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_basic_auth(const std::string &username, const std::string &password);
|
| | void set_bearer_token_auth(const std::string &token);
|
| |
|
| | void set_keep_alive(bool on);
|
| | void set_follow_location(bool on);
|
| |
|
| | void set_path_encode(bool on);
|
| |
|
| | void set_compress(bool on);
|
| |
|
| | void set_decompress(bool on);
|
| |
|
| | void set_payload_max_length(size_t length);
|
| |
|
| | void set_interface(const std::string &intf);
|
| |
|
| | void set_proxy(const std::string &host, int port);
|
| | void set_proxy_basic_auth(const std::string &username,
|
| | const std::string &password);
|
| | void set_proxy_bearer_token_auth(const std::string &token);
|
| |
|
| | void set_logger(Logger logger);
|
| | void set_error_logger(ErrorLogger error_logger);
|
| |
|
| | protected:
|
| | struct Socket {
|
| | socket_t sock = INVALID_SOCKET;
|
| |
|
| |
|
| | std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
| |
|
| | bool is_open() const { return sock != INVALID_SOCKET; }
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | tls::session_t ssl = nullptr;
|
| | #endif
|
| | };
|
| |
|
| | virtual bool create_and_connect_socket(Socket &socket, Error &error);
|
| | virtual bool ensure_socket_connection(Socket &socket, Error &error);
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
|
| | void shutdown_socket(Socket &socket) const;
|
| | void close_socket(Socket &socket);
|
| |
|
| | bool process_request(Stream &strm, Request &req, Response &res,
|
| | bool close_connection, Error &error);
|
| |
|
| | bool write_content_with_provider(Stream &strm, const Request &req,
|
| | Error &error) const;
|
| |
|
| | void copy_settings(const ClientImpl &rhs);
|
| |
|
| | void output_log(const Request &req, const Response &res) const;
|
| | void output_error_log(const Error &err, const Request *req) const;
|
| |
|
| |
|
| | const std::string host_;
|
| | const int port_;
|
| |
|
| |
|
| | Socket socket_;
|
| | mutable std::mutex socket_mutex_;
|
| | std::recursive_mutex request_mutex_;
|
| |
|
| |
|
| | size_t socket_requests_in_flight_ = 0;
|
| | std::thread::id socket_requests_are_from_thread_ = std::thread::id();
|
| | bool socket_should_be_closed_when_request_is_done_ = false;
|
| |
|
| |
|
| | std::map<std::string, std::string> addr_map_;
|
| |
|
| |
|
| | Headers default_headers_;
|
| |
|
| |
|
| | std::function<ssize_t(Stream &, Headers &)> header_writer_ =
|
| | detail::write_headers;
|
| |
|
| |
|
| | std::string client_cert_path_;
|
| | std::string client_key_path_;
|
| |
|
| | time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
|
| | time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
|
| | time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
|
| | time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
|
| | time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
|
| | time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
|
| | time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
|
| |
|
| | std::string basic_auth_username_;
|
| | std::string basic_auth_password_;
|
| | std::string bearer_token_auth_token_;
|
| |
|
| | bool keep_alive_ = false;
|
| | bool follow_location_ = false;
|
| |
|
| | bool path_encode_ = true;
|
| |
|
| | int address_family_ = AF_UNSPEC;
|
| | bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
|
| | bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
|
| | SocketOptions socket_options_ = nullptr;
|
| |
|
| | bool compress_ = false;
|
| | bool decompress_ = true;
|
| |
|
| | size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
|
| | bool has_payload_max_length_ = false;
|
| |
|
| | std::string interface_;
|
| |
|
| | std::string proxy_host_;
|
| | int proxy_port_ = -1;
|
| |
|
| | std::string proxy_basic_auth_username_;
|
| | std::string proxy_basic_auth_password_;
|
| | std::string proxy_bearer_token_auth_token_;
|
| |
|
| | mutable std::mutex logger_mutex_;
|
| | Logger logger_;
|
| | ErrorLogger error_logger_;
|
| |
|
| | private:
|
| | bool send_(Request &req, Response &res, Error &error);
|
| | Result send_(Request &&req);
|
| |
|
| | socket_t create_client_socket(Error &error) const;
|
| | bool read_response_line(Stream &strm, const Request &req, Response &res,
|
| | bool skip_100_continue = true) const;
|
| | bool write_request(Stream &strm, Request &req, bool close_connection,
|
| | Error &error, bool skip_body = false);
|
| | bool write_request_body(Stream &strm, Request &req, Error &error);
|
| | void prepare_default_headers(Request &r, bool for_stream,
|
| | const std::string &ct);
|
| | bool redirect(Request &req, Response &res, Error &error);
|
| | bool create_redirect_client(const std::string &scheme,
|
| | const std::string &host, int port, Request &req,
|
| | Response &res, const std::string &path,
|
| | const std::string &location, Error &error);
|
| | template <typename ClientType> void setup_redirect_client(ClientType &client);
|
| | bool handle_request(Stream &strm, Request &req, Response &res,
|
| | bool close_connection, Error &error);
|
| | std::unique_ptr<Response> send_with_content_provider_and_receiver(
|
| | Request &req, const char *body, size_t content_length,
|
| | ContentProvider content_provider,
|
| | ContentProviderWithoutLength content_provider_without_length,
|
| | const std::string &content_type, ContentReceiver content_receiver,
|
| | Error &error);
|
| | Result send_with_content_provider_and_receiver(
|
| | const std::string &method, const std::string &path,
|
| | const Headers &headers, const char *body, size_t content_length,
|
| | ContentProvider content_provider,
|
| | ContentProviderWithoutLength content_provider_without_length,
|
| | const std::string &content_type, ContentReceiver content_receiver,
|
| | UploadProgress progress);
|
| | ContentProviderWithoutLength get_multipart_content_provider(
|
| | const std::string &boundary, const UploadFormDataItems &items,
|
| | const FormDataProviderItems &provider_items) const;
|
| |
|
| | virtual bool
|
| | process_socket(const Socket &socket,
|
| | std::chrono::time_point<std::chrono::steady_clock> start_time,
|
| | std::function<bool(Stream &strm)> callback);
|
| | virtual bool is_ssl() const;
|
| |
|
| | void transfer_socket_ownership_to_handle(StreamHandle &handle);
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | public:
|
| | void set_digest_auth(const std::string &username,
|
| | const std::string &password);
|
| | void set_proxy_digest_auth(const std::string &username,
|
| | const std::string &password);
|
| | void set_ca_cert_path(const std::string &ca_cert_file_path,
|
| | const std::string &ca_cert_dir_path = std::string());
|
| | void enable_server_certificate_verification(bool enabled);
|
| | void enable_server_hostname_verification(bool enabled);
|
| |
|
| | protected:
|
| | std::string digest_auth_username_;
|
| | std::string digest_auth_password_;
|
| | std::string proxy_digest_auth_username_;
|
| | std::string proxy_digest_auth_password_;
|
| | std::string ca_cert_file_path_;
|
| | std::string ca_cert_dir_path_;
|
| | bool server_certificate_verification_ = true;
|
| | bool server_hostname_verification_ = true;
|
| | std::string ca_cert_pem_;
|
| | int last_ssl_error_ = 0;
|
| | unsigned long last_backend_error_ = 0;
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
| | public:
|
| | [[deprecated("Use load_ca_cert_store() instead")]]
|
| | void set_ca_cert_store(X509_STORE *ca_cert_store);
|
| |
|
| | [[deprecated("Use tls::create_ca_store() instead")]]
|
| | X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
|
| |
|
| | [[deprecated("Use set_server_certificate_verifier(VerifyCallback) instead")]]
|
| | virtual void set_server_certificate_verifier(
|
| | std::function<SSLVerifierResponse(SSL *ssl)> verifier);
|
| | #endif
|
| | };
|
| |
|
| | class Client {
|
| | public:
|
| |
|
| | explicit Client(const std::string &scheme_host_port);
|
| |
|
| | explicit Client(const std::string &scheme_host_port,
|
| | const std::string &client_cert_path,
|
| | const std::string &client_key_path);
|
| |
|
| |
|
| | explicit Client(const std::string &host, int port);
|
| |
|
| | explicit Client(const std::string &host, int port,
|
| | const std::string &client_cert_path,
|
| | const std::string &client_key_path);
|
| |
|
| | Client(Client &&) = default;
|
| | Client &operator=(Client &&) = default;
|
| |
|
| | ~Client();
|
| |
|
| | bool is_valid() const;
|
| |
|
| |
|
| | Result Get(const std::string &path, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Params ¶ms, const Headers &headers, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Get(const std::string &path, const Params ¶ms, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Head(const std::string &path);
|
| | Result Head(const std::string &path, const Headers &headers);
|
| |
|
| | Result Post(const std::string &path);
|
| | Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Params ¶ms);
|
| | Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers);
|
| | Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const Params ¶ms);
|
| | Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
|
| | Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Put(const std::string &path);
|
| | Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Params ¶ms);
|
| | Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers);
|
| | Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const Params ¶ms);
|
| | Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
|
| | Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Patch(const std::string &path);
|
| | Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Params ¶ms);
|
| | Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers);
|
| | Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, ContentReceiver content_receiver, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const Params ¶ms);
|
| | Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);
|
| | Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);
|
| |
|
| | Result Delete(const std::string &path, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Params ¶ms, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);
|
| | Result Delete(const std::string &path, const Headers &headers, const Params ¶ms, DownloadProgress progress = nullptr);
|
| |
|
| | Result Options(const std::string &path);
|
| | Result Options(const std::string &path, const Headers &headers);
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | ClientImpl::StreamHandle open_stream(const std::string &method,
|
| | const std::string &path,
|
| | const Params ¶ms = {},
|
| | const Headers &headers = {},
|
| | const std::string &body = {},
|
| | const std::string &content_type = {});
|
| |
|
| | bool send(Request &req, Response &res, Error &error);
|
| | Result send(const Request &req);
|
| |
|
| | void stop();
|
| |
|
| | std::string host() const;
|
| | int port() const;
|
| |
|
| | size_t is_socket_open() const;
|
| | socket_t socket() const;
|
| |
|
| | void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
| |
|
| | void set_default_headers(Headers headers);
|
| |
|
| | void
|
| | set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);
|
| |
|
| | void set_address_family(int family);
|
| | void set_tcp_nodelay(bool on);
|
| | void set_socket_options(SocketOptions socket_options);
|
| |
|
| | void set_connection_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | void
|
| | set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_read_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_write_timeout(time_t sec, time_t usec = 0);
|
| | template <class Rep, class Period>
|
| | void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_max_timeout(time_t msec);
|
| | template <class Rep, class Period>
|
| | void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
|
| |
|
| | void set_basic_auth(const std::string &username, const std::string &password);
|
| | void set_bearer_token_auth(const std::string &token);
|
| |
|
| | void set_keep_alive(bool on);
|
| | void set_follow_location(bool on);
|
| |
|
| | void set_path_encode(bool on);
|
| | void set_url_encode(bool on);
|
| |
|
| | void set_compress(bool on);
|
| |
|
| | void set_decompress(bool on);
|
| |
|
| | void set_payload_max_length(size_t length);
|
| |
|
| | void set_interface(const std::string &intf);
|
| |
|
| | void set_proxy(const std::string &host, int port);
|
| | void set_proxy_basic_auth(const std::string &username,
|
| | const std::string &password);
|
| | void set_proxy_bearer_token_auth(const std::string &token);
|
| | void set_logger(Logger logger);
|
| | void set_error_logger(ErrorLogger error_logger);
|
| |
|
| | private:
|
| | std::unique_ptr<ClientImpl> cli_;
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | public:
|
| | void set_digest_auth(const std::string &username,
|
| | const std::string &password);
|
| | void set_proxy_digest_auth(const std::string &username,
|
| | const std::string &password);
|
| | void enable_server_certificate_verification(bool enabled);
|
| | void enable_server_hostname_verification(bool enabled);
|
| | void set_ca_cert_path(const std::string &ca_cert_file_path,
|
| | const std::string &ca_cert_dir_path = std::string());
|
| |
|
| | void set_ca_cert_store(tls::ca_store_t ca_cert_store);
|
| | void load_ca_cert_store(const char *ca_cert, std::size_t size);
|
| |
|
| | void set_server_certificate_verifier(tls::VerifyCallback verifier);
|
| |
|
| | void set_session_verifier(
|
| | std::function<SSLVerifierResponse(tls::session_t)> verifier);
|
| |
|
| | tls::ctx_t tls_context() const;
|
| |
|
| | #ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
| | void enable_windows_certificate_verification(bool enabled);
|
| | #endif
|
| |
|
| | private:
|
| | bool is_ssl_ = false;
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
| | public:
|
| | [[deprecated("Use tls_context() instead")]]
|
| | SSL_CTX *ssl_context() const;
|
| |
|
| | [[deprecated("Use set_session_verifier(session_t) instead")]]
|
| | void set_server_certificate_verifier(
|
| | std::function<SSLVerifierResponse(SSL *ssl)> verifier);
|
| |
|
| | [[deprecated("Use Result::ssl_backend_error() instead")]]
|
| | long get_verify_result() const;
|
| | #endif
|
| | };
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | class SSLServer : public Server {
|
| | public:
|
| | SSLServer(const char *cert_path, const char *private_key_path,
|
| | const char *client_ca_cert_file_path = nullptr,
|
| | const char *client_ca_cert_dir_path = nullptr,
|
| | const char *private_key_password = nullptr);
|
| |
|
| | struct PemMemory {
|
| | const char *cert_pem;
|
| | size_t cert_pem_len;
|
| | const char *key_pem;
|
| | size_t key_pem_len;
|
| | const char *client_ca_pem;
|
| | size_t client_ca_pem_len;
|
| | const char *private_key_password;
|
| | };
|
| | explicit SSLServer(const PemMemory &pem);
|
| |
|
| |
|
| |
|
| |
|
| | explicit SSLServer(const tls::ContextSetupCallback &setup_callback);
|
| |
|
| | ~SSLServer() override;
|
| |
|
| | bool is_valid() const override;
|
| |
|
| | bool update_certs_pem(const char *cert_pem, const char *key_pem,
|
| | const char *client_ca_pem = nullptr,
|
| | const char *password = nullptr);
|
| |
|
| | tls::ctx_t tls_context() const { return ctx_; }
|
| |
|
| | int ssl_last_error() const { return last_ssl_error_; }
|
| |
|
| | private:
|
| | bool process_and_close_socket(socket_t sock) override;
|
| |
|
| | tls::ctx_t ctx_ = nullptr;
|
| | std::mutex ctx_mutex_;
|
| |
|
| | int last_ssl_error_ = 0;
|
| |
|
| | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
| | public:
|
| | [[deprecated("Use SSLServer(PemMemory) or "
|
| | "SSLServer(ContextSetupCallback) instead")]]
|
| | SSLServer(X509 *cert, EVP_PKEY *private_key,
|
| | X509_STORE *client_ca_cert_store = nullptr);
|
| |
|
| | [[deprecated("Use SSLServer(ContextSetupCallback) instead")]]
|
| | SSLServer(
|
| | const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);
|
| |
|
| | [[deprecated("Use tls_context() instead")]]
|
| | SSL_CTX *ssl_context() const;
|
| |
|
| | [[deprecated("Use update_certs_pem() instead")]]
|
| | void update_certs(X509 *cert, EVP_PKEY *private_key,
|
| | X509_STORE *client_ca_cert_store = nullptr);
|
| | #endif
|
| | };
|
| |
|
| | class SSLClient final : public ClientImpl {
|
| | public:
|
| | explicit SSLClient(const std::string &host);
|
| |
|
| | explicit SSLClient(const std::string &host, int port);
|
| |
|
| | explicit SSLClient(const std::string &host, int port,
|
| | const std::string &client_cert_path,
|
| | const std::string &client_key_path,
|
| | const std::string &private_key_password = std::string());
|
| |
|
| | struct PemMemory {
|
| | const char *cert_pem;
|
| | size_t cert_pem_len;
|
| | const char *key_pem;
|
| | size_t key_pem_len;
|
| | const char *private_key_password;
|
| | };
|
| | explicit SSLClient(const std::string &host, int port, const PemMemory &pem);
|
| |
|
| | ~SSLClient() override;
|
| |
|
| | bool is_valid() const override;
|
| |
|
| | void set_ca_cert_store(tls::ca_store_t ca_cert_store);
|
| | void load_ca_cert_store(const char *ca_cert, std::size_t size);
|
| |
|
| | void set_server_certificate_verifier(tls::VerifyCallback verifier);
|
| |
|
| |
|
| | void set_session_verifier(
|
| | std::function<SSLVerifierResponse(tls::session_t)> verifier);
|
| |
|
| | tls::ctx_t tls_context() const { return ctx_; }
|
| |
|
| | #ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
| | void enable_windows_certificate_verification(bool enabled);
|
| | #endif
|
| |
|
| | private:
|
| | bool create_and_connect_socket(Socket &socket, Error &error) override;
|
| | bool ensure_socket_connection(Socket &socket, Error &error) override;
|
| | void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
|
| | void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
|
| |
|
| | bool
|
| | process_socket(const Socket &socket,
|
| | std::chrono::time_point<std::chrono::steady_clock> start_time,
|
| | std::function<bool(Stream &strm)> callback) override;
|
| | bool is_ssl() const override;
|
| |
|
| | bool connect_with_proxy(
|
| | Socket &sock,
|
| | std::chrono::time_point<std::chrono::steady_clock> start_time,
|
| | Response &res, bool &success, Error &error);
|
| | bool initialize_ssl(Socket &socket, Error &error);
|
| |
|
| | bool load_certs();
|
| |
|
| | tls::ctx_t ctx_ = nullptr;
|
| | std::mutex ctx_mutex_;
|
| | std::once_flag initialize_cert_;
|
| |
|
| | long verify_result_ = 0;
|
| |
|
| | std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
|
| |
|
| | #ifdef CPPHTTPLIB_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE
|
| | bool enable_windows_cert_verification_ = true;
|
| | #endif
|
| |
|
| | friend class ClientImpl;
|
| |
|
| | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
| | public:
|
| | [[deprecated("Use SSLClient(host, port, PemMemory) instead")]]
|
| | explicit SSLClient(const std::string &host, int port, X509 *client_cert,
|
| | EVP_PKEY *client_key,
|
| | const std::string &private_key_password = std::string());
|
| |
|
| | [[deprecated("Use Result::ssl_backend_error() instead")]]
|
| | long get_verify_result() const;
|
| |
|
| | [[deprecated("Use tls_context() instead")]]
|
| | SSL_CTX *ssl_context() const;
|
| |
|
| | [[deprecated("Use set_session_verifier(session_t) instead")]]
|
| | void set_server_certificate_verifier(
|
| | std::function<SSLVerifierResponse(SSL *ssl)> verifier) override;
|
| |
|
| | private:
|
| | bool verify_host(X509 *server_cert) const;
|
| | bool verify_host_with_subject_alt_name(X509 *server_cert) const;
|
| | bool verify_host_with_common_name(X509 *server_cert) const;
|
| | #endif
|
| | };
|
| | #endif
|
| |
|
| | namespace detail {
|
| |
|
| | template <typename T, typename U>
|
| | inline void duration_to_sec_and_usec(const T &duration, U callback) {
|
| | auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
|
| | auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
|
| | duration - std::chrono::seconds(sec))
|
| | .count();
|
| | callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
|
| | }
|
| |
|
| | template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
|
| | return N - 1;
|
| | }
|
| |
|
| | inline bool is_numeric(const std::string &str) {
|
| | return !str.empty() &&
|
| | std::all_of(str.cbegin(), str.cend(),
|
| | [](unsigned char c) { return std::isdigit(c); });
|
| | }
|
| |
|
| | inline size_t get_header_value_u64(const Headers &headers,
|
| | const std::string &key, size_t def,
|
| | size_t id, bool &is_invalid_value) {
|
| | is_invalid_value = false;
|
| | auto rng = headers.equal_range(key);
|
| | auto it = rng.first;
|
| | std::advance(it, static_cast<ssize_t>(id));
|
| | if (it != rng.second) {
|
| | if (is_numeric(it->second)) {
|
| | return std::strtoull(it->second.data(), nullptr, 10);
|
| | } else {
|
| | is_invalid_value = true;
|
| | }
|
| | }
|
| | return def;
|
| | }
|
| |
|
| | inline size_t get_header_value_u64(const Headers &headers,
|
| | const std::string &key, size_t def,
|
| | size_t id) {
|
| | auto dummy = false;
|
| | return get_header_value_u64(headers, key, def, id, dummy);
|
| | }
|
| |
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline Server &
|
| | Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
|
| | detail::duration_to_sec_and_usec(
|
| | duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
|
| | return *this;
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline Server &
|
| | Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
|
| | detail::duration_to_sec_and_usec(
|
| | duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
|
| | return *this;
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline Server &
|
| | Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
|
| | detail::duration_to_sec_and_usec(
|
| | duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
|
| | return *this;
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void ClientImpl::set_connection_timeout(
|
| | const std::chrono::duration<Rep, Period> &duration) {
|
| | detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
|
| | set_connection_timeout(sec, usec);
|
| | });
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void ClientImpl::set_read_timeout(
|
| | const std::chrono::duration<Rep, Period> &duration) {
|
| | detail::duration_to_sec_and_usec(
|
| | duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void ClientImpl::set_write_timeout(
|
| | const std::chrono::duration<Rep, Period> &duration) {
|
| | detail::duration_to_sec_and_usec(
|
| | duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void ClientImpl::set_max_timeout(
|
| | const std::chrono::duration<Rep, Period> &duration) {
|
| | auto msec =
|
| | std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
|
| | set_max_timeout(msec);
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void Client::set_connection_timeout(
|
| | const std::chrono::duration<Rep, Period> &duration) {
|
| | cli_->set_connection_timeout(duration);
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void
|
| | Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
|
| | cli_->set_read_timeout(duration);
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void
|
| | Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
|
| | cli_->set_write_timeout(duration);
|
| | }
|
| |
|
| | inline void Client::set_max_timeout(time_t msec) {
|
| | cli_->set_max_timeout(msec);
|
| | }
|
| |
|
| | template <class Rep, class Period>
|
| | inline void
|
| | Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
|
| | cli_->set_max_timeout(duration);
|
| | }
|
| |
|
| | |
| | |
| | |
| |
|
| |
|
| | std::string hosted_at(const std::string &hostname);
|
| |
|
| | void hosted_at(const std::string &hostname, std::vector<std::string> &addrs);
|
| |
|
| |
|
| | std::string encode_uri_component(const std::string &value);
|
| | std::string encode_uri(const std::string &value);
|
| | std::string decode_uri_component(const std::string &value);
|
| | std::string decode_uri(const std::string &value);
|
| |
|
| |
|
| | std::string encode_path_component(const std::string &component);
|
| | std::string decode_path_component(const std::string &component);
|
| | std::string encode_query_component(const std::string &component,
|
| | bool space_as_plus = true);
|
| | std::string decode_query_component(const std::string &component,
|
| | bool plus_as_space = true);
|
| |
|
| | std::string append_query_params(const std::string &path, const Params ¶ms);
|
| |
|
| | std::pair<std::string, std::string> make_range_header(const Ranges &ranges);
|
| |
|
| | std::pair<std::string, std::string>
|
| | make_basic_authentication_header(const std::string &username,
|
| | const std::string &password,
|
| | bool is_proxy = false);
|
| |
|
| | namespace detail {
|
| |
|
| | #if defined(_WIN32)
|
| | inline std::wstring u8string_to_wstring(const char *s) {
|
| | if (!s) { return std::wstring(); }
|
| |
|
| | auto len = static_cast<int>(strlen(s));
|
| | if (!len) { return std::wstring(); }
|
| |
|
| | auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
|
| | if (!wlen) { return std::wstring(); }
|
| |
|
| | std::wstring ws;
|
| | ws.resize(wlen);
|
| | wlen = ::MultiByteToWideChar(
|
| | CP_UTF8, 0, s, len,
|
| | const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
|
| | if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
|
| | return ws;
|
| | }
|
| | #endif
|
| |
|
| | struct FileStat {
|
| | FileStat(const std::string &path);
|
| | bool is_file() const;
|
| | bool is_dir() const;
|
| | time_t mtime() const;
|
| | size_t size() const;
|
| |
|
| | private:
|
| | #if defined(_WIN32)
|
| | struct _stat st_;
|
| | #else
|
| | struct stat st_;
|
| | #endif
|
| | int ret_ = -1;
|
| | };
|
| |
|
| | std::string make_host_and_port_string(const std::string &host, int port,
|
| | bool is_ssl);
|
| |
|
| | std::string trim_copy(const std::string &s);
|
| |
|
| | void divide(
|
| | const char *data, std::size_t size, char d,
|
| | std::function<void(const char *, std::size_t, const char *, std::size_t)>
|
| | fn);
|
| |
|
| | void divide(
|
| | const std::string &str, char d,
|
| | std::function<void(const char *, std::size_t, const char *, std::size_t)>
|
| | fn);
|
| |
|
| | void split(const char *b, const char *e, char d,
|
| | std::function<void(const char *, const char *)> fn);
|
| |
|
| | void split(const char *b, const char *e, char d, size_t m,
|
| | std::function<void(const char *, const char *)> fn);
|
| |
|
| | bool process_client_socket(
|
| | socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
|
| | time_t write_timeout_sec, time_t write_timeout_usec,
|
| | time_t max_timeout_msec,
|
| | std::chrono::time_point<std::chrono::steady_clock> start_time,
|
| | std::function<bool(Stream &)> callback);
|
| |
|
| | socket_t create_client_socket(const std::string &host, const std::string &ip,
|
| | int port, int address_family, bool tcp_nodelay,
|
| | bool ipv6_v6only, SocketOptions socket_options,
|
| | time_t connection_timeout_sec,
|
| | time_t connection_timeout_usec,
|
| | time_t read_timeout_sec, time_t read_timeout_usec,
|
| | time_t write_timeout_sec,
|
| | time_t write_timeout_usec,
|
| | const std::string &intf, Error &error);
|
| |
|
| | const char *get_header_value(const Headers &headers, const std::string &key,
|
| | const char *def, size_t id);
|
| |
|
| | std::string params_to_query_str(const Params ¶ms);
|
| |
|
| | void parse_query_text(const char *data, std::size_t size, Params ¶ms);
|
| |
|
| | void parse_query_text(const std::string &s, Params ¶ms);
|
| |
|
| | bool parse_multipart_boundary(const std::string &content_type,
|
| | std::string &boundary);
|
| |
|
| | bool parse_range_header(const std::string &s, Ranges &ranges);
|
| |
|
| | bool parse_accept_header(const std::string &s,
|
| | std::vector<std::string> &content_types);
|
| |
|
| | int close_socket(socket_t sock);
|
| |
|
| | ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
|
| |
|
| | ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
|
| |
|
| | enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
|
| |
|
| | EncodingType encoding_type(const Request &req, const Response &res);
|
| |
|
| | class BufferStream final : public Stream {
|
| | public:
|
| | BufferStream() = default;
|
| | ~BufferStream() override = default;
|
| |
|
| | bool is_readable() const override;
|
| | bool wait_readable() const override;
|
| | bool wait_writable() const override;
|
| | ssize_t read(char *ptr, size_t size) override;
|
| | ssize_t write(const char *ptr, size_t size) override;
|
| | void get_remote_ip_and_port(std::string &ip, int &port) const override;
|
| | void get_local_ip_and_port(std::string &ip, int &port) const override;
|
| | socket_t socket() const override;
|
| | time_t duration() const override;
|
| |
|
| | const std::string &get_buffer() const;
|
| |
|
| | private:
|
| | std::string buffer;
|
| | size_t position = 0;
|
| | };
|
| |
|
| | class compressor {
|
| | public:
|
| | virtual ~compressor() = default;
|
| |
|
| | typedef std::function<bool(const char *data, size_t data_len)> Callback;
|
| | virtual bool compress(const char *data, size_t data_length, bool last,
|
| | Callback callback) = 0;
|
| | };
|
| |
|
| | class decompressor {
|
| | public:
|
| | virtual ~decompressor() = default;
|
| |
|
| | virtual bool is_valid() const = 0;
|
| |
|
| | typedef std::function<bool(const char *data, size_t data_len)> Callback;
|
| | virtual bool decompress(const char *data, size_t data_length,
|
| | Callback callback) = 0;
|
| | };
|
| |
|
| | class nocompressor final : public compressor {
|
| | public:
|
| | ~nocompressor() override = default;
|
| |
|
| | bool compress(const char *data, size_t data_length, bool ,
|
| | Callback callback) override;
|
| | };
|
| |
|
| | #ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
| | class gzip_compressor final : public compressor {
|
| | public:
|
| | gzip_compressor();
|
| | ~gzip_compressor() override;
|
| |
|
| | bool compress(const char *data, size_t data_length, bool last,
|
| | Callback callback) override;
|
| |
|
| | private:
|
| | bool is_valid_ = false;
|
| | z_stream strm_;
|
| | };
|
| |
|
| | class gzip_decompressor final : public decompressor {
|
| | public:
|
| | gzip_decompressor();
|
| | ~gzip_decompressor() override;
|
| |
|
| | bool is_valid() const override;
|
| |
|
| | bool decompress(const char *data, size_t data_length,
|
| | Callback callback) override;
|
| |
|
| | private:
|
| | bool is_valid_ = false;
|
| | z_stream strm_;
|
| | };
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_BROTLI_SUPPORT
|
| | class brotli_compressor final : public compressor {
|
| | public:
|
| | brotli_compressor();
|
| | ~brotli_compressor();
|
| |
|
| | bool compress(const char *data, size_t data_length, bool last,
|
| | Callback callback) override;
|
| |
|
| | private:
|
| | BrotliEncoderState *state_ = nullptr;
|
| | };
|
| |
|
| | class brotli_decompressor final : public decompressor {
|
| | public:
|
| | brotli_decompressor();
|
| | ~brotli_decompressor();
|
| |
|
| | bool is_valid() const override;
|
| |
|
| | bool decompress(const char *data, size_t data_length,
|
| | Callback callback) override;
|
| |
|
| | private:
|
| | BrotliDecoderResult decoder_r;
|
| | BrotliDecoderState *decoder_s = nullptr;
|
| | };
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_ZSTD_SUPPORT
|
| | class zstd_compressor : public compressor {
|
| | public:
|
| | zstd_compressor();
|
| | ~zstd_compressor();
|
| |
|
| | bool compress(const char *data, size_t data_length, bool last,
|
| | Callback callback) override;
|
| |
|
| | private:
|
| | ZSTD_CCtx *ctx_ = nullptr;
|
| | };
|
| |
|
| | class zstd_decompressor : public decompressor {
|
| | public:
|
| | zstd_decompressor();
|
| | ~zstd_decompressor();
|
| |
|
| | bool is_valid() const override;
|
| |
|
| | bool decompress(const char *data, size_t data_length,
|
| | Callback callback) override;
|
| |
|
| | private:
|
| | ZSTD_DCtx *ctx_ = nullptr;
|
| | };
|
| | #endif
|
| |
|
| |
|
| |
|
| | class stream_line_reader {
|
| | public:
|
| | stream_line_reader(Stream &strm, char *fixed_buffer,
|
| | size_t fixed_buffer_size);
|
| | const char *ptr() const;
|
| | size_t size() const;
|
| | bool end_with_crlf() const;
|
| | bool getline();
|
| |
|
| | private:
|
| | void append(char c);
|
| |
|
| | Stream &strm_;
|
| | char *fixed_buffer_;
|
| | const size_t fixed_buffer_size_;
|
| | size_t fixed_buffer_used_size_ = 0;
|
| | std::string growable_buffer_;
|
| | };
|
| |
|
| | bool parse_trailers(stream_line_reader &line_reader, Headers &dest,
|
| | const Headers &src_headers);
|
| |
|
| | struct ChunkedDecoder {
|
| | Stream &strm;
|
| | size_t chunk_remaining = 0;
|
| | bool finished = false;
|
| | char line_buf[64];
|
| | size_t last_chunk_total = 0;
|
| | size_t last_chunk_offset = 0;
|
| |
|
| | explicit ChunkedDecoder(Stream &s);
|
| |
|
| | ssize_t read_payload(char *buf, size_t len, size_t &out_chunk_offset,
|
| | size_t &out_chunk_total);
|
| |
|
| | bool parse_trailers_into(Headers &dest, const Headers &src_headers);
|
| | };
|
| |
|
| | class mmap {
|
| | public:
|
| | mmap(const char *path);
|
| | ~mmap();
|
| |
|
| | bool open(const char *path);
|
| | void close();
|
| |
|
| | bool is_open() const;
|
| | size_t size() const;
|
| | const char *data() const;
|
| |
|
| | private:
|
| | #if defined(_WIN32)
|
| | HANDLE hFile_ = NULL;
|
| | HANDLE hMapping_ = NULL;
|
| | #else
|
| | int fd_ = -1;
|
| | #endif
|
| | size_t size_ = 0;
|
| | void *addr_ = nullptr;
|
| | bool is_open_empty_file = false;
|
| | };
|
| |
|
| |
|
| | namespace fields {
|
| |
|
| | bool is_token_char(char c);
|
| | bool is_token(const std::string &s);
|
| | bool is_field_name(const std::string &s);
|
| | bool is_vchar(char c);
|
| | bool is_obs_text(char c);
|
| | bool is_field_vchar(char c);
|
| | bool is_field_content(const std::string &s);
|
| | bool is_field_value(const std::string &s);
|
| |
|
| | }
|
| | }
|
| |
|
| | |
| | |
| |
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| |
|
| | #ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
|
| | namespace tls {
|
| | namespace impl {
|
| |
|
| |
|
| |
|
| |
|
| | struct MbedTlsContext {
|
| | mbedtls_ssl_config conf;
|
| | mbedtls_entropy_context entropy;
|
| | mbedtls_ctr_drbg_context ctr_drbg;
|
| | mbedtls_x509_crt ca_chain;
|
| | mbedtls_x509_crt own_cert;
|
| | mbedtls_pk_context own_key;
|
| | bool is_server = false;
|
| | bool verify_client = false;
|
| | bool has_verify_callback = false;
|
| |
|
| | MbedTlsContext();
|
| | ~MbedTlsContext();
|
| |
|
| | MbedTlsContext(const MbedTlsContext &) = delete;
|
| | MbedTlsContext &operator=(const MbedTlsContext &) = delete;
|
| | };
|
| |
|
| | }
|
| | }
|
| | #endif
|
| |
|
| | #ifdef CPPHTTPLIB_WOLFSSL_SUPPORT
|
| | namespace tls {
|
| | namespace impl {
|
| |
|
| |
|
| |
|
| |
|
| | struct WolfSSLContext {
|
| | WOLFSSL_CTX *ctx = nullptr;
|
| | bool is_server = false;
|
| | bool verify_client = false;
|
| | bool has_verify_callback = false;
|
| | std::string ca_pem_data_;
|
| |
|
| | WolfSSLContext();
|
| | ~WolfSSLContext();
|
| |
|
| | WolfSSLContext(const WolfSSLContext &) = delete;
|
| | WolfSSLContext &operator=(const WolfSSLContext &) = delete;
|
| | };
|
| |
|
| |
|
| | struct WolfSSLCAStore {
|
| | std::string pem_data;
|
| | };
|
| |
|
| | }
|
| | }
|
| | #endif
|
| |
|
| | #endif
|
| |
|
| | namespace stream {
|
| |
|
| | class Result {
|
| | public:
|
| | Result();
|
| | explicit Result(ClientImpl::StreamHandle &&handle, size_t chunk_size = 8192);
|
| | Result(Result &&other) noexcept;
|
| | Result &operator=(Result &&other) noexcept;
|
| | Result(const Result &) = delete;
|
| | Result &operator=(const Result &) = delete;
|
| |
|
| |
|
| | bool is_valid() const;
|
| | explicit operator bool() const;
|
| | int status() const;
|
| | const Headers &headers() const;
|
| | std::string get_header_value(const std::string &key,
|
| | const char *def = "") const;
|
| | bool has_header(const std::string &key) const;
|
| | Error error() const;
|
| | Error read_error() const;
|
| | bool has_read_error() const;
|
| |
|
| |
|
| | bool next();
|
| | const char *data() const;
|
| | size_t size() const;
|
| | std::string read_all();
|
| |
|
| | private:
|
| | ClientImpl::StreamHandle handle_;
|
| | std::string buffer_;
|
| | size_t current_size_ = 0;
|
| | size_t chunk_size_;
|
| | bool finished_ = false;
|
| | };
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Get(ClientType &cli, const std::string &path,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("GET", path), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Get(ClientType &cli, const std::string &path,
|
| | const Headers &headers, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("GET", path, {}, headers), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Get(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("GET", path, params), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Get(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("GET", path, params, headers), chunk_size};
|
| | }
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Post(ClientType &cli, const std::string &path,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("POST", path, {}, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Post(ClientType &cli, const std::string &path,
|
| | const Headers &headers, const std::string &body,
|
| | const std::string &content_type, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("POST", path, {}, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Post(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const std::string &body,
|
| | const std::string &content_type, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("POST", path, params, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Post(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{
|
| | cli.open_stream("POST", path, params, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Put(ClientType &cli, const std::string &path,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("PUT", path, {}, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Put(ClientType &cli, const std::string &path,
|
| | const Headers &headers, const std::string &body,
|
| | const std::string &content_type, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("PUT", path, {}, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Put(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const std::string &body,
|
| | const std::string &content_type, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("PUT", path, params, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Put(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{
|
| | cli.open_stream("PUT", path, params, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Patch(ClientType &cli, const std::string &path,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("PATCH", path, {}, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Patch(ClientType &cli, const std::string &path,
|
| | const Headers &headers, const std::string &body,
|
| | const std::string &content_type, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("PATCH", path, {}, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Patch(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const std::string &body,
|
| | const std::string &content_type, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("PATCH", path, params, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Patch(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{
|
| | cli.open_stream("PATCH", path, params, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("DELETE", path), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const Headers &headers, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("DELETE", path, {}, headers), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("DELETE", path, {}, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const Headers &headers, const std::string &body,
|
| | const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{
|
| | cli.open_stream("DELETE", path, {}, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("DELETE", path, params), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("DELETE", path, params, headers), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const std::string &body,
|
| | const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("DELETE", path, params, {}, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Delete(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | const std::string &body, const std::string &content_type,
|
| | size_t chunk_size = 8192) {
|
| | return Result{
|
| | cli.open_stream("DELETE", path, params, headers, body, content_type),
|
| | chunk_size};
|
| | }
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Head(ClientType &cli, const std::string &path,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("HEAD", path), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Head(ClientType &cli, const std::string &path,
|
| | const Headers &headers, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("HEAD", path, {}, headers), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Head(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("HEAD", path, params), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Head(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("HEAD", path, params, headers), chunk_size};
|
| | }
|
| |
|
| |
|
| | template <typename ClientType>
|
| | inline Result Options(ClientType &cli, const std::string &path,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("OPTIONS", path), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Options(ClientType &cli, const std::string &path,
|
| | const Headers &headers, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("OPTIONS", path, {}, headers), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Options(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("OPTIONS", path, params), chunk_size};
|
| | }
|
| |
|
| | template <typename ClientType>
|
| | inline Result Options(ClientType &cli, const std::string &path,
|
| | const Params ¶ms, const Headers &headers,
|
| | size_t chunk_size = 8192) {
|
| | return Result{cli.open_stream("OPTIONS", path, params, headers), chunk_size};
|
| | }
|
| |
|
| | }
|
| |
|
| | namespace sse {
|
| |
|
| | struct SSEMessage {
|
| | std::string event;
|
| | std::string data;
|
| | std::string id;
|
| |
|
| | SSEMessage();
|
| | void clear();
|
| | };
|
| |
|
| | class SSEClient {
|
| | public:
|
| | using MessageHandler = std::function<void(const SSEMessage &)>;
|
| | using ErrorHandler = std::function<void(Error)>;
|
| | using OpenHandler = std::function<void()>;
|
| |
|
| | SSEClient(Client &client, const std::string &path);
|
| | SSEClient(Client &client, const std::string &path, const Headers &headers);
|
| | ~SSEClient();
|
| |
|
| | SSEClient(const SSEClient &) = delete;
|
| | SSEClient &operator=(const SSEClient &) = delete;
|
| |
|
| |
|
| | SSEClient &on_message(MessageHandler handler);
|
| | SSEClient &on_event(const std::string &type, MessageHandler handler);
|
| | SSEClient &on_open(OpenHandler handler);
|
| | SSEClient &on_error(ErrorHandler handler);
|
| | SSEClient &set_reconnect_interval(int ms);
|
| | SSEClient &set_max_reconnect_attempts(int n);
|
| |
|
| |
|
| | bool is_connected() const;
|
| | const std::string &last_event_id() const;
|
| |
|
| |
|
| | void start();
|
| |
|
| |
|
| | void start_async();
|
| |
|
| |
|
| | void stop();
|
| |
|
| | private:
|
| | bool parse_sse_line(const std::string &line, SSEMessage &msg, int &retry_ms);
|
| | void run_event_loop();
|
| | void dispatch_event(const SSEMessage &msg);
|
| | bool should_reconnect(int count) const;
|
| | void wait_for_reconnect();
|
| |
|
| |
|
| | Client &client_;
|
| | std::string path_;
|
| | Headers headers_;
|
| |
|
| |
|
| | MessageHandler on_message_;
|
| | std::map<std::string, MessageHandler> event_handlers_;
|
| | OpenHandler on_open_;
|
| | ErrorHandler on_error_;
|
| |
|
| |
|
| | int reconnect_interval_ms_ = 3000;
|
| | int max_reconnect_attempts_ = 0;
|
| |
|
| |
|
| | std::atomic<bool> running_{false};
|
| | std::atomic<bool> connected_{false};
|
| | std::string last_event_id_;
|
| |
|
| |
|
| | std::thread async_thread_;
|
| | };
|
| |
|
| | }
|
| |
|
| | namespace ws {
|
| |
|
| | enum class Opcode : uint8_t {
|
| | Continuation = 0x0,
|
| | Text = 0x1,
|
| | Binary = 0x2,
|
| | Close = 0x8,
|
| | Ping = 0x9,
|
| | Pong = 0xA,
|
| | };
|
| |
|
| | enum class CloseStatus : uint16_t {
|
| | Normal = 1000,
|
| | GoingAway = 1001,
|
| | ProtocolError = 1002,
|
| | UnsupportedData = 1003,
|
| | NoStatus = 1005,
|
| | Abnormal = 1006,
|
| | InvalidPayload = 1007,
|
| | PolicyViolation = 1008,
|
| | MessageTooBig = 1009,
|
| | MandatoryExtension = 1010,
|
| | InternalError = 1011,
|
| | };
|
| |
|
| | enum ReadResult : int { Fail = 0, Text = 1, Binary = 2 };
|
| |
|
| | class WebSocket {
|
| | public:
|
| | WebSocket(const WebSocket &) = delete;
|
| | WebSocket &operator=(const WebSocket &) = delete;
|
| | ~WebSocket();
|
| |
|
| | ReadResult read(std::string &msg);
|
| | bool send(const std::string &data);
|
| | bool send(const char *data, size_t len);
|
| | void close(CloseStatus status = CloseStatus::Normal,
|
| | const std::string &reason = "");
|
| | const Request &request() const;
|
| | bool is_open() const;
|
| |
|
| | private:
|
| | friend class httplib::Server;
|
| | friend class WebSocketClient;
|
| |
|
| | WebSocket(Stream &strm, const Request &req, bool is_server)
|
| | : strm_(strm), req_(req), is_server_(is_server) {
|
| | start_heartbeat();
|
| | }
|
| |
|
| | WebSocket(std::unique_ptr<Stream> &&owned_strm, const Request &req,
|
| | bool is_server)
|
| | : strm_(*owned_strm), owned_strm_(std::move(owned_strm)), req_(req),
|
| | is_server_(is_server) {
|
| | start_heartbeat();
|
| | }
|
| |
|
| | void start_heartbeat();
|
| | bool send_frame(Opcode op, const char *data, size_t len, bool fin = true);
|
| |
|
| | Stream &strm_;
|
| | std::unique_ptr<Stream> owned_strm_;
|
| | Request req_;
|
| | bool is_server_;
|
| | std::atomic<bool> closed_{false};
|
| | std::mutex write_mutex_;
|
| | std::thread ping_thread_;
|
| | std::mutex ping_mutex_;
|
| | std::condition_variable ping_cv_;
|
| | };
|
| |
|
| | class WebSocketClient {
|
| | public:
|
| | explicit WebSocketClient(const std::string &scheme_host_port_path,
|
| | const Headers &headers = {});
|
| |
|
| | ~WebSocketClient();
|
| | WebSocketClient(const WebSocketClient &) = delete;
|
| | WebSocketClient &operator=(const WebSocketClient &) = delete;
|
| |
|
| | bool is_valid() const;
|
| |
|
| | bool connect();
|
| | ReadResult read(std::string &msg);
|
| | bool send(const std::string &data);
|
| | bool send(const char *data, size_t len);
|
| | void close(CloseStatus status = CloseStatus::Normal,
|
| | const std::string &reason = "");
|
| | bool is_open() const;
|
| | const std::string &subprotocol() const;
|
| | void set_read_timeout(time_t sec, time_t usec = 0);
|
| | void set_write_timeout(time_t sec, time_t usec = 0);
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | void set_ca_cert_path(const std::string &path);
|
| | void set_ca_cert_store(tls::ca_store_t store);
|
| | void enable_server_certificate_verification(bool enabled);
|
| | #endif
|
| |
|
| | private:
|
| | void shutdown_and_close();
|
| | bool create_stream(std::unique_ptr<Stream> &strm);
|
| |
|
| | std::string host_;
|
| | int port_;
|
| | std::string path_;
|
| | Headers headers_;
|
| | std::string subprotocol_;
|
| | bool is_valid_ = false;
|
| | socket_t sock_ = INVALID_SOCKET;
|
| | std::unique_ptr<WebSocket> ws_;
|
| | time_t read_timeout_sec_ = CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND;
|
| | time_t read_timeout_usec_ = 0;
|
| | time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
|
| | time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
|
| |
|
| | #ifdef CPPHTTPLIB_SSL_ENABLED
|
| | bool is_ssl_ = false;
|
| | tls::ctx_t tls_ctx_ = nullptr;
|
| | tls::session_t tls_session_ = nullptr;
|
| | std::string ca_cert_file_path_;
|
| | tls::ca_store_t ca_cert_store_ = nullptr;
|
| | bool server_certificate_verification_ = true;
|
| | #endif
|
| | };
|
| |
|
| | namespace impl {
|
| |
|
| | bool is_valid_utf8(const std::string &s);
|
| |
|
| | bool read_websocket_frame(Stream &strm, Opcode &opcode, std::string &payload,
|
| | bool &fin, bool expect_masked, size_t max_len);
|
| |
|
| | }
|
| |
|
| | }
|
| |
|
| |
|
| | }
|
| |
|
| | #endif
|
| |
|