#pragma once
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
#endif
#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
#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_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
#endif
#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
#endif
#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
#endif
#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
#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 10000
#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_REDIRECT_MAX_COUNT
#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
#endif
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
#endif
#ifndef CPPHTTPLIB_TCP_NODELAY
#define CPPHTTPLIB_TCP_NODELAY false
#endif
#ifndef CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
#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_RECV_FLAGS
#define CPPHTTPLIB_RECV_FLAGS 0
#endif
#ifndef CPPHTTPLIB_SEND_FLAGS
#define CPPHTTPLIB_SEND_FLAGS 0
#endif
#ifdef _WIN32
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif //_CRT_SECURE_NO_WARNINGS
#ifndef _CRT_NONSTDC_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#endif //_CRT_NONSTDC_NO_DEPRECATE
#if defined(_MSC_VER)
#ifdef _WIN64
using
ssize_t =
__int64
;
#else
using
ssize_t =
int
;
#endif
#if _MSC_VER < 1900
#define snprintf _snprintf_s
#endif
#endif // _MSC_VER
#ifndef S_ISREG
#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
#endif // S_ISREG
#ifndef S_ISDIR
#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
#endif // S_ISDIR
#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX
#include <io.h>
#include <winsock2.h>
#include <wincrypt.h>
#include <ws2tcpip.h>
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "crypt32.lib")
#pragma comment(lib, "cryptui.lib")
#endif
#ifndef strcasecmp
#define strcasecmp _stricmp
#endif // strcasecmp
using
socket_t = SOCKET;
#ifdef CPPHTTPLIB_USE_POLL
#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
#endif
#else // not _WIN32
#include <arpa/inet.h>
#include <cstring>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef __linux__
#include <resolv.h>
#endif
#include <netinet/tcp.h>
#ifdef CPPHTTPLIB_USE_POLL
#include <poll.h>
#endif
#include <csignal>
#include <pthread.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
using
socket_t =
int
;
#define INVALID_SOCKET (-1)
#endif //_WIN32
#include <algorithm>
#include <array>
#include <atomic>
#include <cassert>
#include <cctype>
#include <climits>
#include <condition_variable>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <random>
#include <regex>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <thread>
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#include <openssl/err.h>
#include <openssl/md5.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 OPENSSL_VERSION_NUMBER < 0x1010100fL
#error Sorry, OpenSSL versions prior to 1.1.1 are not supported
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <openssl/crypto.h>
inline
const
unsigned
char
* ASN1_STRING_get0_data(
const
ASN1_STRING* asn1) {
return
M_ASN1_STRING_data(asn1);
}
#endif
#endif
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
#include <zlib.h>
#endif
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
#include <brotli/decode.h>
#include <brotli/encode.h>
#endif
namespace
httplib {
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]);
}
struct
ci {
bool
operator()(
const
std::string& s1,
const
std::string& s2)
const
{
return
std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),
s2.end(),
[](unsigned
char
c1, unsigned
char
c2) {
return
::
tolower
(c1) < ::
tolower
(c2);
});
}
};
}
using
Headers = std::multimap<std::string, std::string, detail::ci>;
using
Params = std::multimap<std::string, std::string>;
using
Match = std::smatch;
using
Progress = std::function<
bool
(uint64_t current, uint64_t total)>;
struct
Response;
using
ResponseHandler = std::function<
bool
(
const
Response& response)>;
struct
MultipartFormData {
std::string name;
std::string content;
std::string filename;
std::string content_type;
};
using
MultipartFormDataItems = std::vector<MultipartFormData>;
using
MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
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<
void
(
const
char
* data,
size_t
data_len)> write;
std::function<
void
()> done;
std::function<
bool
()> is_writable;
std::ostream os;
private
:
class
data_sink_streambuf :
public
std::streambuf {
public
:
explicit
data_sink_streambuf(DataSink& sink) : sink_(sink) {}
protected
:
std::streamsize xsputn(
const
char
* s, std::streamsize n) {
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
ContentReceiverWithProgress =
std::function<
bool
(
const
char
* data,
size_t
data_length, uint64_t offset,
uint64_t total_length)>;
using
ContentReceiver =
std::function<
bool
(
const
char
* data,
size_t
data_length)>;
using
MultipartContentHeader =
std::function<
bool
(
const
MultipartFormData& file)>;
class
ContentReader {
public
:
using
Reader = std::function<
bool
(ContentReceiver receiver)>;
using
MultipartReader = std::function<
bool
(MultipartContentHeader header,
ContentReceiver receiver)>;
ContentReader(Reader reader, MultipartReader multipart_reader)
: reader_(std::move(reader)),
multipart_reader_(std::move(multipart_reader)) {}
bool
operator()(MultipartContentHeader header,
ContentReceiver receiver)
const
{
return
multipart_reader_(std::move(header), std::move(receiver));
}
bool
operator()(ContentReceiver receiver)
const
{
return
reader_(std::move(receiver));
}
Reader reader_;
MultipartReader multipart_reader_;
};
using
Range = std::pair<ssize_t, ssize_t>;
using
Ranges = std::vector<Range>;
struct
Request {
std::string method;
std::string path;
Headers headers;
std::string body;
std::string remote_addr;
int
remote_port = -1;
std::string version;
std::string target;
Params params;
MultipartFormDataMap files;
Ranges ranges;
Match matches;
ResponseHandler response_handler;
ContentReceiverWithProgress content_receiver;
Progress progress;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
const
SSL* ssl;
#endif
bool
has_header(
const
char
* key)
const
;
std::string get_header_value(
const
char
* key,
size_t
id = 0)
const
;
template
<
typename
T>
T get_header_value(
const
char
* key,
size_t
id = 0)
const
;
size_t
get_header_value_count(
const
char
* key)
const
;
void
set_header(
const
char
* key,
const
char
* val);
void
set_header(
const
char
* key,
const
std::string& val);
bool
has_param(
const
char
* key)
const
;
std::string get_param_value(
const
char
* key,
size_t
id = 0)
const
;
size_t
get_param_value_count(
const
char
* key)
const
;
bool
is_multipart_form_data()
const
;
bool
has_file(
const
char
* key)
const
;
MultipartFormData get_file_value(
const
char
* key)
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;
};
struct
Response {
std::string version;
int
status = -1;
std::string reason;
Headers headers;
std::string body;
std::string location;
bool
has_header(
const
char
* key)
const
;
std::string get_header_value(
const
char
* key,
size_t
id = 0)
const
;
template
<
typename
T>
T get_header_value(
const
char
* key,
size_t
id = 0)
const
;
size_t
get_header_value_count(
const
char
* key)
const
;
void
set_header(
const
char
* key,
const
char
* val);
void
set_header(
const
char
* key,
const
std::string& val);
void
set_redirect(
const
char
* url,
int
status = 302);
void
set_redirect(
const
std::string& url,
int
status = 302);
void
set_content(
const
char
* s,
size_t
n,
const
char
* content_type);
void
set_content(
const
std::string& s,
const
char
* content_type);
void
set_content_provider(
size_t
length,
const
char
* content_type, ContentProvider provider,
const
std::function<
void
()>& resource_releaser =
nullptr
);
void
set_content_provider(
const
char
* content_type, ContentProviderWithoutLength provider,
const
std::function<
void
()>& resource_releaser =
nullptr
);
void
set_chunked_content_provider(
const
char
* content_type, ContentProviderWithoutLength provider,
const
std::function<
void
()>& resource_releaser =
nullptr
);
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_();
}
}
size_t
content_length_ = 0;
ContentProvider content_provider_;
std::function<
void
()> content_provider_resource_releaser_;
bool
is_chunked_content_provider_ =
false
;
};
class
Stream {
public
:
virtual
~Stream() =
default
;
virtual
bool
is_readable()
const
= 0;
virtual
bool
is_writable()
const
= 0;
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
socket_t socket()
const
= 0;
template
<
typename
... Args>
ssize_t write_format(
const
char
* fmt,
const
Args &... args);
ssize_t write(
const
char
* ptr);
ssize_t write(
const
std::string& s);
};
class
TaskQueue {
public
:
TaskQueue() =
default
;
virtual
~TaskQueue() =
default
;
virtual
void
enqueue(std::function<
void
()> fn) = 0;
virtual
void
shutdown() = 0;
virtual
void
on_idle() {};
};
class
ThreadPool :
public
TaskQueue {
public
:
explicit
ThreadPool(
size_t
n) : shutdown_(
false
) {
while
(n) {
threads_.emplace_back(worker(*
this
));
n--;
}
}
ThreadPool(
const
ThreadPool&) =
delete
;
~ThreadPool() override =
default
;
void
enqueue(std::function<
void
()> fn) override {
std::unique_lock<std::mutex> lock(mutex_);
jobs_.push_back(std::move(fn));
cond_.notify_one();
}
void
shutdown() override {
{
std::unique_lock<std::mutex> lock(mutex_);
shutdown_ =
true
;
}
cond_.notify_all();
for
(
auto
& t : threads_) {
t.join();
}
}
private
:
struct
worker {
explicit
worker(ThreadPool& pool) : pool_(pool) {}
void
operator()() {
for
(;;) {
std::function<
void
()> fn;
{
std::unique_lock<std::mutex> lock(pool_.mutex_);
pool_.cond_.wait(
lock, [&] {
return
!pool_.jobs_.empty() || pool_.shutdown_; });
if
(pool_.shutdown_ && pool_.jobs_.empty()) {
break
; }
fn = pool_.jobs_.front();
pool_.jobs_.pop_front();
}
assert
(
true
==
static_cast
<
bool
>(fn));
fn();
}
}
ThreadPool& pool_;
};
friend
struct
worker;
std::vector<std::
thread
> threads_;
std::list<std::function<
void
()>> jobs_;
bool
shutdown_;
std::condition_variable cond_;
std::mutex mutex_;
};
using
Logger = std::function<
void
(
const
Request&,
const
Response&)>;
using
SocketOptions = std::function<
void
(socket_t sock)>;
inline
void
default_socket_options(socket_t sock) {
int
yes = 1;
#ifdef _WIN32
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast
<
char
*>(&yes),
sizeof
(yes));
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
reinterpret_cast
<
char
*>(&yes),
sizeof
(yes));
#else
#ifdef SO_REUSEPORT
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
reinterpret_cast
<
void
*>(&yes),
sizeof
(yes));
#else
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast
<
void
*>(&yes),
sizeof
(yes));
#endif
#endif
}
class
Server {
public
:
using
Handler = std::function<
void
(
const
Request&, Response&)>;
using
ExceptionHandler =
std::function<
void
(
const
Request&, Response&, std::exception& e)>;
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&)>;
Server();
virtual
~Server();
virtual
bool
is_valid()
const
;
Server& Get(
const
char
* pattern, Handler handler);
Server& Get(
const
char
* pattern,
size_t
pattern_len, Handler handler);
Server& Post(
const
char
* pattern, Handler handler);
Server& Post(
const
char
* pattern,
size_t
pattern_len, Handler handler);
Server& Post(
const
char
* pattern, HandlerWithContentReader handler);
Server& Post(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler);
Server& Put(
const
char
* pattern, Handler handler);
Server& Put(
const
char
* pattern,
size_t
pattern_len, Handler handler);
Server& Put(
const
char
* pattern, HandlerWithContentReader handler);
Server& Put(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler);
Server& Patch(
const
char
* pattern, Handler handler);
Server& Patch(
const
char
* pattern,
size_t
pattern_len, Handler handler);
Server& Patch(
const
char
* pattern, HandlerWithContentReader handler);
Server& Patch(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler);
Server& Delete(
const
char
* pattern, Handler handler);
Server& Delete(
const
char
* pattern,
size_t
pattern_len, Handler handler);
Server& Delete(
const
char
* pattern, HandlerWithContentReader handler);
Server& Delete(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler);
Server& Options(
const
char
* pattern, Handler handler);
Server& Options(
const
char
* pattern,
size_t
pattern_len, Handler handler);
bool
set_base_dir(
const
char
* dir,
const
char
* mount_point =
nullptr
);
bool
set_mount_point(
const
char
* mount_point,
const
char
* dir,
Headers headers = Headers());
bool
remove_mount_point(
const
char
* mount_point);
Server& set_file_extension_and_mimetype_mapping(
const
char
* ext,
const
char
* mime);
Server& set_file_request_handler(Handler handler);
Server& set_error_handler(HandlerWithResponse handler);
Server& set_error_handler(Handler handler);
Server& set_exception_handler(ExceptionHandler handler);
Server& set_pre_routing_handler(HandlerWithResponse handler);
Server& set_post_routing_handler(Handler handler);
Server& set_expect_100_continue_handler(Expect100ContinueHandler handler);
Server& set_logger(Logger logger);
Server& set_tcp_nodelay(
bool
on);
Server& set_socket_options(SocketOptions socket_options);
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);
Server& set_write_timeout(
time_t
sec,
time_t
usec = 0);
Server& set_idle_interval(
time_t
sec,
time_t
usec = 0);
Server& set_payload_max_length(
size_t
length);
bool
bind_to_port(
const
char
* host,
int
port,
int
socket_flags = 0);
int
bind_to_any_port(
const
char
* host,
int
socket_flags = 0);
bool
listen_after_bind();
bool
listen(
const
char
* host,
int
port,
int
socket_flags = 0);
bool
is_running()
const
;
void
stop();
std::function<TaskQueue* (
void
)> new_task_queue;
protected
:
bool
process_request(Stream& strm,
bool
close_connection,
bool
& connection_closed,
const
std::function<
void
(Request&)>& setup_request);
std::atomic<socket_t> svr_sock_;
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_READ_TIMEOUT_SECOND;
time_t
read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
time_t
write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
time_t
write_timeout_usec_ = CPPHTTPLIB_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::regex, Handler>>;
using
HandlersForContentReader =
std::vector<std::pair<std::regex, HandlerWithContentReader>>;
socket_t create_server_socket(
const
char
* host,
int
port,
int
socket_flags,
SocketOptions socket_options)
const
;
int
bind_internal(
const
char
* host,
int
port,
int
socket_flags);
bool
listen_internal();
bool
routing(Request& req, Response& res, Stream& strm);
bool
handle_file_request(
const
Request& req, Response& res,
bool
head =
false
);
bool
dispatch_request(Request& req, Response& res,
const
Handlers& handlers);
bool
dispatch_request_for_content_reader(Request& req, Response& res,
ContentReader content_reader,
const
HandlersForContentReader& handlers);
bool
parse_request_line(
const
char
* s, Request& req);
void
apply_ranges(
const
Request& req, Response& res,
std::string& content_type, std::string& boundary);
bool
write_response(Stream& strm,
bool
close_connection,
const
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,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver);
bool
read_content_core(Stream& strm, Request& req, Response& res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
ContentReceiver multipart_receiver);
virtual
bool
process_and_close_socket(socket_t sock);
struct
MountPointEntry {
std::string mount_point;
std::string base_dir;
Headers headers;
};
std::vector<MountPointEntry> base_dirs_;
std::atomic<
bool
> is_running_;
std::map<std::string, std::string> file_extension_and_mimetype_map_;
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_;
HandlerWithResponse error_handler_;
ExceptionHandler exception_handler_;
HandlerWithResponse pre_routing_handler_;
Handler post_routing_handler_;
Logger logger_;
Expect100ContinueHandler expect_100_continue_handler_;
bool
tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
SocketOptions socket_options_ = default_socket_options;
};
enum
Error {
Success = 0,
Unknown,
Connection,
BindIPAddress,
Read,
Write,
ExceedRedirectCount,
Canceled,
SSLConnection,
SSLLoadingCerts,
SSLServerVerification,
UnsupportedMultipartBoundaryChars,
Compression,
};
class
Result {
public
:
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
char
* key)
const
;
std::string get_request_header_value(
const
char
* key,
size_t
id = 0)
const
;
template
<
typename
T>
T get_request_header_value(
const
char
* key,
size_t
id = 0)
const
;
size_t
get_request_header_value_count(
const
char
* key)
const
;
private
:
std::unique_ptr<Response> res_;
Error err_;
Headers request_headers_;
};
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
;
Result Get(
const
char
* path);
Result Get(
const
char
* path,
const
Headers& headers);
Result Get(
const
char
* path, Progress progress);
Result Get(
const
char
* path,
const
Headers& headers, Progress progress);
Result Get(
const
char
* path, ContentReceiver content_receiver);
Result Get(
const
char
* path,
const
Headers& headers,
ContentReceiver content_receiver);
Result Get(
const
char
* path, ContentReceiver content_receiver,
Progress progress);
Result Get(
const
char
* path,
const
Headers& headers,
ContentReceiver content_receiver, Progress progress);
Result Get(
const
char
* path, ResponseHandler response_handler,
ContentReceiver content_receiver);
Result Get(
const
char
* path,
const
Headers& headers,
ResponseHandler response_handler,
ContentReceiver content_receiver);
Result Get(
const
char
* path, ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress);
Result Get(
const
char
* path,
const
Headers& headers,
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress);
Result Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
Progress progress =
nullptr
);
Result Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
ContentReceiver content_receiver, Progress progress =
nullptr
);
Result Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress =
nullptr
);
Result Head(
const
char
* path);
Result Head(
const
char
* path,
const
Headers& headers);
Result Post(
const
char
* path);
Result Post(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Post(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Post(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Post(
const
char
* path, ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Post(
const
char
* path,
const
Params& params);
Result Post(
const
char
* path,
const
Headers& headers,
const
Params& params);
Result Post(
const
char
* path,
const
MultipartFormDataItems& items);
Result Post(
const
char
* path,
const
Headers& headers,
const
MultipartFormDataItems& items);
Result Post(
const
char
* path,
const
Headers& headers,
const
MultipartFormDataItems& items,
const
std::string& boundary);
Result Put(
const
char
* path);
Result Put(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Put(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Put(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Put(
const
char
* path, ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Put(
const
char
* path,
const
Params& params);
Result Put(
const
char
* path,
const
Headers& headers,
const
Params& params);
Result Patch(
const
char
* path);
Result Patch(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Patch(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Patch(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Patch(
const
char
* path, ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Delete(
const
char
* path);
Result Delete(
const
char
* path,
const
Headers& headers);
Result Delete(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Delete(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Delete(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Delete(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Options(
const
char
* path);
Result Options(
const
char
* path,
const
Headers& headers);
bool
send(Request& req, Response& res, Error& error);
Result send(
const
Request& req);
size_t
is_socket_open()
const
;
void
stop();
void
set_default_headers(Headers headers);
void
set_tcp_nodelay(
bool
on);
void
set_socket_options(SocketOptions socket_options);
void
set_connection_timeout(
time_t
sec,
time_t
usec = 0);
void
set_read_timeout(
time_t
sec,
time_t
usec = 0);
void
set_write_timeout(
time_t
sec,
time_t
usec = 0);
void
set_basic_auth(
const
char
* username,
const
char
* password);
void
set_bearer_token_auth(
const
char
* token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
set_digest_auth(
const
char
* username,
const
char
* password);
#endif
void
set_keep_alive(
bool
on);
void
set_follow_location(
bool
on);
void
set_compress(
bool
on);
void
set_decompress(
bool
on);
void
set_interface(
const
char
* intf);
void
set_proxy(
const
char
* host,
int
port);
void
set_proxy_basic_auth(
const
char
* username,
const
char
* password);
void
set_proxy_bearer_token_auth(
const
char
* token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
set_proxy_digest_auth(
const
char
* username,
const
char
* password);
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
enable_server_certificate_verification(
bool
enabled);
#endif
void
set_logger(Logger logger);
protected
:
struct
Socket {
socket_t sock = INVALID_SOCKET;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSL* ssl =
nullptr
;
#endif
bool
is_open()
const
{
return
sock != INVALID_SOCKET; }
};
Result send_(Request&& req);
virtual
bool
create_and_connect_socket(Socket& socket, Error& error);
virtual
void
shutdown_ssl(Socket& socket,
bool
shutdown_gracefully);
void
shutdown_socket(Socket& socket);
void
close_socket(Socket& socket);
void
lock_socket_and_shutdown_and_close();
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);
void
copy_settings(
const
ClientImpl& rhs);
const
std::string host_;
const
int
port_;
const
std::string host_and_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
;
Headers default_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_READ_TIMEOUT_SECOND;
time_t
read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
time_t
write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
time_t
write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
std::string basic_auth_username_;
std::string basic_auth_password_;
std::string bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
std::string digest_auth_username_;
std::string digest_auth_password_;
#endif
bool
keep_alive_ =
false
;
bool
follow_location_ =
false
;
bool
tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
SocketOptions socket_options_ =
nullptr
;
bool
compress_ =
false
;
bool
decompress_ =
true
;
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_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
std::string proxy_digest_auth_username_;
std::string proxy_digest_auth_password_;
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
bool
server_certificate_verification_ =
true
;
#endif
Logger logger_;
private
:
socket_t create_client_socket(Error& error)
const
;
bool
read_response_line(Stream& strm,
const
Request& req, Response& res);
bool
write_request(Stream& strm, Request& req,
bool
close_connection,
Error& error);
bool
redirect(Request& req, Response& res, Error& error);
bool
handle_request(Stream& strm, Request& req, Response& res,
bool
close_connection, Error& error);
std::unique_ptr<Response> send_with_content_provider(
Request& req,
const
char
* body,
size_t
content_length, ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const
char
* content_type, Error& error);
Result send_with_content_provider(
const
char
* method,
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length, ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const
char
* content_type);
virtual
bool
process_socket(
const
Socket& socket,
std::function<
bool
(Stream& strm)> callback);
virtual
bool
is_ssl()
const
;
};
class
Client {
public
:
explicit
Client(
const
char
* scheme_host_port);
explicit
Client(
const
char
* 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();
bool
is_valid()
const
;
Result Get(
const
char
* path);
Result Get(
const
char
* path,
const
Headers& headers);
Result Get(
const
char
* path, Progress progress);
Result Get(
const
char
* path,
const
Headers& headers, Progress progress);
Result Get(
const
char
* path, ContentReceiver content_receiver);
Result Get(
const
char
* path,
const
Headers& headers,
ContentReceiver content_receiver);
Result Get(
const
char
* path, ContentReceiver content_receiver,
Progress progress);
Result Get(
const
char
* path,
const
Headers& headers,
ContentReceiver content_receiver, Progress progress);
Result Get(
const
char
* path, ResponseHandler response_handler,
ContentReceiver content_receiver);
Result Get(
const
char
* path,
const
Headers& headers,
ResponseHandler response_handler,
ContentReceiver content_receiver);
Result Get(
const
char
* path,
const
Headers& headers,
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress);
Result Get(
const
char
* path, ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress);
Result Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
Progress progress =
nullptr
);
Result Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
ContentReceiver content_receiver, Progress progress =
nullptr
);
Result Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
ResponseHandler response_handler, ContentReceiver content_receiver,
Progress progress =
nullptr
);
Result Head(
const
char
* path);
Result Head(
const
char
* path,
const
Headers& headers);
Result Post(
const
char
* path);
Result Post(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Post(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Post(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Post(
const
char
* path, ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Post(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Post(
const
char
* path,
const
Params& params);
Result Post(
const
char
* path,
const
Headers& headers,
const
Params& params);
Result Post(
const
char
* path,
const
MultipartFormDataItems& items);
Result Post(
const
char
* path,
const
Headers& headers,
const
MultipartFormDataItems& items);
Result Post(
const
char
* path,
const
Headers& headers,
const
MultipartFormDataItems& items,
const
std::string& boundary);
Result Put(
const
char
* path);
Result Put(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Put(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Put(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Put(
const
char
* path, ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Put(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Put(
const
char
* path,
const
Params& params);
Result Put(
const
char
* path,
const
Headers& headers,
const
Params& params);
Result Patch(
const
char
* path);
Result Patch(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Patch(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Patch(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Patch(
const
char
* path, ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type);
Result Patch(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type);
Result Delete(
const
char
* path);
Result Delete(
const
char
* path,
const
Headers& headers);
Result Delete(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Delete(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type);
Result Delete(
const
char
* path,
const
std::string& body,
const
char
* content_type);
Result Delete(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type);
Result Options(
const
char
* path);
Result Options(
const
char
* path,
const
Headers& headers);
bool
send(Request& req, Response& res, Error& error);
Result send(
const
Request& req);
size_t
is_socket_open()
const
;
void
stop();
void
set_default_headers(Headers headers);
void
set_tcp_nodelay(
bool
on);
void
set_socket_options(SocketOptions socket_options);
void
set_connection_timeout(
time_t
sec,
time_t
usec = 0);
void
set_read_timeout(
time_t
sec,
time_t
usec = 0);
void
set_write_timeout(
time_t
sec,
time_t
usec = 0);
void
set_basic_auth(
const
char
* username,
const
char
* password);
void
set_bearer_token_auth(
const
char
* token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
set_digest_auth(
const
char
* username,
const
char
* password);
#endif
void
set_keep_alive(
bool
on);
void
set_follow_location(
bool
on);
void
set_compress(
bool
on);
void
set_decompress(
bool
on);
void
set_interface(
const
char
* intf);
void
set_proxy(
const
char
* host,
int
port);
void
set_proxy_basic_auth(
const
char
* username,
const
char
* password);
void
set_proxy_bearer_token_auth(
const
char
* token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
set_proxy_digest_auth(
const
char
* username,
const
char
* password);
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
enable_server_certificate_verification(
bool
enabled);
#endif
void
set_logger(Logger logger);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void
set_ca_cert_path(
const
char
* ca_cert_file_path,
const
char
* ca_cert_dir_path =
nullptr
);
void
set_ca_cert_store(X509_STORE* ca_cert_store);
long
get_openssl_verify_result()
const
;
SSL_CTX* ssl_context()
const
;
#endif
private
:
std::unique_ptr<ClientImpl> cli_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
bool
is_ssl_ =
false
;
#endif
};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
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
);
SSLServer(X509* cert, EVP_PKEY* private_key,
X509_STORE* client_ca_cert_store =
nullptr
);
~SSLServer() override;
bool
is_valid()
const
override;
private
:
bool
process_and_close_socket(socket_t sock) override;
SSL_CTX* ctx_;
std::mutex ctx_mutex_;
};
class
SSLClient :
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);
explicit
SSLClient(
const
std::string& host,
int
port, X509* client_cert,
EVP_PKEY* client_key);
~SSLClient() override;
bool
is_valid()
const
override;
void
set_ca_cert_path(
const
char
* ca_cert_file_path,
const
char
* ca_cert_dir_path =
nullptr
);
void
set_ca_cert_store(X509_STORE* ca_cert_store);
long
get_openssl_verify_result()
const
;
SSL_CTX* ssl_context()
const
;
private
:
bool
create_and_connect_socket(Socket& socket, Error& error) override;
void
shutdown_ssl(Socket& socket,
bool
shutdown_gracefully) override;
bool
process_socket(
const
Socket& socket,
std::function<
bool
(Stream& strm)> callback) override;
bool
is_ssl()
const
override;
bool
connect_with_proxy(Socket& sock, Response& res,
bool
& success,
Error& error);
bool
initialize_ssl(Socket& socket, Error& error);
bool
load_certs();
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
;
bool
check_host_name(
const
char
* pattern,
size_t
pattern_len)
const
;
SSL_CTX* ctx_;
std::mutex ctx_mutex_;
std::once_flag initialize_cert_;
std::vector<std::string> host_components_;
std::string ca_cert_file_path_;
std::string ca_cert_dir_path_;
long
verify_result_ = 0;
friend
class
ClientImpl;
};
#endif
namespace
detail {
inline
bool
is_hex(
char
c,
int
& v) {
if
(0x20 <= c &&
isdigit
(c)) {
v = c -
'0'
;
return
true
;
}
else
if
(
'A'
<= c && c <=
'F'
) {
v = c -
'A'
+ 10;
return
true
;
}
else
if
(
'a'
<= c && c <=
'f'
) {
v = c -
'a'
+ 10;
return
true
;
}
return
false
;
}
inline
bool
from_hex_to_i(
const
std::string& s,
size_t
i,
size_t
cnt,
int
& val) {
if
(i >= s.size()) {
return
false
; }
val = 0;
for
(; cnt; i++, cnt--) {
if
(!s[i]) {
return
false
; }
int
v = 0;
if
(is_hex(s[i], v)) {
val = val * 16 + v;
}
else
{
return
false
;
}
}
return
true
;
}
inline
std::string from_i_to_hex(
size_t
n) {
const
char
* charset =
"0123456789abcdef"
;
std::string ret;
do
{
ret = charset[n & 15] + ret;
n >>= 4;
}
while
(n > 0);
return
ret;
}
inline
size_t
to_utf8(
int
code,
char
* buff) {
if
(code < 0x0080) {
buff[0] = (code & 0x7F);
return
1;
}
else
if
(code < 0x0800) {
buff[0] =
static_cast
<
char
>(0xC0 | ((code >> 6) & 0x1F));
buff[1] =
static_cast
<
char
>(0x80 | (code & 0x3F));
return
2;
}
else
if
(code < 0xD800) {
buff[0] =
static_cast
<
char
>(0xE0 | ((code >> 12) & 0xF));
buff[1] =
static_cast
<
char
>(0x80 | ((code >> 6) & 0x3F));
buff[2] =
static_cast
<
char
>(0x80 | (code & 0x3F));
return
3;
}
else
if
(code < 0xE000) {
return
0;
}
else
if
(code < 0x10000) {
buff[0] =
static_cast
<
char
>(0xE0 | ((code >> 12) & 0xF));
buff[1] =
static_cast
<
char
>(0x80 | ((code >> 6) & 0x3F));
buff[2] =
static_cast
<
char
>(0x80 | (code & 0x3F));
return
3;
}
else
if
(code < 0x110000) {
buff[0] =
static_cast
<
char
>(0xF0 | ((code >> 18) & 0x7));
buff[1] =
static_cast
<
char
>(0x80 | ((code >> 12) & 0x3F));
buff[2] =
static_cast
<
char
>(0x80 | ((code >> 6) & 0x3F));
buff[3] =
static_cast
<
char
>(0x80 | (code & 0x3F));
return
4;
}
return
0;
}
inline
std::string base64_encode(
const
std::string& in) {
static
const
auto
lookup =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
std::string out;
out.reserve(in.size());
int
val = 0;
int
valb = -6;
for
(
auto
c : in) {
val = (val << 8) +
static_cast
<uint8_t>(c);
valb += 8;
while
(valb >= 0) {
out.push_back(lookup[(val >> valb) & 0x3F]);
valb -= 6;
}
}
if
(valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }
while
(out.size() % 4) {
out.push_back(
'='
);
}
return
out;
}
inline
bool
is_file(
const
std::string& path) {
struct
stat st;
return
stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
}
inline
bool
is_dir(
const
std::string& path) {
struct
stat st;
return
stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
}
inline
bool
is_valid_path(
const
std::string& path) {
size_t
level = 0;
size_t
i = 0;
while
(i < path.size() && path[i] ==
'/'
) {
i++;
}
while
(i < path.size()) {
auto
beg = i;
while
(i < path.size() && path[i] !=
'/'
) {
i++;
}
auto
len = i - beg;
assert
(len > 0);
if
(!path.compare(beg, len,
"."
)) {
;
}
else
if
(!path.compare(beg, len,
".."
)) {
if
(level == 0) {
return
false
; }
level--;
}
else
{
level++;
}
while
(i < path.size() && path[i] ==
'/'
) {
i++;
}
}
return
true
;
}
inline
std::string encode_query_param(
const
std::string& value) {
std::ostringstream escaped;
escaped.fill(
'0'
);
escaped << std::hex;
for
(
auto
c : value) {
if
(std::
isalnum
(
static_cast
<uint8_t>(c)) || c ==
'-'
|| c ==
'_'
||
c ==
'.'
|| c ==
'!'
|| c ==
'~'
|| c ==
'*'
|| c ==
'\''
|| c ==
'('
||
c ==
')'
) {
escaped << c;
}
else
{
escaped << std::uppercase;
escaped <<
'%'
<< std::setw(2)
<<
static_cast
<
int
>(
static_cast
<unsigned
char
>(c));
escaped << std::nouppercase;
}
}
return
escaped.str();
}
inline
std::string encode_url(
const
std::string& s) {
std::string result;
for
(
size_t
i = 0; s[i]; i++) {
switch
(s[i]) {
case
' '
: result +=
"%20"
;
break
;
case
'+'
: result +=
"%2B"
;
break
;
case
'\r'
: result +=
"%0D"
;
break
;
case
'\n'
: result +=
"%0A"
;
break
;
case
'\''
: result +=
"%27"
;
break
;
case
','
: result +=
"%2C"
;
break
;
case
';'
: result +=
"%3B"
;
break
;
default
:
auto
c =
static_cast
<uint8_t>(s[i]);
if
(c >= 0x80) {
result +=
'%'
;
char
hex[4];
auto
len = snprintf(hex,
sizeof
(hex) - 1,
"%02X"
, c);
assert
(len == 2);
result.append(hex,
static_cast
<
size_t
>(len));
}
else
{
result += s[i];
}
break
;
}
}
return
result;
}
inline
std::string decode_url(
const
std::string& s,
bool
convert_plus_to_space) {
std::string result;
for
(
size_t
i = 0; i < s.size(); i++) {
if
(s[i] ==
'%'
&& i + 1 < s.size()) {
if
(s[i + 1] ==
'u'
) {
int
val = 0;
if
(from_hex_to_i(s, i + 2, 4, val)) {
char
buff[4];
size_t
len = to_utf8(val, buff);
if
(len > 0) { result.append(buff, len); }
i += 5;
}
else
{
result += s[i];
}
}
else
{
int
val = 0;
if
(from_hex_to_i(s, i + 1, 2, val)) {
result +=
static_cast
<
char
>(val);
i += 2;
}
else
{
result += s[i];
}
}
}
else
if
(convert_plus_to_space && s[i] ==
'+'
) {
result +=
' '
;
}
else
{
result += s[i];
}
}
return
result;
}
inline
void
read_file(
const
std::string& path, std::string& out) {
std::ifstream fs(path, std::ios_base::binary);
fs.seekg(0, std::ios_base::end);
auto
size = fs.tellg();
fs.seekg(0);
out.resize(
static_cast
<
size_t
>(size));
fs.read(&out[0],
static_cast
<std::streamsize>(size));
}
inline
std::string file_extension(
const
std::string& path) {
std::smatch m;
static
auto
re = std::regex(
"\\.([a-zA-Z0-9]+)$"
);
if
(std::regex_search(path, m, re)) {
return
m[1].str(); }
return
std::string();
}
inline
bool
is_space_or_tab(
char
c) {
return
c ==
' '
|| c ==
'\t'
; }
inline
std::pair<
size_t
,
size_t
> trim(
const
char
* b,
const
char
* e,
size_t
left,
size_t
right) {
while
(b + left < e && is_space_or_tab(b[left])) {
left++;
}
while
(right > 0 && is_space_or_tab(b[right - 1])) {
right--;
}
return
std::make_pair(left, right);
}
inline
std::string trim_copy(
const
std::string& s) {
auto
r = trim(s.data(), s.data() + s.size(), 0, s.size());
return
s.substr(r.first, r.second - r.first);
}
template
<
class
Fn>
void
split(
const
char
* b,
const
char
* e,
char
d, Fn fn) {
size_t
i = 0;
size_t
beg = 0;
while
(e ? (b + i < e) : (b[i] !=
'\0'
)) {
if
(b[i] == d) {
auto
r = trim(b, e, beg, i);
if
(r.first < r.second) { fn(&b[r.first], &b[r.second]); }
beg = i + 1;
}
i++;
}
if
(i) {
auto
r = trim(b, e, beg, i);
if
(r.first < r.second) { fn(&b[r.first], &b[r.second]); }
}
}
class
stream_line_reader {
public
:
stream_line_reader(Stream& strm,
char
* fixed_buffer,
size_t
fixed_buffer_size)
: strm_(strm), fixed_buffer_(fixed_buffer),
fixed_buffer_size_(fixed_buffer_size) {}
const
char
* ptr()
const
{
if
(glowable_buffer_.empty()) {
return
fixed_buffer_;
}
else
{
return
glowable_buffer_.data();
}
}
size_t
size()
const
{
if
(glowable_buffer_.empty()) {
return
fixed_buffer_used_size_;
}
else
{
return
glowable_buffer_.size();
}
}
bool
end_with_crlf()
const
{
auto
end = ptr() + size();
return
size() >= 2 && end[-2] ==
'\r'
&& end[-1] ==
'\n'
;
}
bool
getline() {
fixed_buffer_used_size_ = 0;
glowable_buffer_.clear();
for
(
size_t
i = 0;; i++) {
char
byte;
auto
n = strm_.read(&byte, 1);
if
(n < 0) {
return
false
;
}
else
if
(n == 0) {
if
(i == 0) {
return
false
;
}
else
{
break
;
}
}
append(byte);
if
(byte ==
'\n'
) {
break
; }
}
return
true
;
}
private
:
void
append(
char
c) {
if
(fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {
fixed_buffer_[fixed_buffer_used_size_++] = c;
fixed_buffer_[fixed_buffer_used_size_] =
'\0'
;
}
else
{
if
(glowable_buffer_.empty()) {
assert
(fixed_buffer_[fixed_buffer_used_size_] ==
'\0'
);
glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
}
glowable_buffer_ += c;
}
}
Stream& strm_;
char
* fixed_buffer_;
const
size_t
fixed_buffer_size_;
size_t
fixed_buffer_used_size_ = 0;
std::string glowable_buffer_;
};
inline
int
close_socket(socket_t sock) {
#ifdef _WIN32
return
closesocket(sock);
#else
return
close(sock);
#endif
}
template
<
typename
T>
inline
ssize_t handle_EINTR(T fn) {
ssize_t res =
false
;
while
(
true
) {
res = fn();
if
(res < 0 &&
errno
== EINTR) {
continue
; }
break
;
}
return
res;
}
inline
ssize_t select_read(socket_t sock,
time_t
sec,
time_t
usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct
pollfd pfd_read;
pfd_read.fd = sock;
pfd_read.events = POLLIN;
auto
timeout =
static_cast
<
int
>(sec * 1000 + usec / 1000);
return
handle_EINTR([&]() {
return
poll(&pfd_read, 1, timeout); });
#else
#ifndef _WIN32
if
(sock >= FD_SETSIZE) {
return
1; }
#endif
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
timeval tv;
tv.tv_sec =
static_cast
<
long
>(sec);
tv.tv_usec =
static_cast
<
decltype
(tv.tv_usec)>(usec);
return
handle_EINTR([&]() {
return
select(
static_cast
<
int
>(sock + 1), &fds,
nullptr
,
nullptr
, &tv);
});
#endif
}
inline
ssize_t select_write(socket_t sock,
time_t
sec,
time_t
usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct
pollfd pfd_read;
pfd_read.fd = sock;
pfd_read.events = POLLOUT;
auto
timeout =
static_cast
<
int
>(sec * 1000 + usec / 1000);
return
handle_EINTR([&]() {
return
poll(&pfd_read, 1, timeout); });
#else
#ifndef _WIN32
if
(sock >= FD_SETSIZE) {
return
1; }
#endif
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
timeval tv;
tv.tv_sec =
static_cast
<
long
>(sec);
tv.tv_usec =
static_cast
<
decltype
(tv.tv_usec)>(usec);
return
handle_EINTR([&]() {
return
select(
static_cast
<
int
>(sock + 1),
nullptr
, &fds,
nullptr
, &tv);
});
#endif
}
inline
bool
wait_until_socket_is_ready(socket_t sock,
time_t
sec,
time_t
usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct
pollfd pfd_read;
pfd_read.fd = sock;
pfd_read.events = POLLIN | POLLOUT;
auto
timeout =
static_cast
<
int
>(sec * 1000 + usec / 1000);
auto
poll_res = handle_EINTR([&]() {
return
poll(&pfd_read, 1, timeout); });
if
(poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
int
error = 0;
socklen_t len =
sizeof
(error);
auto
res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast
<
char
*>(&error), &len);
return
res >= 0 && !error;
}
return
false
;
#else
#ifndef _WIN32
if
(sock >= FD_SETSIZE) {
return
false
; }
#endif
fd_set fdsr;
FD_ZERO(&fdsr);
FD_SET(sock, &fdsr);
auto
fdsw = fdsr;
auto
fdse = fdsr;
timeval tv;
tv.tv_sec =
static_cast
<
long
>(sec);
tv.tv_usec =
static_cast
<
decltype
(tv.tv_usec)>(usec);
auto
ret = handle_EINTR([&]() {
return
select(
static_cast
<
int
>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
});
if
(ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int
error = 0;
socklen_t len =
sizeof
(error);
return
getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast
<
char
*>(&error), &len) >= 0 &&
!error;
}
return
false
;
#endif
}
class
SocketStream :
public
Stream {
public
:
SocketStream(socket_t sock,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec);
~SocketStream() override;
bool
is_readable()
const
override;
bool
is_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;
socket_t socket()
const
override;
private
:
socket_t sock_;
time_t
read_timeout_sec_;
time_t
read_timeout_usec_;
time_t
write_timeout_sec_;
time_t
write_timeout_usec_;
};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
class
SSLSocketStream :
public
Stream {
public
:
SSLSocketStream(socket_t sock, SSL* ssl,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec);
~SSLSocketStream() override;
bool
is_readable()
const
override;
bool
is_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;
socket_t socket()
const
override;
private
:
socket_t sock_;
SSL* ssl_;
time_t
read_timeout_sec_;
time_t
read_timeout_usec_;
time_t
write_timeout_sec_;
time_t
write_timeout_usec_;
};
#endif
class
BufferStream :
public
Stream {
public
:
BufferStream() =
default
;
~BufferStream() override =
default
;
bool
is_readable()
const
override;
bool
is_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;
socket_t socket()
const
override;
const
std::string& get_buffer()
const
;
private
:
std::string buffer;
size_t
position = 0;
};
inline
bool
keep_alive(socket_t sock,
time_t
keep_alive_timeout_sec) {
using
namespace
std::chrono;
auto
start = steady_clock::now();
while
(
true
) {
auto
val = select_read(sock, 0, 10000);
if
(val < 0) {
return
false
;
}
else
if
(val == 0) {
auto
current = steady_clock::now();
auto
duration = duration_cast<milliseconds>(current - start);
auto
timeout = keep_alive_timeout_sec * 1000;
if
(duration.count() > timeout) {
return
false
; }
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
else
{
return
true
;
}
}
}
template
<
typename
T>
inline
bool
process_server_socket_core(socket_t sock,
size_t
keep_alive_max_count,
time_t
keep_alive_timeout_sec, T callback) {
assert
(keep_alive_max_count > 0);
auto
ret =
false
;
auto
count = keep_alive_max_count;
while
(count > 0 && keep_alive(sock, keep_alive_timeout_sec)) {
auto
close_connection = count == 1;
auto
connection_closed =
false
;
ret = callback(close_connection, connection_closed);
if
(!ret || connection_closed) {
break
; }
count--;
}
return
ret;
}
template
<
typename
T>
inline
bool
process_server_socket(socket_t sock,
size_t
keep_alive_max_count,
time_t
keep_alive_timeout_sec,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec, T callback) {
return
process_server_socket_core(
sock, keep_alive_max_count, keep_alive_timeout_sec,
[&](
bool
close_connection,
bool
& connection_closed) {
SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
write_timeout_sec, write_timeout_usec);
return
callback(strm, close_connection, connection_closed);
});
}
template
<
typename
T>
inline
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, T callback) {
SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
write_timeout_sec, write_timeout_usec);
return
callback(strm);
}
inline
int
shutdown_socket(socket_t sock) {
#ifdef _WIN32
return
shutdown(sock, SD_BOTH);
#else
return
shutdown(sock, SHUT_RDWR);
#endif
}
template
<
typename
BindOrConnect>
socket_t create_socket(
const
char
* host,
int
port,
int
socket_flags,
bool
tcp_nodelay, SocketOptions socket_options,
BindOrConnect bind_or_connect) {
struct
addrinfo hints;
struct
addrinfo* result;
memset
(&hints, 0,
sizeof
(
struct
addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = socket_flags;
hints.ai_protocol = 0;
auto
service = std::to_string(port);
if
(getaddrinfo(host, service.c_str(), &hints, &result)) {
#ifdef __linux__
res_init();
#endif
return
INVALID_SOCKET;
}
for
(
auto
rp = result; rp; rp = rp->ai_next) {
#ifdef _WIN32
auto
sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr
, 0, WSA_FLAG_NO_HANDLE_INHERIT);
if
(sock == INVALID_SOCKET) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
}
#else
auto
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
#endif
if
(sock == INVALID_SOCKET) {
continue
; }
#ifndef _WIN32
if
(fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
continue
; }
#endif
if
(tcp_nodelay) {
int
yes = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast
<
char
*>(&yes),
sizeof
(yes));
}
if
(socket_options) { socket_options(sock); }
if
(rp->ai_family == AF_INET6) {
int
no = 0;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast
<
char
*>(&no),
sizeof
(no));
}
if
(bind_or_connect(sock, *rp)) {
freeaddrinfo(result);
return
sock;
}
close_socket(sock);
}
freeaddrinfo(result);
return
INVALID_SOCKET;
}
inline
void
set_nonblocking(socket_t sock,
bool
nonblocking) {
#ifdef _WIN32
auto
flags = nonblocking ? 1UL : 0UL;
ioctlsocket(sock, FIONBIO, &flags);
#else
auto
flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL,
nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));
#endif
}
inline
bool
is_connection_error() {
#ifdef _WIN32
return
WSAGetLastError() != WSAEWOULDBLOCK;
#else
return
errno
!= EINPROGRESS;
#endif
}
inline
bool
bind_ip_address(socket_t sock,
const
char
* host) {
struct
addrinfo hints;
struct
addrinfo* result;
memset
(&hints, 0,
sizeof
(
struct
addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if
(getaddrinfo(host,
"0"
, &hints, &result)) {
return
false
; }
auto
ret =
false
;
for
(
auto
rp = result; rp; rp = rp->ai_next) {
const
auto
& ai = *rp;
if
(!::bind(sock, ai.ai_addr,
static_cast
<socklen_t>(ai.ai_addrlen))) {
ret =
true
;
break
;
}
}
freeaddrinfo(result);
return
ret;
}
#if !defined _WIN32 && !defined ANDROID
#define USE_IF2IP
#endif
#ifdef USE_IF2IP
inline
std::string if2ip(
const
std::string& ifn) {
struct
ifaddrs* ifap;
getifaddrs(&ifap);
for
(
auto
ifa = ifap; ifa; ifa = ifa->ifa_next) {
if
(ifa->ifa_addr && ifn == ifa->ifa_name) {
if
(ifa->ifa_addr->sa_family == AF_INET) {
auto
sa =
reinterpret_cast
<
struct
sockaddr_in*>(ifa->ifa_addr);
char
buf[INET_ADDRSTRLEN];
if
(inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
freeifaddrs(ifap);
return
std::string(buf, INET_ADDRSTRLEN);
}
}
}
}
freeifaddrs(ifap);
return
std::string();
}
#endif
inline
socket_t create_client_socket(
const
char
* host,
int
port,
bool
tcp_nodelay,
SocketOptions socket_options,
time_t
timeout_sec,
time_t
timeout_usec,
const
std::string& intf, Error& error) {
auto
sock = create_socket(
host, port, 0, tcp_nodelay, std::move(socket_options),
[&](socket_t sock,
struct
addrinfo& ai) ->
bool
{
if
(!intf.empty()) {
#ifdef USE_IF2IP
auto
ip = if2ip(intf);
if
(ip.empty()) { ip = intf; }
if
(!bind_ip_address(sock, ip.c_str())) {
error = Error::BindIPAddress;
return
false
;
}
#endif
}
set_nonblocking(sock,
true
);
auto
ret =
::connect(sock, ai.ai_addr,
static_cast
<socklen_t>(ai.ai_addrlen));
if
(ret < 0) {
if
(is_connection_error() ||
!wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) {
close_socket(sock);
error = Error::Connection;
return
false
;
}
}
set_nonblocking(sock,
false
);
error = Error::Success;
return
true
;
});
if
(sock != INVALID_SOCKET) {
error = Error::Success;
}
else
{
if
(error == Error::Success) { error = Error::Connection; }
}
return
sock;
}
inline
void
get_remote_ip_and_port(
const
struct
sockaddr_storage& addr,
socklen_t addr_len, std::string& ip,
int
& port) {
if
(addr.ss_family == AF_INET) {
port = ntohs(
reinterpret_cast
<
const
struct
sockaddr_in*>(&addr)->sin_port);
}
else
if
(addr.ss_family == AF_INET6) {
port =
ntohs(
reinterpret_cast
<
const
struct
sockaddr_in6*>(&addr)->sin6_port);
}
std::array<
char
, NI_MAXHOST> ipstr{};
if
(!getnameinfo(
reinterpret_cast
<
const
struct
sockaddr*>(&addr), addr_len,
ipstr.data(),
static_cast
<socklen_t>(ipstr.size()),
nullptr
,
0, NI_NUMERICHOST)) {
ip = ipstr.data();
}
}
inline
void
get_remote_ip_and_port(socket_t sock, std::string& ip,
int
& port) {
struct
sockaddr_storage addr;
socklen_t addr_len =
sizeof
(addr);
if
(!getpeername(sock,
reinterpret_cast
<
struct
sockaddr*>(&addr),
&addr_len)) {
get_remote_ip_and_port(addr, addr_len, ip, port);
}
}
inline
constexpr
unsigned
int
str2tag_core(
const
char
* s,
size_t
l,
unsigned
int
h) {
return
(l == 0) ? h
: str2tag_core(s + 1, l - 1,
(h * 33) ^
static_cast
<unsigned
char
>(*s));
}
inline
unsigned
int
str2tag(
const
std::string& s) {
return
str2tag_core(s.data(), s.size(), 0);
}
namespace
udl {
inline
constexpr
unsigned
int
operator
""
_(
const
char
* s,
size_t
l) {
return
str2tag_core(s, l, 0);
}
}
inline
const
char
*
find_content_type(
const
std::string& path,
const
std::map<std::string, std::string>& user_data) {
auto
ext = file_extension(path);
auto
it = user_data.find(ext);
if
(it != user_data.end()) {
return
it->second.c_str(); }
using
udl::operator
""
_;
switch
(str2tag(ext)) {
default
:
return
nullptr
;
case
"css"
_:
return
"text/css"
;
case
"csv"
_:
return
"text/csv"
;
case
"txt"
_:
return
"text/plain"
;
case
"vtt"
_:
return
"text/vtt"
;
case
"htm"
_:
case
"html"
_:
return
"text/html"
;
case
"apng"
_:
return
"image/apng"
;
case
"avif"
_:
return
"image/avif"
;
case
"bmp"
_:
return
"image/bmp"
;
case
"gif"
_:
return
"image/gif"
;
case
"png"
_:
return
"image/png"
;
case
"svg"
_:
return
"image/svg+xml"
;
case
"webp"
_:
return
"image/webp"
;
case
"ico"
_:
return
"image/x-icon"
;
case
"tif"
_:
return
"image/tiff"
;
case
"tiff"
_:
return
"image/tiff"
;
case
"jpg"
_:
case
"jpeg"
_:
return
"image/jpeg"
;
case
"mp4"
_:
return
"video/mp4"
;
case
"mpeg"
_:
return
"video/mpeg"
;
case
"webm"
_:
return
"video/webm"
;
case
"mp3"
_:
return
"audio/mp3"
;
case
"mpga"
_:
return
"audio/mpeg"
;
case
"weba"
_:
return
"audio/webm"
;
case
"wav"
_:
return
"audio/wave"
;
case
"otf"
_:
return
"font/otf"
;
case
"ttf"
_:
return
"font/ttf"
;
case
"woff"
_:
return
"font/woff"
;
case
"woff2"
_:
return
"font/woff2"
;
case
"7z"
_:
return
"application/x-7z-compressed"
;
case
"atom"
_:
return
"application/atom+xml"
;
case
"pdf"
_:
return
"application/pdf"
;
case
"js"
_:
case
"mjs"
_:
return
"application/javascript"
;
case
"json"
_:
return
"application/json"
;
case
"rss"
_:
return
"application/rss+xml"
;
case
"tar"
_:
return
"application/x-tar"
;
case
"xht"
_:
case
"xhtml"
_:
return
"application/xhtml+xml"
;
case
"xslt"
_:
return
"application/xslt+xml"
;
case
"xml"
_:
return
"application/xml"
;
case
"gz"
_:
return
"application/gzip"
;
case
"zip"
_:
return
"application/zip"
;
case
"wasm"
_:
return
"application/wasm"
;
}
}
inline
const
char
* status_message(
int
status) {
switch
(status) {
case
100:
return
"Continue"
;
case
101:
return
"Switching Protocol"
;
case
102:
return
"Processing"
;
case
103:
return
"Early Hints"
;
case
200:
return
"OK"
;
case
201:
return
"Created"
;
case
202:
return
"Accepted"
;
case
203:
return
"Non-Authoritative Information"
;
case
204:
return
"No Content"
;
case
205:
return
"Reset Content"
;
case
206:
return
"Partial Content"
;
case
207:
return
"Multi-Status"
;
case
208:
return
"Already Reported"
;
case
226:
return
"IM Used"
;
case
300:
return
"Multiple Choice"
;
case
301:
return
"Moved Permanently"
;
case
302:
return
"Found"
;
case
303:
return
"See Other"
;
case
304:
return
"Not Modified"
;
case
305:
return
"Use Proxy"
;
case
306:
return
"unused"
;
case
307:
return
"Temporary Redirect"
;
case
308:
return
"Permanent Redirect"
;
case
400:
return
"Bad Request"
;
case
401:
return
"Unauthorized"
;
case
402:
return
"Payment Required"
;
case
403:
return
"Forbidden"
;
case
404:
return
"Not Found"
;
case
405:
return
"Method Not Allowed"
;
case
406:
return
"Not Acceptable"
;
case
407:
return
"Proxy Authentication Required"
;
case
408:
return
"Request Timeout"
;
case
409:
return
"Conflict"
;
case
410:
return
"Gone"
;
case
411:
return
"Length Required"
;
case
412:
return
"Precondition Failed"
;
case
413:
return
"Payload Too Large"
;
case
414:
return
"URI Too Long"
;
case
415:
return
"Unsupported Media Type"
;
case
416:
return
"Range Not Satisfiable"
;
case
417:
return
"Expectation Failed"
;
case
418:
return
"I'm a teapot"
;
case
421:
return
"Misdirected Request"
;
case
422:
return
"Unprocessable Entity"
;
case
423:
return
"Locked"
;
case
424:
return
"Failed Dependency"
;
case
425:
return
"Too Early"
;
case
426:
return
"Upgrade Required"
;
case
428:
return
"Precondition Required"
;
case
429:
return
"Too Many Requests"
;
case
431:
return
"Request Header Fields Too Large"
;
case
451:
return
"Unavailable For Legal Reasons"
;
case
501:
return
"Not Implemented"
;
case
502:
return
"Bad Gateway"
;
case
503:
return
"Service Unavailable"
;
case
504:
return
"Gateway Timeout"
;
case
505:
return
"HTTP Version Not Supported"
;
case
506:
return
"Variant Also Negotiates"
;
case
507:
return
"Insufficient Storage"
;
case
508:
return
"Loop Detected"
;
case
510:
return
"Not Extended"
;
case
511:
return
"Network Authentication Required"
;
default
:
case
500:
return
"Internal Server Error"
;
}
}
inline
bool
can_compress_content_type(
const
std::string& content_type) {
return
(!content_type.find(
"text/"
) && content_type !=
"text/event-stream"
) ||
content_type ==
"image/svg+xml"
||
content_type ==
"application/javascript"
||
content_type ==
"application/json"
||
content_type ==
"application/xml"
||
content_type ==
"application/xhtml+xml"
;
}
enum
class
EncodingType { None = 0, Gzip, Brotli };
inline
EncodingType encoding_type(
const
Request& req,
const
Response& res) {
auto
ret =
detail::can_compress_content_type(res.get_header_value(
"Content-Type"
));
if
(!ret) {
return
EncodingType::None; }
const
auto
& s = req.get_header_value(
"Accept-Encoding"
);
(
void
)(s);
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
ret = s.find(
"br"
) != std::string::npos;
if
(ret) {
return
EncodingType::Brotli; }
#endif
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
ret = s.find(
"gzip"
) != std::string::npos;
if
(ret) {
return
EncodingType::Gzip; }
#endif
return
EncodingType::None;
}
class
compressor {
public
:
virtual
~compressor() {};
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() {}
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 :
public
compressor {
public
:
~nocompressor() {};
bool
compress(
const
char
* data,
size_t
data_length,
bool
,
Callback callback) override {
if
(!data_length) {
return
true
; }
return
callback(data, data_length);
}
};
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
class
gzip_compressor :
public
compressor {
public
:
gzip_compressor() {
std::
memset
(&strm_, 0,
sizeof
(strm_));
strm_.zalloc = Z_NULL;
strm_.zfree = Z_NULL;
strm_.opaque = Z_NULL;
is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
Z_DEFAULT_STRATEGY) == Z_OK;
}
~gzip_compressor() { deflateEnd(&strm_); }
bool
compress(
const
char
* data,
size_t
data_length,
bool
last,
Callback callback) override {
assert
(is_valid_);
auto
flush = last ? Z_FINISH : Z_NO_FLUSH;
strm_.avail_in =
static_cast
<
decltype
(strm_.avail_in)>(data_length);
strm_.next_in =
const_cast
<Bytef*>(
reinterpret_cast
<
const
Bytef*>(data));
int
ret = Z_OK;
std::array<
char
, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
do
{
strm_.avail_out = buff.size();
strm_.next_out =
reinterpret_cast
<Bytef*>(buff.data());
ret = deflate(&strm_, flush);
if
(ret == Z_STREAM_ERROR) {
return
false
; }
if
(!callback(buff.data(), buff.size() - strm_.avail_out)) {
return
false
;
}
}
while
(strm_.avail_out == 0);
assert
((last && ret == Z_STREAM_END) || (!last && ret == Z_OK));
assert
(strm_.avail_in == 0);
return
true
;
}
private
:
bool
is_valid_ =
false
;
z_stream strm_;
};
class
gzip_decompressor :
public
decompressor {
public
:
gzip_decompressor() {
std::
memset
(&strm_, 0,
sizeof
(strm_));
strm_.zalloc = Z_NULL;
strm_.zfree = Z_NULL;
strm_.opaque = Z_NULL;
is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
}
~gzip_decompressor() { inflateEnd(&strm_); }
bool
is_valid()
const
override {
return
is_valid_; }
bool
decompress(
const
char
* data,
size_t
data_length,
Callback callback) override {
assert
(is_valid_);
int
ret = Z_OK;
strm_.avail_in =
static_cast
<
decltype
(strm_.avail_in)>(data_length);
strm_.next_in =
const_cast
<Bytef*>(
reinterpret_cast
<
const
Bytef*>(data));
std::array<
char
, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
while
(strm_.avail_in > 0) {
strm_.avail_out = buff.size();
strm_.next_out =
reinterpret_cast
<Bytef*>(buff.data());
ret = inflate(&strm_, Z_NO_FLUSH);
assert
(ret != Z_STREAM_ERROR);
switch
(ret) {
case
Z_NEED_DICT:
case
Z_DATA_ERROR:
case
Z_MEM_ERROR: inflateEnd(&strm_);
return
false
;
}
if
(!callback(buff.data(), buff.size() - strm_.avail_out)) {
return
false
;
}
}
return
ret == Z_OK || ret == Z_STREAM_END;
}
private
:
bool
is_valid_ =
false
;
z_stream strm_;
};
#endif
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
class
brotli_compressor :
public
compressor {
public
:
brotli_compressor() {
state_ = BrotliEncoderCreateInstance(
nullptr
,
nullptr
,
nullptr
);
}
~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
bool
compress(
const
char
* data,
size_t
data_length,
bool
last,
Callback callback) override {
std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
auto
operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
auto
available_in = data_length;
auto
next_in =
reinterpret_cast
<
const
uint8_t*>(data);
for
(;;) {
if
(last) {
if
(BrotliEncoderIsFinished(state_)) {
break
; }
}
else
{
if
(!available_in) {
break
; }
}
auto
available_out = buff.size();
auto
next_out = buff.data();
if
(!BrotliEncoderCompressStream(state_, operation, &available_in,
&next_in, &available_out, &next_out,
nullptr
)) {
return
false
;
}
auto
output_bytes = buff.size() - available_out;
if
(output_bytes) {
callback(
reinterpret_cast
<
const
char
*>(buff.data()), output_bytes);
}
}
return
true
;
}
private
:
BrotliEncoderState* state_ =
nullptr
;
};
class
brotli_decompressor :
public
decompressor {
public
:
brotli_decompressor() {
decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
: BROTLI_DECODER_RESULT_ERROR;
}
~brotli_decompressor() {
if
(decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
}
bool
is_valid()
const
override {
return
decoder_s; }
bool
decompress(
const
char
* data,
size_t
data_length,
Callback callback) override {
if
(decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return
0;
}
const
uint8_t* next_in = (
const
uint8_t*)data;
size_t
avail_in = data_length;
size_t
total_out;
decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
std::array<
char
, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
while
(decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
char
* next_out = buff.data();
size_t
avail_out = buff.size();
decoder_r = BrotliDecoderDecompressStream(
decoder_s, &avail_in, &next_in, &avail_out,
reinterpret_cast
<uint8_t**>(&next_out), &total_out);
if
(decoder_r == BROTLI_DECODER_RESULT_ERROR) {
return
false
; }
if
(!callback(buff.data(), buff.size() - avail_out)) {
return
false
; }
}
return
decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
}
private
:
BrotliDecoderResult decoder_r;
BrotliDecoderState* decoder_s =
nullptr
;
};
#endif
inline
bool
has_header(
const
Headers& headers,
const
char
* key) {
return
headers.find(key) != headers.end();
}
inline
const
char
* get_header_value(
const
Headers& headers,
const
char
* key,
size_t
id = 0,
const
char
* def =
nullptr
) {
auto
rng = headers.equal_range(key);
auto
it = rng.first;
std::advance(it,
static_cast
<ssize_t>(id));
if
(it != rng.second) {
return
it->second.c_str(); }
return
def;
}
template
<
typename
T>
inline
T get_header_value(
const
Headers&
,
const
char
*
,
size_t
= 0, uint64_t
= 0) {}
template
<>
inline
uint64_t get_header_value<uint64_t>(
const
Headers& headers,
const
char
* key,
size_t
id,
uint64_t def) {
auto
rng = headers.equal_range(key);
auto
it = rng.first;
std::advance(it,
static_cast
<ssize_t>(id));
if
(it != rng.second) {
return
std::strtoull(it->second.data(),
nullptr
, 10);
}
return
def;
}
template
<
typename
T>
inline
bool
parse_header(
const
char
* beg,
const
char
* end, T fn) {
while
(beg < end && is_space_or_tab(end[-1])) {
end--;
}
auto
p = beg;
while
(p < end && *p !=
':'
) {
p++;
}
if
(p == end) {
return
false
; }
auto
key_end = p;
if
(*p++ !=
':'
) {
return
false
; }
while
(p < end && is_space_or_tab(*p)) {
p++;
}
if
(p < end) {
fn(std::string(beg, key_end), decode_url(std::string(p, end),
false
));
return
true
;
}
return
false
;
}
inline
bool
read_headers(Stream& strm, Headers& headers) {
const
auto
bufsiz = 2048;
char
buf[bufsiz];
stream_line_reader line_reader(strm, buf, bufsiz);
for
(;;) {
if
(!line_reader.getline()) {
return
false
; }
if
(line_reader.end_with_crlf()) {
if
(line_reader.size() == 2) {
break
; }
}
else
{
continue
;
}
auto
end = line_reader.ptr() + line_reader.size() - 2;
parse_header(line_reader.ptr(), end,
[&](std::string&& key, std::string&& val) {
headers.emplace(std::move(key), std::move(val));
});
}
return
true
;
}
inline
bool
read_content_with_length(Stream& strm, uint64_t len,
Progress progress,
ContentReceiverWithProgress out) {
char
buf[CPPHTTPLIB_RECV_BUFSIZ];
uint64_t r = 0;
while
(r < len) {
auto
read_len =
static_cast
<
size_t
>(len - r);
auto
n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if
(n <= 0) {
return
false
; }
if
(!out(buf,
static_cast
<
size_t
>(n), r, len)) {
return
false
; }
r +=
static_cast
<uint64_t>(n);
if
(progress) {
if
(!progress(r, len)) {
return
false
; }
}
}
return
true
;
}
inline
void
skip_content_with_length(Stream& strm, uint64_t len) {
char
buf[CPPHTTPLIB_RECV_BUFSIZ];
uint64_t r = 0;
while
(r < len) {
auto
read_len =
static_cast
<
size_t
>(len - r);
auto
n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if
(n <= 0) {
return
; }
r +=
static_cast
<uint64_t>(n);
}
}
inline
bool
read_content_without_length(Stream& strm,
ContentReceiverWithProgress out) {
char
buf[CPPHTTPLIB_RECV_BUFSIZ];
uint64_t r = 0;
for
(;;) {
auto
n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
if
(n < 0) {
return
false
;
}
else
if
(n == 0) {
return
true
;
}
if
(!out(buf,
static_cast
<
size_t
>(n), r, 0)) {
return
false
; }
r +=
static_cast
<uint64_t>(n);
}
return
true
;
}
inline
bool
read_content_chunked(Stream& strm,
ContentReceiverWithProgress out) {
const
auto
bufsiz = 16;
char
buf[bufsiz];
stream_line_reader line_reader(strm, buf, bufsiz);
if
(!line_reader.getline()) {
return
false
; }
unsigned
long
chunk_len;
while
(
true
) {
char
* end_ptr;
chunk_len = std::
strtoul
(line_reader.ptr(), &end_ptr, 16);
if
(end_ptr == line_reader.ptr()) {
return
false
; }
if
(chunk_len == ULONG_MAX) {
return
false
; }
if
(chunk_len == 0) {
break
; }
if
(!read_content_with_length(strm, chunk_len,
nullptr
, out)) {
return
false
;
}
if
(!line_reader.getline()) {
return
false
; }
if
(
strcmp
(line_reader.ptr(),
"\r\n"
)) {
break
; }
if
(!line_reader.getline()) {
return
false
; }
}
if
(chunk_len == 0) {
if
(!line_reader.getline() ||
strcmp
(line_reader.ptr(),
"\r\n"
))
return
false
;
}
return
true
;
}
inline
bool
is_chunked_transfer_encoding(
const
Headers& headers) {
return
!strcasecmp(get_header_value(headers,
"Transfer-Encoding"
, 0,
""
),
"chunked"
);
}
template
<
typename
T,
typename
U>
bool
prepare_content_receiver(T& x,
int
& status,
ContentReceiverWithProgress receiver,
bool
decompress, U callback) {
if
(decompress) {
std::string encoding = x.get_header_value(
"Content-Encoding"
);
std::unique_ptr<decompressor> decompressor;
if
(encoding.find(
"gzip"
) != std::string::npos ||
encoding.find(
"deflate"
) != std::string::npos) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
decompressor = detail::make_unique<gzip_decompressor>();
#else
status = 415;
return
false
;
#endif
}
else
if
(encoding.find(
"br"
) != std::string::npos) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
decompressor = detail::make_unique<brotli_decompressor>();
#else
status = 415;
return
false
;
#endif
}
if
(decompressor) {
if
(decompressor->is_valid()) {
ContentReceiverWithProgress out = [&](
const
char
* buf,
size_t
n,
uint64_t off, uint64_t len) {
return
decompressor->decompress(buf, n,
[&](
const
char
* buf,
size_t
n) {
return
receiver(buf, n, off, len);
});
};
return
callback(std::move(out));
}
else
{
status = 500;
return
false
;
}
}
}
ContentReceiverWithProgress out = [&](
const
char
* buf,
size_t
n, uint64_t off,
uint64_t len) {
return
receiver(buf, n, off, len);
};
return
callback(std::move(out));
}
template
<
typename
T>
bool
read_content(Stream& strm, T& x,
size_t
payload_max_length,
int
& status,
Progress progress, ContentReceiverWithProgress receiver,
bool
decompress) {
return
prepare_content_receiver(
x, status, std::move(receiver), decompress,
[&](
const
ContentReceiverWithProgress& out) {
auto
ret =
true
;
auto
exceed_payload_max_length =
false
;
if
(is_chunked_transfer_encoding(x.headers)) {
ret = read_content_chunked(strm, out);
}
else
if
(!has_header(x.headers,
"Content-Length"
)) {
ret = read_content_without_length(strm, out);
}
else
{
auto
len = get_header_value<uint64_t>(x.headers,
"Content-Length"
);
if
(len > payload_max_length) {
exceed_payload_max_length =
true
;
skip_content_with_length(strm, len);
ret =
false
;
}
else
if
(len > 0) {
ret = read_content_with_length(strm, len, std::move(progress), out);
}
}
if
(!ret) { status = exceed_payload_max_length ? 413 : 400; }
return
ret;
});
}
inline
ssize_t write_headers(Stream& strm,
const
Headers& headers) {
ssize_t write_len = 0;
for
(
const
auto
& x : headers) {
auto
len =
strm.write_format(
"%s: %s\r\n"
, x.first.c_str(), x.second.c_str());
if
(len < 0) {
return
len; }
write_len += len;
}
auto
len = strm.write(
"\r\n"
);
if
(len < 0) {
return
len; }
write_len += len;
return
write_len;
}
inline
bool
write_data(Stream& strm,
const
char
* d,
size_t
l) {
size_t
offset = 0;
while
(offset < l) {
auto
length = strm.write(d + offset, l - offset);
if
(length < 0) {
return
false
; }
offset +=
static_cast
<
size_t
>(length);
}
return
true
;
}
template
<
typename
T>
inline
bool
write_content(Stream& strm,
const
ContentProvider& content_provider,
size_t
offset,
size_t
length, T is_shutting_down,
Error& error) {
size_t
end_offset = offset + length;
auto
ok =
true
;
DataSink data_sink;
data_sink.write = [&](
const
char
* d,
size_t
l) {
if
(ok) {
if
(write_data(strm, d, l)) {
offset += l;
}
else
{
ok =
false
;
}
}
};
data_sink.is_writable = [&](
void
) {
return
ok && strm.is_writable(); };
while
(offset < end_offset && !is_shutting_down()) {
if
(!content_provider(offset, end_offset - offset, data_sink)) {
error = Error::Canceled;
return
false
;
}
if
(!ok) {
error = Error::Write;
return
false
;
}
}
error = Error::Success;
return
true
;
}
template
<
typename
T>
inline
bool
write_content(Stream& strm,
const
ContentProvider& content_provider,
size_t
offset,
size_t
length,
const
T& is_shutting_down) {
auto
error = Error::Success;
return
write_content(strm, content_provider, offset, length, is_shutting_down,
error);
}
template
<
typename
T>
inline
bool
write_content_without_length(Stream& strm,
const
ContentProvider& content_provider,
const
T& is_shutting_down) {
size_t
offset = 0;
auto
data_available =
true
;
auto
ok =
true
;
DataSink data_sink;
data_sink.write = [&](
const
char
* d,
size_t
l) {
if
(ok) {
offset += l;
if
(!write_data(strm, d, l)) { ok =
false
; }
}
};
data_sink.done = [&](
void
) { data_available =
false
; };
data_sink.is_writable = [&](
void
) {
return
ok && strm.is_writable(); };
while
(data_available && !is_shutting_down()) {
if
(!content_provider(offset, 0, data_sink)) {
return
false
; }
if
(!ok) {
return
false
; }
}
return
true
;
}
template
<
typename
T,
typename
U>
inline
bool
write_content_chunked(Stream& strm,
const
ContentProvider& content_provider,
const
T& is_shutting_down, U& compressor, Error& error) {
size_t
offset = 0;
auto
data_available =
true
;
auto
ok =
true
;
DataSink data_sink;
data_sink.write = [&](
const
char
* d,
size_t
l) {
if
(!ok) {
return
; }
data_available = l > 0;
offset += l;
std::string payload;
if
(!compressor.compress(d, l,
false
,
[&](
const
char
* data,
size_t
data_len) {
payload.append(data, data_len);
return
true
;
})) {
ok =
false
;
return
;
}
if
(!payload.empty()) {
auto
chunk = from_i_to_hex(payload.size()) +
"\r\n"
+ payload +
"\r\n"
;
if
(!write_data(strm, chunk.data(), chunk.size())) {
ok =
false
;
return
;
}
}
};
data_sink.done = [&](
void
) {
if
(!ok) {
return
; }
data_available =
false
;
std::string payload;
if
(!compressor.compress(
nullptr
, 0,
true
,
[&](
const
char
* data,
size_t
data_len) {
payload.append(data, data_len);
return
true
;
})) {
ok =
false
;
return
;
}
if
(!payload.empty()) {
auto
chunk = from_i_to_hex(payload.size()) +
"\r\n"
+ payload +
"\r\n"
;
if
(!write_data(strm, chunk.data(), chunk.size())) {
ok =
false
;
return
;
}
}
static
const
std::string done_marker(
"0\r\n\r\n"
);
if
(!write_data(strm, done_marker.data(), done_marker.size())) {
ok =
false
;
}
};
data_sink.is_writable = [&](
void
) {
return
ok && strm.is_writable(); };
while
(data_available && !is_shutting_down()) {
if
(!content_provider(offset, 0, data_sink)) {
error = Error::Canceled;
return
false
;
}
if
(!ok) {
error = Error::Write;
return
false
;
}
}
error = Error::Success;
return
true
;
}
template
<
typename
T,
typename
U>
inline
bool
write_content_chunked(Stream& strm,
const
ContentProvider& content_provider,
const
T& is_shutting_down, U& compressor) {
auto
error = Error::Success;
return
write_content_chunked(strm, content_provider, is_shutting_down,
compressor, error);
}
template
<
typename
T>
inline
bool
redirect(T& cli, Request& req, Response& res,
const
std::string& path,
const
std::string& location,
Error& error) {
Request new_req = req;
new_req.path = path;
new_req.redirect_count_ -= 1;
if
(res.status == 303 && (req.method !=
"GET"
&& req.method !=
"HEAD"
)) {
new_req.method =
"GET"
;
new_req.body.clear();
new_req.headers.clear();
}
Response new_res;
auto
ret = cli.send(new_req, new_res, error);
if
(ret) {
req = new_req;
res = new_res;
res.location = location;
}
return
ret;
}
inline
std::string params_to_query_str(
const
Params& params) {
std::string query;
for
(
auto
it = params.begin(); it != params.end(); ++it) {
if
(it != params.begin()) { query +=
"&"
; }
query += it->first;
query +=
"="
;
query += encode_query_param(it->second);
}
return
query;
}
inline
std::string append_query_params(
const
char
* path,
const
Params& params) {
std::string path_with_query = path;
const
static
std::regex re(
"[^?]+\\?.*"
);
auto
delm = std::regex_match(path, re) ?
'&'
:
'?'
;
path_with_query += delm + params_to_query_str(params);
return
path_with_query;
}
inline
void
parse_query_text(
const
std::string& s, Params& params) {
split(s.data(), s.data() + s.size(),
'&'
, [&](
const
char
* b,
const
char
* e) {
std::string key;
std::string val;
split(b, e,
'='
, [&](
const
char
* b2,
const
char
* e2) {
if
(key.empty()) {
key.assign(b2, e2);
}
else
{
val.assign(b2, e2);
}
});
if
(!key.empty()) {
params.emplace(decode_url(key,
true
), decode_url(val,
true
));
}
});
}
inline
bool
parse_multipart_boundary(
const
std::string& content_type,
std::string& boundary) {
auto
pos = content_type.find(
"boundary="
);
if
(pos == std::string::npos) {
return
false
; }
boundary = content_type.substr(pos + 9);
if
(boundary.length() >= 2 && boundary.front() ==
'"'
&&
boundary.back() ==
'"'
) {
boundary = boundary.substr(1, boundary.size() - 2);
}
return
!boundary.empty();
}
inline
bool
parse_range_header(
const
std::string& s, Ranges& ranges)
try
{
static
auto
re_first_range = std::regex(R
"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"
);
std::smatch m;
if
(std::regex_match(s, m, re_first_range)) {
auto
pos =
static_cast
<
size_t
>(m.position(1));
auto
len =
static_cast
<
size_t
>(m.length(1));
bool
all_valid_ranges =
true
;
split(&s[pos], &s[pos + len],
','
, [&](
const
char
* b,
const
char
* e) {
if
(!all_valid_ranges)
return
;
static
auto
re_another_range = std::regex(R
"(\s*(\d*)-(\d*))"
);
std::cmatch cm;
if
(std::regex_match(b, e, cm, re_another_range)) {
ssize_t first = -1;
if
(!cm.str(1).empty()) {
first =
static_cast
<ssize_t>(std::stoll(cm.str(1)));
}
ssize_t last = -1;
if
(!cm.str(2).empty()) {
last =
static_cast
<ssize_t>(std::stoll(cm.str(2)));
}
if
(first != -1 && last != -1 && first > last) {
all_valid_ranges =
false
;
return
;
}
ranges.emplace_back(std::make_pair(first, last));
}
});
return
all_valid_ranges;
}
return
false
;
}
catch
(...) {
return
false
; }
class
MultipartFormDataParser {
public
:
MultipartFormDataParser() =
default
;
void
set_boundary(std::string&& boundary) { boundary_ = boundary; }
bool
is_valid()
const
{
return
is_valid_; }
bool
parse(
const
char
* buf,
size_t
n,
const
ContentReceiver& content_callback,
const
MultipartContentHeader& header_callback) {
static
const
std::regex re_content_disposition(
"^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
"\"(.*?)\")?\\s*$"
,
std::regex_constants::icase);
static
const
std::string dash_ =
"--"
;
static
const
std::string crlf_ =
"\r\n"
;
buf_.append(buf, n);
while
(!buf_.empty()) {
switch
(state_) {
case
0: {
auto
pattern = dash_ + boundary_ + crlf_;
if
(pattern.size() > buf_.size()) {
return
true
; }
auto
pos = buf_.find(pattern);
if
(pos != 0) {
return
false
; }
buf_.erase(0, pattern.size());
off_ += pattern.size();
state_ = 1;
break
;
}
case
1: {
clear_file_info();
state_ = 2;
break
;
}
case
2: {
auto
pos = buf_.find(crlf_);
while
(pos != std::string::npos) {
if
(pos == 0) {
if
(!header_callback(file_)) {
is_valid_ =
false
;
return
false
;
}
buf_.erase(0, crlf_.size());
off_ += crlf_.size();
state_ = 3;
break
;
}
static
const
std::string header_name =
"content-type:"
;
const
auto
header = buf_.substr(0, pos);
if
(start_with_case_ignore(header, header_name)) {
file_.content_type = trim_copy(header.substr(header_name.size()));
}
else
{
std::smatch m;
if
(std::regex_match(header, m, re_content_disposition)) {
file_.name = m[1];
file_.filename = m[2];
}
}
buf_.erase(0, pos + crlf_.size());
off_ += pos + crlf_.size();
pos = buf_.find(crlf_);
}
if
(state_ != 3) {
return
true
; }
break
;
}
case
3: {
{
auto
pattern = crlf_ + dash_;
if
(pattern.size() > buf_.size()) {
return
true
; }
auto
pos = find_string(buf_, pattern);
if
(!content_callback(buf_.data(), pos)) {
is_valid_ =
false
;
return
false
;
}
off_ += pos;
buf_.erase(0, pos);
}
{
auto
pattern = crlf_ + dash_ + boundary_;
if
(pattern.size() > buf_.size()) {
return
true
; }
auto
pos = buf_.find(pattern);
if
(pos != std::string::npos) {
if
(!content_callback(buf_.data(), pos)) {
is_valid_ =
false
;
return
false
;
}
off_ += pos + pattern.size();
buf_.erase(0, pos + pattern.size());
state_ = 4;
}
else
{
if
(!content_callback(buf_.data(), pattern.size())) {
is_valid_ =
false
;
return
false
;
}
off_ += pattern.size();
buf_.erase(0, pattern.size());
}
}
break
;
}
case
4: {
if
(crlf_.size() > buf_.size()) {
return
true
; }
if
(buf_.compare(0, crlf_.size(), crlf_) == 0) {
buf_.erase(0, crlf_.size());
off_ += crlf_.size();
state_ = 1;
}
else
{
auto
pattern = dash_ + crlf_;
if
(pattern.size() > buf_.size()) {
return
true
; }
if
(buf_.compare(0, pattern.size(), pattern) == 0) {
buf_.erase(0, pattern.size());
off_ += pattern.size();
is_valid_ =
true
;
state_ = 5;
}
else
{
return
true
;
}
}
break
;
}
case
5: {
is_valid_ =
false
;
return
false
;
}
}
}
return
true
;
}
private
:
void
clear_file_info() {
file_.name.clear();
file_.filename.clear();
file_.content_type.clear();
}
bool
start_with_case_ignore(
const
std::string& a,
const
std::string& b)
const
{
if
(a.size() < b.size()) {
return
false
; }
for
(
size_t
i = 0; i < b.size(); i++) {
if
(::
tolower
(a[i]) != ::
tolower
(b[i])) {
return
false
; }
}
return
true
;
}
bool
start_with(
const
std::string& a,
size_t
off,
const
std::string& b)
const
{
if
(a.size() - off < b.size()) {
return
false
; }
for
(
size_t
i = 0; i < b.size(); i++) {
if
(a[i + off] != b[i]) {
return
false
; }
}
return
true
;
}
size_t
find_string(
const
std::string& s,
const
std::string& pattern)
const
{
auto
c = pattern.front();
size_t
off = 0;
while
(off < s.size()) {
auto
pos = s.find(c, off);
if
(pos == std::string::npos) {
return
s.size(); }
auto
rem = s.size() - pos;
if
(pattern.size() > rem) {
return
pos; }
if
(start_with(s, pos, pattern)) {
return
pos; }
off = pos + 1;
}
return
s.size();
}
std::string boundary_;
std::string buf_;
size_t
state_ = 0;
bool
is_valid_ =
false
;
size_t
off_ = 0;
MultipartFormData file_;
};
inline
std::string to_lower(
const
char
* beg,
const
char
* end) {
std::string out;
auto
it = beg;
while
(it != end) {
out +=
static_cast
<
char
>(::
tolower
(*it));
it++;
}
return
out;
}
inline
std::string make_multipart_data_boundary() {
static
const
char
data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
;
std::random_device seed_gen;
std::seed_seq seed_sequence{ seed_gen(), seed_gen(), seed_gen(), seed_gen() };
std::mt19937 engine(seed_sequence);
std::string result =
"--cpp-httplib-multipart-data-"
;
for
(
auto
i = 0; i < 16; i++) {
result += data[engine() % (
sizeof
(data) - 1)];
}
return
result;
}
inline
std::pair<
size_t
,
size_t
>
get_range_offset_and_length(
const
Request& req,
size_t
content_length,
size_t
index) {
auto
r = req.ranges[index];
if
(r.first == -1 && r.second == -1) {
return
std::make_pair(0, content_length);
}
auto
slen =
static_cast
<ssize_t>(content_length);
if
(r.first == -1) {
r.first = (std::max)(
static_cast
<ssize_t>(0), slen - r.second);
r.second = slen - 1;
}
if
(r.second == -1) { r.second = slen - 1; }
return
std::make_pair(r.first,
static_cast
<
size_t
>(r.second - r.first) + 1);
}
inline
std::string make_content_range_header_field(
size_t
offset,
size_t
length,
size_t
content_length) {
std::string field =
"bytes "
;
field += std::to_string(offset);
field +=
"-"
;
field += std::to_string(offset + length - 1);
field +=
"/"
;
field += std::to_string(content_length);
return
field;
}
template
<
typename
SToken,
typename
CToken,
typename
Content>
bool
process_multipart_ranges_data(
const
Request& req, Response& res,
const
std::string& boundary,
const
std::string& content_type,
SToken stoken, CToken ctoken,
Content content) {
for
(
size_t
i = 0; i < req.ranges.size(); i++) {
ctoken(
"--"
);
stoken(boundary);
ctoken(
"\r\n"
);
if
(!content_type.empty()) {
ctoken(
"Content-Type: "
);
stoken(content_type);
ctoken(
"\r\n"
);
}
auto
offsets = get_range_offset_and_length(req, res.body.size(), i);
auto
offset = offsets.first;
auto
length = offsets.second;
ctoken(
"Content-Range: "
);
stoken(make_content_range_header_field(offset, length, res.body.size()));
ctoken(
"\r\n"
);
ctoken(
"\r\n"
);
if
(!content(offset, length)) {
return
false
; }
ctoken(
"\r\n"
);
}
ctoken(
"--"
);
stoken(boundary);
ctoken(
"--\r\n"
);
return
true
;
}
inline
bool
make_multipart_ranges_data(
const
Request& req, Response& res,
const
std::string& boundary,
const
std::string& content_type,
std::string& data) {
return
process_multipart_ranges_data(
req, res, boundary, content_type,
[&](
const
std::string& token) { data += token; },
[&](
const
char
* token) { data += token; },
[&](
size_t
offset,
size_t
length) {
if
(offset < res.body.size()) {
data += res.body.substr(offset, length);
return
true
;
}
return
false
;
});
}
inline
size_t
get_multipart_ranges_data_length(
const
Request& req, Response& res,
const
std::string& boundary,
const
std::string& content_type) {
size_t
data_length = 0;
process_multipart_ranges_data(
req, res, boundary, content_type,
[&](
const
std::string& token) { data_length += token.size(); },
[&](
const
char
* token) { data_length +=
strlen
(token); },
[&](
size_t
,
size_t
length) {
data_length += length;
return
true
;
});
return
data_length;
}
template
<
typename
T>
inline
bool
write_multipart_ranges_data(Stream& strm,
const
Request& req,
Response& res,
const
std::string& boundary,
const
std::string& content_type,
const
T& is_shutting_down) {
return
process_multipart_ranges_data(
req, res, boundary, content_type,
[&](
const
std::string& token) { strm.write(token); },
[&](
const
char
* token) { strm.write(token); },
[&](
size_t
offset,
size_t
length) {
return
write_content(strm, res.content_provider_, offset, length,
is_shutting_down);
});
}
inline
std::pair<
size_t
,
size_t
>
get_range_offset_and_length(
const
Request& req,
const
Response& res,
size_t
index) {
auto
r = req.ranges[index];
if
(r.second == -1) {
r.second =
static_cast
<ssize_t>(res.content_length_) - 1;
}
return
std::make_pair(r.first, r.second - r.first + 1);
}
inline
bool
expect_content(
const
Request& req) {
if
(req.method ==
"POST"
|| req.method ==
"PUT"
|| req.method ==
"PATCH"
||
req.method ==
"PRI"
|| req.method ==
"DELETE"
) {
return
true
;
}
return
false
;
}
inline
bool
has_crlf(
const
char
* s) {
auto
p = s;
while
(*p) {
if
(*p ==
'\r'
|| *p ==
'\n'
) {
return
true
; }
p++;
}
return
false
;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
template
<
typename
CTX,
typename
Init,
typename
Update,
typename
Final>
inline
std::string message_digest(
const
std::string& s, Init init,
Update update, Final final,
size_t
digest_length) {
using
namespace
std;
std::vector<unsigned
char
> md(digest_length, 0);
CTX ctx;
init(&ctx);
update(&ctx, s.data(), s.size());
final(md.data(), &ctx);
stringstream ss;
for
(
auto
c : md) {
ss << setfill(
'0'
) << setw(2) << hex << (unsigned
int
)c;
}
return
ss.str();
}
inline
std::string MD5(
const
std::string& s) {
return
message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final,
MD5_DIGEST_LENGTH);
}
inline
std::string SHA_256(
const
std::string& s) {
return
message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final,
SHA256_DIGEST_LENGTH);
}
inline
std::string SHA_512(
const
std::string& s) {
return
message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final,
SHA512_DIGEST_LENGTH);
}
#endif
#ifdef _WIN32
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
bool
load_system_certs_on_windows(X509_STORE* store) {
auto
hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L
"ROOT"
);
if
(!hStore) {
return
false
; }
PCCERT_CONTEXT pContext = NULL;
while
((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=
nullptr
) {
auto
encoded_cert =
static_cast
<
const
unsigned
char
*>(pContext->pbCertEncoded);
auto
x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
if
(x509) {
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
}
CertFreeCertificateContext(pContext);
CertCloseStore(hStore, 0);
return
true
;
}
#endif
class
WSInit {
public
:
WSInit() {
WSADATA wsaData;
WSAStartup(0x0002, &wsaData);
}
~WSInit() { WSACleanup(); }
};
static
WSInit wsinit_;
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
std::pair<std::string, std::string> make_digest_authentication_header(
const
Request& req,
const
std::map<std::string, std::string>& auth,
size_t
cnonce_count,
const
std::string& cnonce,
const
std::string& username,
const
std::string& password,
bool
is_proxy =
false
) {
using
namespace
std;
string nc;
{
stringstream ss;
ss << setfill(
'0'
) << setw(8) << hex << cnonce_count;
nc = ss.str();
}
auto
qop = auth.at(
"qop"
);
if
(qop.find(
"auth-int"
) != std::string::npos) {
qop =
"auth-int"
;
}
else
{
qop =
"auth"
;
}
std::string algo =
"MD5"
;
if
(auth.find(
"algorithm"
) != auth.end()) { algo = auth.at(
"algorithm"
); }
string response;
{
auto
H = algo ==
"SHA-256"
? detail::SHA_256
: algo ==
"SHA-512"
? detail::SHA_512 : detail::MD5;
auto
A1 = username +
":"
+ auth.at(
"realm"
) +
":"
+ password;
auto
A2 = req.method +
":"
+ req.path;
if
(qop ==
"auth-int"
) { A2 +=
":"
+ H(req.body); }
response = H(H(A1) +
":"
+ auth.at(
"nonce"
) +
":"
+ nc +
":"
+ cnonce +
":"
+ qop +
":"
+ H(A2));
}
auto
field =
"Digest username=\""
+ username +
"\", realm=\""
+
auth.at(
"realm"
) +
"\", nonce=\""
+ auth.at(
"nonce"
) +
"\", uri=\""
+ req.path +
"\", algorithm="
+ algo +
", qop="
+ qop +
", nc=\""
+ nc +
"\", cnonce=\""
+ cnonce +
"\", response=\""
+ response +
"\""
;
auto
key = is_proxy ?
"Proxy-Authorization"
:
"Authorization"
;
return
std::make_pair(key, field);
}
#endif
inline
bool
parse_www_authenticate(
const
Response& res,
std::map<std::string, std::string>& auth,
bool
is_proxy) {
auto
auth_key = is_proxy ?
"Proxy-Authenticate"
:
"WWW-Authenticate"
;
if
(res.has_header(auth_key)) {
static
auto
re = std::regex(R
"~((?:(?:,\s*)?(.+?)=(?:"
(.*?)
"|([^,]*))))~"
);
auto
s = res.get_header_value(auth_key);
auto
pos = s.find(
' '
);
if
(pos != std::string::npos) {
auto
type = s.substr(0, pos);
if
(type ==
"Basic"
) {
return
false
;
}
else
if
(type ==
"Digest"
) {
s = s.substr(pos + 1);
auto
beg = std::sregex_iterator(s.begin(), s.end(), re);
for
(
auto
i = beg; i != std::sregex_iterator(); ++i) {
auto
m = *i;
auto
key = s.substr(
static_cast
<
size_t
>(m.position(1)),
static_cast
<
size_t
>(m.length(1)));
auto
val = m.length(2) > 0
? s.substr(
static_cast
<
size_t
>(m.position(2)),
static_cast
<
size_t
>(m.length(2)))
: s.substr(
static_cast
<
size_t
>(m.position(3)),
static_cast
<
size_t
>(m.length(3)));
auth[key] = val;
}
return
true
;
}
}
}
return
false
;
}
inline
std::string random_string(
size_t
length) {
auto
randchar = []() ->
char
{
const
char
charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
;
const
size_t
max_index = (
sizeof
(charset) - 1);
return
charset[
static_cast
<
size_t
>(
rand
()) % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return
str;
}
class
ContentProviderAdapter {
public
:
explicit
ContentProviderAdapter(
ContentProviderWithoutLength&& content_provider)
: content_provider_(content_provider) {}
bool
operator()(
size_t
offset,
size_t
, DataSink& sink) {
return
content_provider_(offset, sink);
}
private
:
ContentProviderWithoutLength content_provider_;
};
}
inline
std::pair<std::string, std::string> make_range_header(Ranges ranges) {
std::string field =
"bytes="
;
auto
i = 0;
for
(
auto
r : ranges) {
if
(i != 0) { field +=
", "
; }
if
(r.first != -1) { field += std::to_string(r.first); }
field +=
'-'
;
if
(r.second != -1) { field += std::to_string(r.second); }
i++;
}
return
std::make_pair(
"Range"
, std::move(field));
}
inline
std::pair<std::string, std::string>
make_basic_authentication_header(
const
std::string& username,
const
std::string& password,
bool
is_proxy =
false
) {
auto
field =
"Basic "
+ detail::base64_encode(username +
":"
+ password);
auto
key = is_proxy ?
"Proxy-Authorization"
:
"Authorization"
;
return
std::make_pair(key, std::move(field));
}
inline
std::pair<std::string, std::string>
make_bearer_token_authentication_header(
const
std::string& token,
bool
is_proxy =
false
) {
auto
field =
"Bearer "
+ token;
auto
key = is_proxy ?
"Proxy-Authorization"
:
"Authorization"
;
return
std::make_pair(key, std::move(field));
}
inline
bool
Request::has_header(
const
char
* key)
const
{
return
detail::has_header(headers, key);
}
inline
std::string Request::get_header_value(
const
char
* key,
size_t
id)
const
{
return
detail::get_header_value(headers, key, id,
""
);
}
template
<
typename
T>
inline
T Request::get_header_value(
const
char
* key,
size_t
id)
const
{
return
detail::get_header_value<T>(headers, key, id, 0);
}
inline
size_t
Request::get_header_value_count(
const
char
* key)
const
{
auto
r = headers.equal_range(key);
return
static_cast
<
size_t
>(std::distance(r.first, r.second));
}
inline
void
Request::set_header(
const
char
* key,
const
char
* val) {
if
(!detail::has_crlf(key) && !detail::has_crlf(val)) {
headers.emplace(key, val);
}
}
inline
void
Request::set_header(
const
char
* key,
const
std::string& val) {
if
(!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
headers.emplace(key, val);
}
}
inline
bool
Request::has_param(
const
char
* key)
const
{
return
params.find(key) != params.end();
}
inline
std::string Request::get_param_value(
const
char
* key,
size_t
id)
const
{
auto
rng = params.equal_range(key);
auto
it = rng.first;
std::advance(it,
static_cast
<ssize_t>(id));
if
(it != rng.second) {
return
it->second; }
return
std::string();
}
inline
size_t
Request::get_param_value_count(
const
char
* key)
const
{
auto
r = params.equal_range(key);
return
static_cast
<
size_t
>(std::distance(r.first, r.second));
}
inline
bool
Request::is_multipart_form_data()
const
{
const
auto
& content_type = get_header_value(
"Content-Type"
);
return
!content_type.find(
"multipart/form-data"
);
}
inline
bool
Request::has_file(
const
char
* key)
const
{
return
files.find(key) != files.end();
}
inline
MultipartFormData Request::get_file_value(
const
char
* key)
const
{
auto
it = files.find(key);
if
(it != files.end()) {
return
it->second; }
return
MultipartFormData();
}
inline
bool
Response::has_header(
const
char
* key)
const
{
return
headers.find(key) != headers.end();
}
inline
std::string Response::get_header_value(
const
char
* key,
size_t
id)
const
{
return
detail::get_header_value(headers, key, id,
""
);
}
template
<
typename
T>
inline
T Response::get_header_value(
const
char
* key,
size_t
id)
const
{
return
detail::get_header_value<T>(headers, key, id, 0);
}
inline
size_t
Response::get_header_value_count(
const
char
* key)
const
{
auto
r = headers.equal_range(key);
return
static_cast
<
size_t
>(std::distance(r.first, r.second));
}
inline
void
Response::set_header(
const
char
* key,
const
char
* val) {
if
(!detail::has_crlf(key) && !detail::has_crlf(val)) {
headers.emplace(key, val);
}
}
inline
void
Response::set_header(
const
char
* key,
const
std::string& val) {
if
(!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
headers.emplace(key, val);
}
}
inline
void
Response::set_redirect(
const
char
* url,
int
stat) {
if
(!detail::has_crlf(url)) {
set_header(
"Location"
, url);
if
(300 <= stat && stat < 400) {
this
->status = stat;
}
else
{
this
->status = 302;
}
}
}
inline
void
Response::set_redirect(
const
std::string& url,
int
stat) {
set_redirect(url.c_str(), stat);
}
inline
void
Response::set_content(
const
char
* s,
size_t
n,
const
char
* content_type) {
body.assign(s, n);
auto
rng = headers.equal_range(
"Content-Type"
);
headers.erase(rng.first, rng.second);
set_header(
"Content-Type"
, content_type);
}
inline
void
Response::set_content(
const
std::string& s,
const
char
* content_type) {
set_content(s.data(), s.size(), content_type);
}
inline
void
Response::set_content_provider(
size_t
in_length,
const
char
* content_type,
ContentProvider provider,
const
std::function<
void
()>& resource_releaser) {
assert
(in_length > 0);
set_header(
"Content-Type"
, content_type);
content_length_ = in_length;
content_provider_ = std::move(provider);
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider_ =
false
;
}
inline
void
Response::set_content_provider(
const
char
* content_type,
ContentProviderWithoutLength provider,
const
std::function<
void
()>& resource_releaser) {
set_header(
"Content-Type"
, content_type);
content_length_ = 0;
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider_ =
false
;
}
inline
void
Response::set_chunked_content_provider(
const
char
* content_type, ContentProviderWithoutLength provider,
const
std::function<
void
()>& resource_releaser) {
set_header(
"Content-Type"
, content_type);
content_length_ = 0;
content_provider_ = detail::ContentProviderAdapter(std::move(provider));
content_provider_resource_releaser_ = resource_releaser;
is_chunked_content_provider_ =
true
;
}
inline
bool
Result::has_request_header(
const
char
* key)
const
{
return
request_headers_.find(key) != request_headers_.end();
}
inline
std::string Result::get_request_header_value(
const
char
* key,
size_t
id)
const
{
return
detail::get_header_value(request_headers_, key, id,
""
);
}
template
<
typename
T>
inline
T Result::get_request_header_value(
const
char
* key,
size_t
id)
const
{
return
detail::get_header_value<T>(request_headers_, key, id, 0);
}
inline
size_t
Result::get_request_header_value_count(
const
char
* key)
const
{
auto
r = request_headers_.equal_range(key);
return
static_cast
<
size_t
>(std::distance(r.first, r.second));
}
inline
ssize_t Stream::write(
const
char
* ptr) {
return
write(ptr,
strlen
(ptr));
}
inline
ssize_t Stream::write(
const
std::string& s) {
return
write(s.data(), s.size());
}
template
<
typename
... Args>
inline
ssize_t Stream::write_format(
const
char
* fmt,
const
Args &... args) {
const
auto
bufsiz = 2048;
std::array<
char
, bufsiz> buf;
#if defined(_MSC_VER) && _MSC_VER < 1900
auto
sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...);
#else
auto
sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
#endif
if
(sn <= 0) {
return
sn; }
auto
n =
static_cast
<
size_t
>(sn);
if
(n >= buf.size() - 1) {
std::vector<
char
> glowable_buf(buf.size());
while
(n >= glowable_buf.size() - 1) {
glowable_buf.resize(glowable_buf.size() * 2);
#if defined(_MSC_VER) && _MSC_VER < 1900
n =
static_cast
<
size_t
>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
glowable_buf.size() - 1, fmt,
args...));
#else
n =
static_cast
<
size_t
>(
snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
#endif
}
return
write(&glowable_buf[0], n);
}
else
{
return
write(buf.data(), n);
}
}
namespace
detail {
inline
SocketStream::SocketStream(socket_t sock,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec)
: sock_(sock), read_timeout_sec_(read_timeout_sec),
read_timeout_usec_(read_timeout_usec),
write_timeout_sec_(write_timeout_sec),
write_timeout_usec_(write_timeout_usec) {}
inline
SocketStream::~SocketStream() {}
inline
bool
SocketStream::is_readable()
const
{
return
select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
}
inline
bool
SocketStream::is_writable()
const
{
return
select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
}
inline
ssize_t SocketStream::read(
char
* ptr,
size_t
size) {
if
(!is_readable()) {
return
-1; }
#ifdef _WIN32
if
(size >
static_cast
<
size_t
>((std::numeric_limits<
int
>::max)())) {
return
-1;
}
return
recv(sock_, ptr,
static_cast
<
int
>(size), CPPHTTPLIB_RECV_FLAGS);
#else
return
handle_EINTR(
[&]() {
return
recv(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS); });
#endif
}
inline
ssize_t SocketStream::write(
const
char
* ptr,
size_t
size) {
if
(!is_writable()) {
return
-1; }
#ifdef _WIN32
if
(size >
static_cast
<
size_t
>((std::numeric_limits<
int
>::max)())) {
return
-1;
}
return
send(sock_, ptr,
static_cast
<
int
>(size), CPPHTTPLIB_SEND_FLAGS);
#else
return
handle_EINTR(
[&]() {
return
send(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS); });
#endif
}
inline
void
SocketStream::get_remote_ip_and_port(std::string& ip,
int
& port)
const
{
return
detail::get_remote_ip_and_port(sock_, ip, port);
}
inline
socket_t SocketStream::socket()
const
{
return
sock_; }
inline
bool
BufferStream::is_readable()
const
{
return
true
; }
inline
bool
BufferStream::is_writable()
const
{
return
true
; }
inline
ssize_t BufferStream::read(
char
* ptr,
size_t
size) {
#if defined(_MSC_VER) && _MSC_VER <= 1900
auto
len_read = buffer._Copy_s(ptr, size, size, position);
#else
auto
len_read = buffer.copy(ptr, size, position);
#endif
position +=
static_cast
<
size_t
>(len_read);
return
static_cast
<ssize_t>(len_read);
}
inline
ssize_t BufferStream::write(
const
char
* ptr,
size_t
size) {
buffer.append(ptr, size);
return
static_cast
<ssize_t>(size);
}
inline
void
BufferStream::get_remote_ip_and_port(std::string&
,
int
&
)
const
{}
inline
socket_t BufferStream::socket()
const
{
return
0; }
inline
const
std::string& BufferStream::get_buffer()
const
{
return
buffer; }
}
inline
Server::Server()
: new_task_queue(
[] {
return
new
ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
svr_sock_(INVALID_SOCKET), is_running_(
false
) {
#ifndef _WIN32
signal
(SIGPIPE, SIG_IGN);
#endif
}
inline
Server::~Server() {}
inline
Server& Server::Get(
const
char
* pattern, Handler handler) {
return
Get(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Get(
const
char
* pattern,
size_t
pattern_len,
Handler handler) {
get_handlers_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Post(
const
char
* pattern, Handler handler) {
return
Post(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Post(
const
char
* pattern,
size_t
pattern_len,
Handler handler) {
post_handlers_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Post(
const
char
* pattern,
HandlerWithContentReader handler) {
return
Post(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Post(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler) {
post_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Put(
const
char
* pattern, Handler handler) {
return
Put(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Put(
const
char
* pattern,
size_t
pattern_len,
Handler handler) {
put_handlers_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Put(
const
char
* pattern,
HandlerWithContentReader handler) {
return
Put(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Put(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler) {
put_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Patch(
const
char
* pattern, Handler handler) {
return
Patch(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Patch(
const
char
* pattern,
size_t
pattern_len,
Handler handler) {
patch_handlers_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Patch(
const
char
* pattern,
HandlerWithContentReader handler) {
return
Patch(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Patch(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler) {
patch_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Delete(
const
char
* pattern, Handler handler) {
return
Delete(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Delete(
const
char
* pattern,
size_t
pattern_len,
Handler handler) {
delete_handlers_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Delete(
const
char
* pattern,
HandlerWithContentReader handler) {
return
Delete(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Delete(
const
char
* pattern,
size_t
pattern_len,
HandlerWithContentReader handler) {
delete_handlers_for_content_reader_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
Server& Server::Options(
const
char
* pattern, Handler handler) {
return
Options(pattern,
strlen
(pattern), handler);
}
inline
Server& Server::Options(
const
char
* pattern,
size_t
pattern_len,
Handler handler) {
options_handlers_.push_back(
std::make_pair(std::regex(pattern, pattern_len), std::move(handler)));
return
*
this
;
}
inline
bool
Server::set_base_dir(
const
char
* dir,
const
char
* mount_point) {
return
set_mount_point(mount_point, dir);
}
inline
bool
Server::set_mount_point(
const
char
* mount_point,
const
char
* dir,
Headers headers) {
if
(detail::is_dir(dir)) {
std::string mnt = mount_point ? mount_point :
"/"
;
if
(!mnt.empty() && mnt[0] ==
'/'
) {
base_dirs_.push_back({ mnt, dir, std::move(headers) });
return
true
;
}
}
return
false
;
}
inline
bool
Server::remove_mount_point(
const
char
* mount_point) {
for
(
auto
it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
if
(it->mount_point == mount_point) {
base_dirs_.erase(it);
return
true
;
}
}
return
false
;
}
inline
Server&
Server::set_file_extension_and_mimetype_mapping(
const
char
* ext,
const
char
* mime) {
file_extension_and_mimetype_map_[ext] = mime;
return
*
this
;
}
inline
Server& Server::set_file_request_handler(Handler handler) {
file_request_handler_ = std::move(handler);
return
*
this
;
}
inline
Server& Server::set_error_handler(HandlerWithResponse handler) {
error_handler_ = std::move(handler);
return
*
this
;
}
inline
Server& Server::set_error_handler(Handler handler) {
error_handler_ = [handler](
const
Request& req, Response& res) {
handler(req, res);
return
HandlerResponse::Handled;
};
return
*
this
;
}
inline
Server& Server::set_exception_handler(ExceptionHandler handler) {
exception_handler_ = std::move(handler);
return
*
this
;
}
inline
Server& Server::set_pre_routing_handler(HandlerWithResponse handler) {
pre_routing_handler_ = std::move(handler);
return
*
this
;
}
inline
Server& Server::set_post_routing_handler(Handler handler) {
post_routing_handler_ = std::move(handler);
return
*
this
;
}
inline
Server& Server::set_logger(Logger logger) {
logger_ = std::move(logger);
return
*
this
;
}
inline
Server&
Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) {
expect_100_continue_handler_ = std::move(handler);
return
*
this
;
}
inline
Server& Server::set_tcp_nodelay(
bool
on) {
tcp_nodelay_ = on;
return
*
this
;
}
inline
Server& Server::set_socket_options(SocketOptions socket_options) {
socket_options_ = std::move(socket_options);
return
*
this
;
}
inline
Server& Server::set_keep_alive_max_count(
size_t
count) {
keep_alive_max_count_ = count;
return
*
this
;
}
inline
Server& Server::set_keep_alive_timeout(
time_t
sec) {
keep_alive_timeout_sec_ = sec;
return
*
this
;
}
inline
Server& Server::set_read_timeout(
time_t
sec,
time_t
usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
return
*
this
;
}
inline
Server& Server::set_write_timeout(
time_t
sec,
time_t
usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
return
*
this
;
}
inline
Server& Server::set_idle_interval(
time_t
sec,
time_t
usec) {
idle_interval_sec_ = sec;
idle_interval_usec_ = usec;
return
*
this
;
}
inline
Server& Server::set_payload_max_length(
size_t
length) {
payload_max_length_ = length;
return
*
this
;
}
inline
bool
Server::bind_to_port(
const
char
* host,
int
port,
int
socket_flags) {
if
(bind_internal(host, port, socket_flags) < 0)
return
false
;
return
true
;
}
inline
int
Server::bind_to_any_port(
const
char
* host,
int
socket_flags) {
return
bind_internal(host, 0, socket_flags);
}
inline
bool
Server::listen_after_bind() {
return
listen_internal(); }
inline
bool
Server::listen(
const
char
* host,
int
port,
int
socket_flags) {
return
bind_to_port(host, port, socket_flags) && listen_internal();
}
inline
bool
Server::is_running()
const
{
return
is_running_; }
inline
void
Server::stop() {
if
(is_running_) {
assert
(svr_sock_ != INVALID_SOCKET);
std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));
detail::shutdown_socket(sock);
detail::close_socket(sock);
}
}
inline
bool
Server::parse_request_line(
const
char
* s, Request& req) {
const
static
std::regex re(
"(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) "
"(([^? ]+)(?:\\?([^ ]*?))?) (HTTP/1\\.[01])\r\n"
);
std::cmatch m;
if
(std::regex_match(s, m, re)) {
req.version = std::string(m[5]);
req.method = std::string(m[1]);
req.target = std::string(m[2]);
req.path = detail::decode_url(m[3],
false
);
auto
len = std::distance(m[4].first, m[4].second);
if
(len > 0) { detail::parse_query_text(m[4], req.params); }
return
true
;
}
return
false
;
}
inline
bool
Server::write_response(Stream& strm,
bool
close_connection,
const
Request& req, Response& res) {
return
write_response_core(strm, close_connection, req, res,
false
);
}
inline
bool
Server::write_response_with_content(Stream& strm,
bool
close_connection,
const
Request& req,
Response& res) {
return
write_response_core(strm, close_connection, req, res,
true
);
}
inline
bool
Server::write_response_core(Stream& strm,
bool
close_connection,
const
Request& req, Response& res,
bool
need_apply_ranges) {
assert
(res.status != -1);
if
(400 <= res.status && error_handler_ &&
error_handler_(req, res) == HandlerResponse::Handled) {
need_apply_ranges =
true
;
}
std::string content_type;
std::string boundary;
if
(need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }
if
(close_connection || req.get_header_value(
"Connection"
) ==
"close"
) {
res.set_header(
"Connection"
,
"close"
);
}
else
{
std::stringstream ss;
ss <<
"timeout="
<< keep_alive_timeout_sec_
<<
", max="
<< keep_alive_max_count_;
res.set_header(
"Keep-Alive"
, ss.str());
}
if
(!res.has_header(
"Content-Type"
) &&
(!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
res.set_header(
"Content-Type"
,
"text/plain"
);
}
if
(!res.has_header(
"Content-Length"
) && res.body.empty() &&
!res.content_length_ && !res.content_provider_) {
res.set_header(
"Content-Length"
,
"0"
);
}
if
(!res.has_header(
"Accept-Ranges"
) && req.method ==
"HEAD"
) {
res.set_header(
"Accept-Ranges"
,
"bytes"
);
}
if
(post_routing_handler_) { post_routing_handler_(req, res); }
{
detail::BufferStream bstrm;
if
(!bstrm.write_format(
"HTTP/1.1 %d %s\r\n"
, res.status,
detail::status_message(res.status))) {
return
false
;
}
if
(!detail::write_headers(bstrm, res.headers)) {
return
false
; }
auto
& data = bstrm.get_buffer();
strm.write(data.data(), data.size());
}
auto
ret =
true
;
if
(req.method !=
"HEAD"
) {
if
(!res.body.empty()) {
if
(!strm.write(res.body)) { ret =
false
; }
}
else
if
(res.content_provider_) {
if
(!write_content_with_provider(strm, req, res, boundary,
content_type)) {
ret =
false
;
}
}
}
if
(logger_) { logger_(req, res); }
return
ret;
}
inline
bool
Server::write_content_with_provider(Stream& strm,
const
Request& req,
Response& res,
const
std::string& boundary,
const
std::string& content_type) {
auto
is_shutting_down = [
this
]() {
return
this
->svr_sock_ == INVALID_SOCKET;
};
if
(res.content_length_ > 0) {
if
(req.ranges.empty()) {
return
detail::write_content(strm, res.content_provider_, 0,
res.content_length_, is_shutting_down);
}
else
if
(req.ranges.size() == 1) {
auto
offsets =
detail::get_range_offset_and_length(req, res.content_length_, 0);
auto
offset = offsets.first;
auto
length = offsets.second;
return
detail::write_content(strm, res.content_provider_, offset, length,
is_shutting_down);
}
else
{
return
detail::write_multipart_ranges_data(
strm, req, res, boundary, content_type, is_shutting_down);
}
}
else
{
if
(res.is_chunked_content_provider_) {
auto
type = detail::encoding_type(req, res);
std::unique_ptr<detail::compressor> compressor;
if
(type == detail::EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
compressor = detail::make_unique<detail::gzip_compressor>();
#endif
}
else
if
(type == detail::EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = detail::make_unique<detail::brotli_compressor>();
#endif
}
else
{
compressor = detail::make_unique<detail::nocompressor>();
}
assert
(compressor !=
nullptr
);
return
detail::write_content_chunked(strm, res.content_provider_,
is_shutting_down, *compressor);
}
else
{
return
detail::write_content_without_length(strm, res.content_provider_,
is_shutting_down);
}
}
}
inline
bool
Server::read_content(Stream& strm, Request& req, Response& res) {
MultipartFormDataMap::iterator cur;
if
(read_content_core(
strm, req, res,
[&](
const
char
* buf,
size_t
n) {
if
(req.body.size() + n > req.body.max_size()) {
return
false
; }
req.body.append(buf, n);
return
true
;
},
[&](
const
MultipartFormData& file) {
cur = req.files.emplace(file.name, file);
return
true
;
},
[&](
const
char
* buf,
size_t
n) {
auto
& content = cur->second.content;
if
(content.size() + n > content.max_size()) {
return
false
; }
content.append(buf, n);
return
true
;
})) {
const
auto
& content_type = req.get_header_value(
"Content-Type"
);
if
(!content_type.find(
"application/x-www-form-urlencoded"
)) {
detail::parse_query_text(req.body, req.params);
}
return
true
;
}
return
false
;
}
inline
bool
Server::read_content_with_content_receiver(
Stream& strm, Request& req, Response& res, ContentReceiver receiver,
MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) {
return
read_content_core(strm, req, res, std::move(receiver),
std::move(multipart_header),
std::move(multipart_receiver));
}
inline
bool
Server::read_content_core(Stream& strm, Request& req, Response& res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
ContentReceiver multipart_receiver) {
detail::MultipartFormDataParser multipart_form_data_parser;
ContentReceiverWithProgress out;
if
(req.is_multipart_form_data()) {
const
auto
& content_type = req.get_header_value(
"Content-Type"
);
std::string boundary;
if
(!detail::parse_multipart_boundary(content_type, boundary)) {
res.status = 400;
return
false
;
}
multipart_form_data_parser.set_boundary(std::move(boundary));
out = [&](
const
char
* buf,
size_t
n, uint64_t
, uint64_t
) {
return
multipart_form_data_parser.parse(buf, n, multipart_receiver,
mulitpart_header);
};
}
else
{
out = [receiver](
const
char
* buf,
size_t
n, uint64_t
,
uint64_t
) {
return
receiver(buf, n); };
}
if
(req.method ==
"DELETE"
&& !req.has_header(
"Content-Length"
)) {
return
true
;
}
if
(!detail::read_content(strm, req, payload_max_length_, res.status,
nullptr
,
out,
true
)) {
return
false
;
}
if
(req.is_multipart_form_data()) {
if
(!multipart_form_data_parser.is_valid()) {
res.status = 400;
return
false
;
}
}
return
true
;
}
inline
bool
Server::handle_file_request(
const
Request& req, Response& res,
bool
head) {
for
(
const
auto
& entry : base_dirs_) {
if
(!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
std::string sub_path =
"/"
+ req.path.substr(entry.mount_point.size());
if
(detail::is_valid_path(sub_path)) {
auto
path = entry.base_dir + sub_path;
if
(path.back() ==
'/'
) { path +=
"index.html"
; }
if
(detail::is_file(path)) {
detail::read_file(path, res.body);
auto
type =
detail::find_content_type(path, file_extension_and_mimetype_map_);
if
(type) { res.set_header(
"Content-Type"
, type); }
for
(
const
auto
& kv : entry.headers) {
res.set_header(kv.first.c_str(), kv.second);
}
res.status = req.has_header(
"Range"
) ? 206 : 200;
if
(!head && file_request_handler_) {
file_request_handler_(req, res);
}
return
true
;
}
}
}
}
return
false
;
}
inline
socket_t
Server::create_server_socket(
const
char
* host,
int
port,
int
socket_flags,
SocketOptions socket_options)
const
{
return
detail::create_socket(
host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
[](socket_t sock,
struct
addrinfo& ai) ->
bool
{
if
(::bind(sock, ai.ai_addr,
static_cast
<socklen_t>(ai.ai_addrlen))) {
return
false
;
}
if
(::listen(sock, 5)) {
return
false
;
}
return
true
;
});
}
inline
int
Server::bind_internal(
const
char
* host,
int
port,
int
socket_flags) {
if
(!is_valid()) {
return
-1; }
svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
if
(svr_sock_ == INVALID_SOCKET) {
return
-1; }
if
(port == 0) {
struct
sockaddr_storage addr;
socklen_t addr_len =
sizeof
(addr);
if
(getsockname(svr_sock_,
reinterpret_cast
<
struct
sockaddr*>(&addr),
&addr_len) == -1) {
return
-1;
}
if
(addr.ss_family == AF_INET) {
return
ntohs(
reinterpret_cast
<
struct
sockaddr_in*>(&addr)->sin_port);
}
else
if
(addr.ss_family == AF_INET6) {
return
ntohs(
reinterpret_cast
<
struct
sockaddr_in6*>(&addr)->sin6_port);
}
else
{
return
-1;
}
}
else
{
return
port;
}
}
inline
bool
Server::listen_internal() {
auto
ret =
true
;
is_running_ =
true
;
{
std::unique_ptr<TaskQueue> task_queue(new_task_queue());
while
(svr_sock_ != INVALID_SOCKET) {
#ifndef _WIN32
if
(idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
#endif
auto
val = detail::select_read(svr_sock_, idle_interval_sec_,
idle_interval_usec_);
if
(val == 0) {
task_queue->on_idle();
continue
;
}
#ifndef _WIN32
}
#endif
socket_t sock = accept(svr_sock_,
nullptr
,
nullptr
);
if
(sock == INVALID_SOCKET) {
if
(
errno
== EMFILE) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue
;
}
if
(svr_sock_ != INVALID_SOCKET) {
detail::close_socket(svr_sock_);
ret =
false
;
}
else
{
;
}
break
;
}
#if __cplusplus > 201703L
task_queue->enqueue([=,
this
]() { process_and_close_socket(sock); });
#else
task_queue->enqueue([=]() { process_and_close_socket(sock); });
#endif
}
task_queue->shutdown();
}
is_running_ =
false
;
return
ret;
}
inline
bool
Server::routing(Request& req, Response& res, Stream& strm) {
if
(pre_routing_handler_ &&
pre_routing_handler_(req, res) == HandlerResponse::Handled) {
return
true
;
}
bool
is_head_request = req.method ==
"HEAD"
;
if
((req.method ==
"GET"
|| is_head_request) &&
handle_file_request(req, res, is_head_request)) {
return
true
;
}
if
(detail::expect_content(req)) {
{
ContentReader reader(
[&](ContentReceiver receiver) {
return
read_content_with_content_receiver(
strm, req, res, std::move(receiver),
nullptr
,
nullptr
);
},
[&](MultipartContentHeader header, ContentReceiver receiver) {
return
read_content_with_content_receiver(strm, req, res,
nullptr
,
std::move(header),
std::move(receiver));
});
if
(req.method ==
"POST"
) {
if
(dispatch_request_for_content_reader(
req, res, std::move(reader),
post_handlers_for_content_reader_)) {
return
true
;
}
}
else
if
(req.method ==
"PUT"
) {
if
(dispatch_request_for_content_reader(
req, res, std::move(reader),
put_handlers_for_content_reader_)) {
return
true
;
}
}
else
if
(req.method ==
"PATCH"
) {
if
(dispatch_request_for_content_reader(
req, res, std::move(reader),
patch_handlers_for_content_reader_)) {
return
true
;
}
}
else
if
(req.method ==
"DELETE"
) {
if
(dispatch_request_for_content_reader(
req, res, std::move(reader),
delete_handlers_for_content_reader_)) {
return
true
;
}
}
}
if
(!read_content(strm, req, res)) {
return
false
; }
}
if
(req.method ==
"GET"
|| req.method ==
"HEAD"
) {
return
dispatch_request(req, res, get_handlers_);
}
else
if
(req.method ==
"POST"
) {
return
dispatch_request(req, res, post_handlers_);
}
else
if
(req.method ==
"PUT"
) {
return
dispatch_request(req, res, put_handlers_);
}
else
if
(req.method ==
"DELETE"
) {
return
dispatch_request(req, res, delete_handlers_);
}
else
if
(req.method ==
"OPTIONS"
) {
return
dispatch_request(req, res, options_handlers_);
}
else
if
(req.method ==
"PATCH"
) {
return
dispatch_request(req, res, patch_handlers_);
}
res.status = 400;
return
false
;
}
inline
bool
Server::dispatch_request(Request& req, Response& res,
const
Handlers& handlers) {
for
(
const
auto
& x : handlers) {
const
auto
& pattern = x.first;
const
auto
& handler = x.second;
if
(std::regex_match(req.path, req.matches, pattern)) {
handler(req, res);
return
true
;
}
}
return
false
;
}
inline
void
Server::apply_ranges(
const
Request& req, Response& res,
std::string& content_type,
std::string& boundary) {
if
(req.ranges.size() > 1) {
boundary = detail::make_multipart_data_boundary();
auto
it = res.headers.find(
"Content-Type"
);
if
(it != res.headers.end()) {
content_type = it->second;
res.headers.erase(it);
}
res.headers.emplace(
"Content-Type"
,
"multipart/byteranges; boundary="
+ boundary);
}
auto
type = detail::encoding_type(req, res);
if
(res.body.empty()) {
if
(res.content_length_ > 0) {
size_t
length = 0;
if
(req.ranges.empty()) {
length = res.content_length_;
}
else
if
(req.ranges.size() == 1) {
auto
offsets =
detail::get_range_offset_and_length(req, res.content_length_, 0);
auto
offset = offsets.first;
length = offsets.second;
auto
content_range = detail::make_content_range_header_field(
offset, length, res.content_length_);
res.set_header(
"Content-Range"
, content_range);
}
else
{
length = detail::get_multipart_ranges_data_length(req, res, boundary,
content_type);
}
res.set_header(
"Content-Length"
, std::to_string(length));
}
else
{
if
(res.content_provider_) {
if
(res.is_chunked_content_provider_) {
res.set_header(
"Transfer-Encoding"
,
"chunked"
);
if
(type == detail::EncodingType::Gzip) {
res.set_header(
"Content-Encoding"
,
"gzip"
);
}
else
if
(type == detail::EncodingType::Brotli) {
res.set_header(
"Content-Encoding"
,
"br"
);
}
}
}
}
}
else
{
if
(req.ranges.empty()) {
;
}
else
if
(req.ranges.size() == 1) {
auto
offsets =
detail::get_range_offset_and_length(req, res.body.size(), 0);
auto
offset = offsets.first;
auto
length = offsets.second;
auto
content_range = detail::make_content_range_header_field(
offset, length, res.body.size());
res.set_header(
"Content-Range"
, content_range);
if
(offset < res.body.size()) {
res.body = res.body.substr(offset, length);
}
else
{
res.body.clear();
res.status = 416;
}
}
else
{
std::string data;
if
(detail::make_multipart_ranges_data(req, res, boundary, content_type,
data)) {
res.body.swap(data);
}
else
{
res.body.clear();
res.status = 416;
}
}
if
(type != detail::EncodingType::None) {
std::unique_ptr<detail::compressor> compressor;
std::string content_encoding;
if
(type == detail::EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
compressor = detail::make_unique<detail::gzip_compressor>();
content_encoding =
"gzip"
;
#endif
}
else
if
(type == detail::EncodingType::Brotli) {
#ifdef CPPHTTPLIB_BROTLI_SUPPORT
compressor = detail::make_unique<detail::brotli_compressor>();
content_encoding =
"br"
;
#endif
}
if
(compressor) {
std::string compressed;
if
(compressor->compress(res.body.data(), res.body.size(),
true
,
[&](
const
char
* data,
size_t
data_len) {
compressed.append(data, data_len);
return
true
;
})) {
res.body.swap(compressed);
res.set_header(
"Content-Encoding"
, content_encoding);
}
}
}
auto
length = std::to_string(res.body.size());
res.set_header(
"Content-Length"
, length);
}
}
inline
bool
Server::dispatch_request_for_content_reader(
Request& req, Response& res, ContentReader content_reader,
const
HandlersForContentReader& handlers) {
for
(
const
auto
& x : handlers) {
const
auto
& pattern = x.first;
const
auto
& handler = x.second;
if
(std::regex_match(req.path, req.matches, pattern)) {
handler(req, res, content_reader);
return
true
;
}
}
return
false
;
}
inline
bool
Server::process_request(Stream& strm,
bool
close_connection,
bool
& connection_closed,
const
std::function<
void
(Request&)>& setup_request) {
std::array<
char
, 2048> buf{};
detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
if
(!line_reader.getline()) {
return
false
; }
Request req;
Response res;
res.version =
"HTTP/1.1"
;
#ifdef _WIN32
#else
#ifndef CPPHTTPLIB_USE_POLL
if
(strm.socket() >= FD_SETSIZE) {
Headers dummy;
detail::read_headers(strm, dummy);
res.status = 500;
return
write_response(strm, close_connection, req, res);
}
#endif
#endif
if
(line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
Headers dummy;
detail::read_headers(strm, dummy);
res.status = 414;
return
write_response(strm, close_connection, req, res);
}
if
(!parse_request_line(line_reader.ptr(), req) ||
!detail::read_headers(strm, req.headers)) {
res.status = 400;
return
write_response(strm, close_connection, req, res);
}
if
(req.get_header_value(
"Connection"
) ==
"close"
) {
connection_closed =
true
;
}
if
(req.version ==
"HTTP/1.0"
&&
req.get_header_value(
"Connection"
) !=
"Keep-Alive"
) {
connection_closed =
true
;
}
strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
req.set_header(
"REMOTE_ADDR"
, req.remote_addr);
req.set_header(
"REMOTE_PORT"
, std::to_string(req.remote_port));
if
(req.has_header(
"Range"
)) {
const
auto
& range_header_value = req.get_header_value(
"Range"
);
if
(!detail::parse_range_header(range_header_value, req.ranges)) {
res.status = 416;
return
write_response(strm, close_connection, req, res);
}
}
if
(setup_request) { setup_request(req); }
if
(req.get_header_value(
"Expect"
) ==
"100-continue"
) {
auto
status = 100;
if
(expect_100_continue_handler_) {
status = expect_100_continue_handler_(req, res);
}
switch
(status) {
case
100:
case
417:
strm.write_format(
"HTTP/1.1 %d %s\r\n\r\n"
, status,
detail::status_message(status));
break
;
default
:
return
write_response(strm, close_connection, req, res);
}
}
bool
routed =
false
;
try
{
routed = routing(req, res, strm);
}
catch
(std::exception& e) {
if
(exception_handler_) {
exception_handler_(req, res, e);
routed =
true
;
}
else
{
res.status = 500;
res.set_header(
"EXCEPTION_WHAT"
, e.what());
}
}
catch
(...) {
res.status = 500;
res.set_header(
"EXCEPTION_WHAT"
,
"UNKNOWN"
);
}
if
(routed) {
if
(res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
return
write_response_with_content(strm, close_connection, req, res);
}
else
{
if
(res.status == -1) { res.status = 404; }
return
write_response(strm, close_connection, req, res);
}
}
inline
bool
Server::is_valid()
const
{
return
true
; }
inline
bool
Server::process_and_close_socket(socket_t sock) {
auto
ret = detail::process_server_socket(
sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
[
this
](Stream& strm,
bool
close_connection,
bool
& connection_closed) {
return
process_request(strm, close_connection, connection_closed,
nullptr
);
});
detail::shutdown_socket(sock);
detail::close_socket(sock);
return
ret;
}
inline
ClientImpl::ClientImpl(
const
std::string& host)
: ClientImpl(host, 80, std::string(), std::string()) {}
inline
ClientImpl::ClientImpl(
const
std::string& host,
int
port)
: ClientImpl(host, port, std::string(), std::string()) {}
inline
ClientImpl::ClientImpl(
const
std::string& host,
int
port,
const
std::string& client_cert_path,
const
std::string& client_key_path)
: host_(host), port_(port),
host_and_port_(host_ +
":"
+ std::to_string(port_)),
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
inline
ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
inline
bool
ClientImpl::is_valid()
const
{
return
true
; }
inline
void
ClientImpl::copy_settings(
const
ClientImpl& rhs) {
client_cert_path_ = rhs.client_cert_path_;
client_key_path_ = rhs.client_key_path_;
connection_timeout_sec_ = rhs.connection_timeout_sec_;
read_timeout_sec_ = rhs.read_timeout_sec_;
read_timeout_usec_ = rhs.read_timeout_usec_;
write_timeout_sec_ = rhs.write_timeout_sec_;
write_timeout_usec_ = rhs.write_timeout_usec_;
basic_auth_username_ = rhs.basic_auth_username_;
basic_auth_password_ = rhs.basic_auth_password_;
bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
digest_auth_username_ = rhs.digest_auth_username_;
digest_auth_password_ = rhs.digest_auth_password_;
#endif
keep_alive_ = rhs.keep_alive_;
follow_location_ = rhs.follow_location_;
tcp_nodelay_ = rhs.tcp_nodelay_;
socket_options_ = rhs.socket_options_;
compress_ = rhs.compress_;
decompress_ = rhs.decompress_;
interface_ = rhs.interface_;
proxy_host_ = rhs.proxy_host_;
proxy_port_ = rhs.proxy_port_;
proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
server_certificate_verification_ = rhs.server_certificate_verification_;
#endif
logger_ = rhs.logger_;
}
inline
socket_t ClientImpl::create_client_socket(Error& error)
const
{
if
(!proxy_host_.empty() && proxy_port_ != -1) {
return
detail::create_client_socket(
proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_,
connection_timeout_sec_, connection_timeout_usec_, interface_, error);
}
return
detail::create_client_socket(
host_.c_str(), port_, tcp_nodelay_, socket_options_,
connection_timeout_sec_, connection_timeout_usec_, interface_, error);
}
inline
bool
ClientImpl::create_and_connect_socket(Socket& socket,
Error& error) {
auto
sock = create_client_socket(error);
if
(sock == INVALID_SOCKET) {
return
false
; }
socket.sock = sock;
return
true
;
}
inline
void
ClientImpl::shutdown_ssl(Socket&
,
bool
) {
assert
(socket_requests_in_flight_ == 0 ||
socket_requests_are_from_thread_ == std::this_thread::get_id());
}
inline
void
ClientImpl::shutdown_socket(Socket& socket) {
if
(socket.sock == INVALID_SOCKET) {
return
; }
detail::shutdown_socket(socket.sock);
}
inline
void
ClientImpl::close_socket(Socket& socket) {
assert
(socket_requests_in_flight_ == 0 ||
socket_requests_are_from_thread_ == std::this_thread::get_id());
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
assert
(socket.ssl ==
nullptr
);
#endif
if
(socket.sock == INVALID_SOCKET) {
return
; }
detail::close_socket(socket.sock);
socket.sock = INVALID_SOCKET;
}
inline
void
ClientImpl::lock_socket_and_shutdown_and_close() {
std::lock_guard<std::mutex> guard(socket_mutex_);
shutdown_ssl(socket_,
true
);
shutdown_socket(socket_);
close_socket(socket_);
}
inline
bool
ClientImpl::read_response_line(Stream& strm,
const
Request& req,
Response& res) {
std::array<
char
, 2048> buf;
detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
if
(!line_reader.getline()) {
return
false
; }
const
static
std::regex re(
"(HTTP/1\\.[01]) (\\d{3}) (.*?)\r\n"
);
std::cmatch m;
if
(!std::regex_match(line_reader.ptr(), m, re)) {
return
req.method ==
"CONNECT"
;
}
res.version = std::string(m[1]);
res.status = std::stoi(std::string(m[2]));
res.reason = std::string(m[3]);
while
(res.status == 100) {
if
(!line_reader.getline()) {
return
false
; }
if
(!line_reader.getline()) {
return
false
; }
if
(!std::regex_match(line_reader.ptr(), m, re)) {
return
false
; }
res.version = std::string(m[1]);
res.status = std::stoi(std::string(m[2]));
res.reason = std::string(m[3]);
}
return
true
;
}
inline
bool
ClientImpl::send(Request& req, Response& res, Error& error) {
std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
{
std::lock_guard<std::mutex> guard(socket_mutex_);
socket_should_be_closed_when_request_is_done_ =
false
;
auto
is_alive =
false
;
if
(socket_.is_open()) {
is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
if
(!is_alive) {
const
bool
shutdown_gracefully =
false
;
shutdown_ssl(socket_, shutdown_gracefully);
shutdown_socket(socket_);
close_socket(socket_);
}
}
if
(!is_alive) {
if
(!create_and_connect_socket(socket_, error)) {
return
false
; }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if
(is_ssl()) {
auto
& scli =
static_cast
<SSLClient&>(*
this
);
if
(!proxy_host_.empty() && proxy_port_ != -1) {
bool
success =
false
;
if
(!scli.connect_with_proxy(socket_, res, success, error)) {
return
success;
}
}
if
(!scli.initialize_ssl(socket_, error)) {
return
false
; }
}
#endif
}
if
(socket_requests_in_flight_ > 1) {
assert
(socket_requests_are_from_thread_ == std::this_thread::get_id());
}
socket_requests_in_flight_ += 1;
socket_requests_are_from_thread_ = std::this_thread::get_id();
}
for
(
const
auto
& header : default_headers_) {
if
(req.headers.find(header.first) == req.headers.end()) {
req.headers.insert(header);
}
}
auto
close_connection = !keep_alive_;
auto
ret = process_socket(socket_, [&](Stream& strm) {
return
handle_request(strm, req, res, close_connection, error);
});
{
std::lock_guard<std::mutex> guard(socket_mutex_);
socket_requests_in_flight_ -= 1;
if
(socket_requests_in_flight_ <= 0) {
assert
(socket_requests_in_flight_ == 0);
socket_requests_are_from_thread_ = std::
thread
::id();
}
if
(socket_should_be_closed_when_request_is_done_ || close_connection ||
!ret) {
shutdown_ssl(socket_,
true
);
shutdown_socket(socket_);
close_socket(socket_);
}
}
if
(!ret) {
if
(error == Error::Success) { error = Error::Unknown; }
}
return
ret;
}
inline
Result ClientImpl::send(
const
Request& req) {
auto
req2 = req;
return
send_(std::move(req2));
}
inline
Result ClientImpl::send_(Request&& req) {
auto
res = detail::make_unique<Response>();
auto
error = Error::Success;
auto
ret = send(req, *res, error);
return
Result{ ret ? std::move(res) :
nullptr
, error, std::move(req.headers) };
}
inline
bool
ClientImpl::handle_request(Stream& strm, Request& req,
Response& res,
bool
close_connection,
Error& error) {
if
(req.path.empty()) {
error = Error::Connection;
return
false
;
}
auto
req_save = req;
bool
ret;
if
(!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
auto
req2 = req;
req2.path =
"http://"
+ host_and_port_ + req.path;
ret = process_request(strm, req2, res, close_connection, error);
req = req2;
req.path = req_save.path;
}
else
{
ret = process_request(strm, req, res, close_connection, error);
}
if
(!ret) {
return
false
; }
if
(300 < res.status && res.status < 400 && follow_location_) {
req = req_save;
ret = redirect(req, res, error);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if
((res.status == 401 || res.status == 407) &&
req.authorization_count_ < 5) {
auto
is_proxy = res.status == 407;
const
auto
& username =
is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
const
auto
& password =
is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;
if
(!username.empty() && !password.empty()) {
std::map<std::string, std::string> auth;
if
(detail::parse_www_authenticate(res, auth, is_proxy)) {
Request new_req = req;
new_req.authorization_count_ += 1;
auto
key = is_proxy ?
"Proxy-Authorization"
:
"Authorization"
;
new_req.headers.erase(key);
new_req.headers.insert(detail::make_digest_authentication_header(
req, auth, new_req.authorization_count_, detail::random_string(10),
username, password, is_proxy));
Response new_res;
ret = send(new_req, new_res, error);
if
(ret) { res = new_res; }
}
}
}
#endif
return
ret;
}
inline
bool
ClientImpl::redirect(Request& req, Response& res, Error& error) {
if
(req.redirect_count_ == 0) {
error = Error::ExceedRedirectCount;
return
false
;
}
auto
location = detail::decode_url(res.get_header_value(
"location"
),
true
);
if
(location.empty()) {
return
false
; }
const
static
std::regex re(
R
"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"
);
std::smatch m;
if
(!std::regex_match(location, m, re)) {
return
false
; }
auto
scheme = is_ssl() ?
"https"
:
"http"
;
auto
next_scheme = m[1].str();
auto
next_host = m[2].str();
auto
port_str = m[3].str();
auto
next_path = m[4].str();
auto
next_port = port_;
if
(!port_str.empty()) {
next_port = std::stoi(port_str);
}
else
if
(!next_scheme.empty()) {
next_port = next_scheme ==
"https"
? 443 : 80;
}
if
(next_scheme.empty()) { next_scheme = scheme; }
if
(next_host.empty()) { next_host = host_; }
if
(next_path.empty()) { next_path =
"/"
; }
if
(next_scheme == scheme && next_host == host_ && next_port == port_) {
return
detail::redirect(*
this
, req, res, next_path, location, error);
}
else
{
if
(next_scheme ==
"https"
) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*
this
);
return
detail::redirect(cli, req, res, next_path, location, error);
#else
return
false
;
#endif
}
else
{
ClientImpl cli(next_host.c_str(), next_port);
cli.copy_settings(*
this
);
return
detail::redirect(cli, req, res, next_path, location, error);
}
}
}
inline
bool
ClientImpl::write_content_with_provider(Stream& strm,
const
Request& req,
Error& error) {
auto
is_shutting_down = []() {
return
false
; };
if
(req.is_chunked_content_provider_) {
std::unique_ptr<detail::compressor> compressor;
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if
(compress_) {
compressor = detail::make_unique<detail::gzip_compressor>();
}
else
#endif
{
compressor = detail::make_unique<detail::nocompressor>();
}
return
detail::write_content_chunked(strm, req.content_provider_,
is_shutting_down, *compressor, error);
}
else
{
return
detail::write_content(strm, req.content_provider_, 0,
req.content_length_, is_shutting_down, error);
}
}
inline
bool
ClientImpl::write_request(Stream& strm, Request& req,
bool
close_connection, Error& error) {
if
(close_connection) { req.headers.emplace(
"Connection"
,
"close"
); }
if
(!req.has_header(
"Host"
)) {
if
(is_ssl()) {
if
(port_ == 443) {
req.headers.emplace(
"Host"
, host_);
}
else
{
req.headers.emplace(
"Host"
, host_and_port_);
}
}
else
{
if
(port_ == 80) {
req.headers.emplace(
"Host"
, host_);
}
else
{
req.headers.emplace(
"Host"
, host_and_port_);
}
}
}
if
(!req.has_header(
"Accept"
)) { req.headers.emplace(
"Accept"
,
"*/*"
); }
if
(!req.has_header(
"User-Agent"
)) {
req.headers.emplace(
"User-Agent"
,
"cpp-httplib/0.7"
);
}
if
(req.body.empty()) {
if
(req.content_provider_) {
if
(!req.is_chunked_content_provider_) {
auto
length = std::to_string(req.content_length_);
req.headers.emplace(
"Content-Length"
, length);
}
}
else
{
if
(req.method ==
"POST"
|| req.method ==
"PUT"
||
req.method ==
"PATCH"
) {
req.headers.emplace(
"Content-Length"
,
"0"
);
}
}
}
else
{
if
(!req.has_header(
"Content-Type"
)) {
req.headers.emplace(
"Content-Type"
,
"text/plain"
);
}
if
(!req.has_header(
"Content-Length"
)) {
auto
length = std::to_string(req.body.size());
req.headers.emplace(
"Content-Length"
, length);
}
}
if
(!basic_auth_password_.empty()) {
req.headers.insert(make_basic_authentication_header(
basic_auth_username_, basic_auth_password_,
false
));
}
if
(!proxy_basic_auth_username_.empty() &&
!proxy_basic_auth_password_.empty()) {
req.headers.insert(make_basic_authentication_header(
proxy_basic_auth_username_, proxy_basic_auth_password_,
true
));
}
if
(!bearer_token_auth_token_.empty()) {
req.headers.insert(make_bearer_token_authentication_header(
bearer_token_auth_token_,
false
));
}
if
(!proxy_bearer_token_auth_token_.empty()) {
req.headers.insert(make_bearer_token_authentication_header(
proxy_bearer_token_auth_token_,
true
));
}
{
detail::BufferStream bstrm;
const
auto
& path = detail::encode_url(req.path);
bstrm.write_format(
"%s %s HTTP/1.1\r\n"
, req.method.c_str(), path.c_str());
detail::write_headers(bstrm, req.headers);
auto
& data = bstrm.get_buffer();
if
(!detail::write_data(strm, data.data(), data.size())) {
error = Error::Write;
return
false
;
}
}
if
(req.body.empty()) {
return
write_content_with_provider(strm, req, error);
}
else
{
return
detail::write_data(strm, req.body.data(), req.body.size());
}
return
true
;
}
inline
std::unique_ptr<Response> ClientImpl::send_with_content_provider(
Request& req,
const
char
* body,
size_t
content_length, ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const
char
* content_type, Error& error) {
if
(content_type) { req.headers.emplace(
"Content-Type"
, content_type); }
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if
(compress_) { req.headers.emplace(
"Content-Encoding"
,
"gzip"
); }
#endif
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if
(compress_ && !content_provider_without_length) {
detail::gzip_compressor compressor;
if
(content_provider) {
auto
ok =
true
;
size_t
offset = 0;
DataSink data_sink;
data_sink.write = [&](
const
char
* data,
size_t
data_len) {
if
(ok) {
auto
last = offset + data_len == content_length;
auto
ret = compressor.compress(
data, data_len, last, [&](
const
char
* data,
size_t
data_len) {
req.body.append(data, data_len);
return
true
;
});
if
(ret) {
offset += data_len;
}
else
{
ok =
false
;
}
}
};
data_sink.is_writable = [&](
void
) {
return
ok &&
true
; };
while
(ok && offset < content_length) {
if
(!content_provider(offset, content_length - offset, data_sink)) {
error = Error::Canceled;
return
nullptr
;
}
}
}
else
{
if
(!compressor.compress(body, content_length,
true
,
[&](
const
char
* data,
size_t
data_len) {
req.body.append(data, data_len);
return
true
;
})) {
error = Error::Compression;
return
nullptr
;
}
}
}
else
#endif
{
if
(content_provider) {
req.content_length_ = content_length;
req.content_provider_ = std::move(content_provider);
req.is_chunked_content_provider_ =
false
;
}
else
if
(content_provider_without_length) {
req.content_length_ = 0;
req.content_provider_ = detail::ContentProviderAdapter(
std::move(content_provider_without_length));
req.is_chunked_content_provider_ =
true
;
req.headers.emplace(
"Transfer-Encoding"
,
"chunked"
);
}
else
{
req.body.assign(body, content_length);
;
}
}
auto
res = detail::make_unique<Response>();
return
send(req, *res, error) ? std::move(res) :
nullptr
;
}
inline
Result ClientImpl::send_with_content_provider(
const
char
* method,
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length, ContentProvider content_provider,
ContentProviderWithoutLength content_provider_without_length,
const
char
* content_type) {
Request req;
req.method = method;
req.headers = headers;
req.path = path;
auto
error = Error::Success;
auto
res = send_with_content_provider(
req,
body, content_length, std::move(content_provider),
std::move(content_provider_without_length), content_type, error);
return
Result{ std::move(res), error, std::move(req.headers) };
}
inline
bool
ClientImpl::process_request(Stream& strm, Request& req,
Response& res,
bool
close_connection,
Error& error) {
if
(!write_request(strm, req, close_connection, error)) {
return
false
; }
if
(!read_response_line(strm, req, res) ||
!detail::read_headers(strm, res.headers)) {
error = Error::Read;
return
false
;
}
if
(req.response_handler) {
if
(!req.response_handler(res)) {
error = Error::Canceled;
return
false
;
}
}
if
((res.status != 204) && req.method !=
"HEAD"
&& req.method !=
"CONNECT"
) {
auto
out =
req.content_receiver
?
static_cast
<ContentReceiverWithProgress>(
[&](
const
char
* buf,
size_t
n, uint64_t off, uint64_t len) {
auto
ret = req.content_receiver(buf, n, off, len);
if
(!ret) { error = Error::Canceled; }
return
ret;
})
:
static_cast
<ContentReceiverWithProgress>(
[&](
const
char
* buf,
size_t
n, uint64_t /*off*/,
uint64_t
) {
if
(res.body.size() + n > res.body.max_size()) {
return
false
;
}
res.body.append(buf, n);
return
true
;
});
auto
progress = [&](uint64_t current, uint64_t total) {
if
(!req.progress) {
return
true
; }
auto
ret = req.progress(current, total);
if
(!ret) { error = Error::Canceled; }
return
ret;
};
int
dummy_status;
if
(!detail::read_content(strm, res, (std::numeric_limits<
size_t
>::max)(),
dummy_status, std::move(progress), std::move(out),
decompress_)) {
if
(error != Error::Canceled) { error = Error::Read; }
return
false
;
}
}
if
(res.get_header_value(
"Connection"
) ==
"close"
||
(res.version ==
"HTTP/1.0"
&& res.reason !=
"Connection established"
)) {
lock_socket_and_shutdown_and_close();
}
if
(logger_) { logger_(req, res); }
return
true
;
}
inline
bool
ClientImpl::process_socket(
const
Socket& socket,
std::function<
bool
(Stream& strm)> callback) {
return
detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
write_timeout_usec_, std::move(callback));
}
inline
bool
ClientImpl::is_ssl()
const
{
return
false
; }
inline
Result ClientImpl::Get(
const
char
* path) {
return
Get(path, Headers(), Progress());
}
inline
Result ClientImpl::Get(
const
char
* path, Progress progress) {
return
Get(path, Headers(), std::move(progress));
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Headers& headers) {
return
Get(path, headers, Progress());
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Headers& headers,
Progress progress) {
Request req;
req.method =
"GET"
;
req.path = path;
req.headers = headers;
req.progress = std::move(progress);
return
send_(std::move(req));
}
inline
Result ClientImpl::Get(
const
char
* path,
ContentReceiver content_receiver) {
return
Get(path, Headers(),
nullptr
, std::move(content_receiver),
nullptr
);
}
inline
Result ClientImpl::Get(
const
char
* path,
ContentReceiver content_receiver,
Progress progress) {
return
Get(path, Headers(),
nullptr
, std::move(content_receiver),
std::move(progress));
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Headers& headers,
ContentReceiver content_receiver) {
return
Get(path, headers,
nullptr
, std::move(content_receiver),
nullptr
);
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Headers& headers,
ContentReceiver content_receiver,
Progress progress) {
return
Get(path, headers,
nullptr
, std::move(content_receiver),
std::move(progress));
}
inline
Result ClientImpl::Get(
const
char
* path,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return
Get(path, Headers(), std::move(response_handler),
std::move(content_receiver),
nullptr
);
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Headers& headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return
Get(path, headers, std::move(response_handler),
std::move(content_receiver),
nullptr
);
}
inline
Result ClientImpl::Get(
const
char
* path,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
return
Get(path, Headers(), std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Headers& headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
Request req;
req.method =
"GET"
;
req.path = path;
req.headers = headers;
req.response_handler = std::move(response_handler);
req.content_receiver =
[content_receiver](
const
char
* data,
size_t
data_length,
uint64_t
, uint64_t
) {
return
content_receiver(data, data_length);
};
req.progress = std::move(progress);
return
send_(std::move(req));
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Params& params,
const
Headers& headers, Progress progress) {
if
(params.empty()) {
return
Get(path, headers); }
std::string path_with_query = detail::append_query_params(path, params);
return
Get(path_with_query.c_str(), headers, progress);
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
ContentReceiver content_receiver,
Progress progress) {
return
Get(path, params, headers,
nullptr
, content_receiver, progress);
}
inline
Result ClientImpl::Get(
const
char
* path,
const
Params& params,
const
Headers& headers,
ResponseHandler response_handler,
ContentReceiver content_receiver,
Progress progress) {
if
(params.empty()) {
return
Get(path, headers, response_handler, content_receiver, progress);
}
std::string path_with_query = detail::append_query_params(path, params);
return
Get(path_with_query.c_str(), params, headers, response_handler,
content_receiver, progress);
}
inline
Result ClientImpl::Head(
const
char
* path) {
return
Head(path, Headers());
}
inline
Result ClientImpl::Head(
const
char
* path,
const
Headers& headers) {
Request req;
req.method =
"HEAD"
;
req.headers = headers;
req.path = path;
return
send_(std::move(req));
}
inline
Result ClientImpl::Post(
const
char
* path) {
return
Post(path, std::string(),
nullptr
);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
Post(path, Headers(), body, content_length, content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
send_with_content_provider(
"POST"
, path, headers, body, content_length,
nullptr
,
nullptr
, content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
std::string& body,
const
char
* content_type) {
return
Post(path, Headers(), body, content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type) {
return
send_with_content_provider(
"POST"
, path, headers, body.data(),
body.size(),
nullptr
,
nullptr
,
content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Params& params) {
return
Post(path, Headers(), params);
}
inline
Result ClientImpl::Post(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
Post(path, Headers(), content_length, std::move(content_provider),
content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
Post(path, Headers(), std::move(content_provider), content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
send_with_content_provider(
"POST"
, path, headers,
nullptr
,
content_length, std::move(content_provider),
nullptr
, content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
send_with_content_provider(
"POST"
, path, headers,
nullptr
, 0,
nullptr
,
std::move(content_provider), content_type);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
const
Params& params) {
auto
query = detail::params_to_query_str(params);
return
Post(path, headers, query,
"application/x-www-form-urlencoded"
);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
MultipartFormDataItems& items) {
return
Post(path, Headers(), items);
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
const
MultipartFormDataItems& items) {
return
Post(path, headers, items, detail::make_multipart_data_boundary());
}
inline
Result ClientImpl::Post(
const
char
* path,
const
Headers& headers,
const
MultipartFormDataItems& items,
const
std::string& boundary) {
for
(
size_t
i = 0; i < boundary.size(); i++) {
char
c = boundary[i];
if
(!std::
isalnum
(c) && c !=
'-'
&& c !=
'_'
) {
return
Result{
nullptr
, Error::UnsupportedMultipartBoundaryChars };
}
}
std::string body;
for
(
const
auto
& item : items) {
body +=
"--"
+ boundary +
"\r\n"
;
body +=
"Content-Disposition: form-data; name=\""
+ item.name +
"\""
;
if
(!item.filename.empty()) {
body +=
"; filename=\""
+ item.filename +
"\""
;
}
body +=
"\r\n"
;
if
(!item.content_type.empty()) {
body +=
"Content-Type: "
+ item.content_type +
"\r\n"
;
}
body +=
"\r\n"
;
body += item.content +
"\r\n"
;
}
body +=
"--"
+ boundary +
"--\r\n"
;
std::string content_type =
"multipart/form-data; boundary="
+ boundary;
return
Post(path, headers, body, content_type.c_str());
}
inline
Result ClientImpl::Put(
const
char
* path) {
return
Put(path, std::string(),
nullptr
);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
Put(path, Headers(), body, content_length, content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
send_with_content_provider(
"PUT"
, path, headers, body, content_length,
nullptr
,
nullptr
, content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
std::string& body,
const
char
* content_type) {
return
Put(path, Headers(), body, content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type) {
return
send_with_content_provider(
"PUT"
, path, headers, body.data(),
body.size(),
nullptr
,
nullptr
,
content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
Put(path, Headers(), content_length, std::move(content_provider),
content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
Put(path, Headers(), std::move(content_provider), content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
send_with_content_provider(
"PUT"
, path, headers,
nullptr
,
content_length, std::move(content_provider),
nullptr
, content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
send_with_content_provider(
"PUT"
, path, headers,
nullptr
, 0,
nullptr
,
std::move(content_provider), content_type);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
Params& params) {
return
Put(path, Headers(), params);
}
inline
Result ClientImpl::Put(
const
char
* path,
const
Headers& headers,
const
Params& params) {
auto
query = detail::params_to_query_str(params);
return
Put(path, headers, query,
"application/x-www-form-urlencoded"
);
}
inline
Result ClientImpl::Patch(
const
char
* path) {
return
Patch(path, std::string(),
nullptr
);
}
inline
Result ClientImpl::Patch(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
Patch(path, Headers(), body, content_length, content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
send_with_content_provider(
"PATCH"
, path, headers, body,
content_length,
nullptr
,
nullptr
,
content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
const
std::string& body,
const
char
* content_type) {
return
Patch(path, Headers(), body, content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type) {
return
send_with_content_provider(
"PATCH"
, path, headers, body.data(),
body.size(),
nullptr
,
nullptr
,
content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
Patch(path, Headers(), content_length, std::move(content_provider),
content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
Patch(path, Headers(), std::move(content_provider), content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
const
Headers& headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
send_with_content_provider(
"PATCH"
, path, headers,
nullptr
,
content_length, std::move(content_provider),
nullptr
, content_type);
}
inline
Result ClientImpl::Patch(
const
char
* path,
const
Headers& headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
send_with_content_provider(
"PATCH"
, path, headers,
nullptr
, 0,
nullptr
,
std::move(content_provider), content_type);
}
inline
Result ClientImpl::Delete(
const
char
* path) {
return
Delete(path, Headers(), std::string(),
nullptr
);
}
inline
Result ClientImpl::Delete(
const
char
* path,
const
Headers& headers) {
return
Delete(path, headers, std::string(),
nullptr
);
}
inline
Result ClientImpl::Delete(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
Delete(path, Headers(), body, content_length, content_type);
}
inline
Result ClientImpl::Delete(
const
char
* path,
const
Headers& headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
Request req;
req.method =
"DELETE"
;
req.headers = headers;
req.path = path;
if
(content_type) { req.headers.emplace(
"Content-Type"
, content_type); }
req.body.assign(body, content_length);
return
send_(std::move(req));
}
inline
Result ClientImpl::Delete(
const
char
* path,
const
std::string& body,
const
char
* content_type) {
return
Delete(path, Headers(), body.data(), body.size(), content_type);
}
inline
Result ClientImpl::Delete(
const
char
* path,
const
Headers& headers,
const
std::string& body,
const
char
* content_type) {
return
Delete(path, headers, body.data(), body.size(), content_type);
}
inline
Result ClientImpl::Options(
const
char
* path) {
return
Options(path, Headers());
}
inline
Result ClientImpl::Options(
const
char
* path,
const
Headers& headers) {
Request req;
req.method =
"OPTIONS"
;
req.headers = headers;
req.path = path;
return
send_(std::move(req));
}
inline
size_t
ClientImpl::is_socket_open()
const
{
std::lock_guard<std::mutex> guard(socket_mutex_);
return
socket_.is_open();
}
inline
void
ClientImpl::stop() {
std::lock_guard<std::mutex> guard(socket_mutex_);
if
(socket_requests_in_flight_ > 0) {
shutdown_socket(socket_);
socket_should_be_closed_when_request_is_done_ =
true
;
return
;
}
shutdown_ssl(socket_,
true
);
shutdown_socket(socket_);
close_socket(socket_);
}
inline
void
ClientImpl::set_connection_timeout(
time_t
sec,
time_t
usec) {
connection_timeout_sec_ = sec;
connection_timeout_usec_ = usec;
}
inline
void
ClientImpl::set_read_timeout(
time_t
sec,
time_t
usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
}
inline
void
ClientImpl::set_write_timeout(
time_t
sec,
time_t
usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
}
inline
void
ClientImpl::set_basic_auth(
const
char
* username,
const
char
* password) {
basic_auth_username_ = username;
basic_auth_password_ = password;
}
inline
void
ClientImpl::set_bearer_token_auth(
const
char
* token) {
bearer_token_auth_token_ = token;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
ClientImpl::set_digest_auth(
const
char
* username,
const
char
* password) {
digest_auth_username_ = username;
digest_auth_password_ = password;
}
#endif
inline
void
ClientImpl::set_keep_alive(
bool
on) { keep_alive_ = on; }
inline
void
ClientImpl::set_follow_location(
bool
on) { follow_location_ = on; }
inline
void
ClientImpl::set_default_headers(Headers headers) {
default_headers_ = std::move(headers);
}
inline
void
ClientImpl::set_tcp_nodelay(
bool
on) { tcp_nodelay_ = on; }
inline
void
ClientImpl::set_socket_options(SocketOptions socket_options) {
socket_options_ = std::move(socket_options);
}
inline
void
ClientImpl::set_compress(
bool
on) { compress_ = on; }
inline
void
ClientImpl::set_decompress(
bool
on) { decompress_ = on; }
inline
void
ClientImpl::set_interface(
const
char
* intf) { interface_ = intf; }
inline
void
ClientImpl::set_proxy(
const
char
* host,
int
port) {
proxy_host_ = host;
proxy_port_ = port;
}
inline
void
ClientImpl::set_proxy_basic_auth(
const
char
* username,
const
char
* password) {
proxy_basic_auth_username_ = username;
proxy_basic_auth_password_ = password;
}
inline
void
ClientImpl::set_proxy_bearer_token_auth(
const
char
* token) {
proxy_bearer_token_auth_token_ = token;
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
ClientImpl::set_proxy_digest_auth(
const
char
* username,
const
char
* password) {
proxy_digest_auth_username_ = username;
proxy_digest_auth_password_ = password;
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
ClientImpl::enable_server_certificate_verification(
bool
enabled) {
server_certificate_verification_ = enabled;
}
#endif
inline
void
ClientImpl::set_logger(Logger logger) {
logger_ = std::move(logger);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
namespace
detail {
template
<
typename
U,
typename
V>
inline
SSL* ssl_new(socket_t sock, SSL_CTX* ctx, std::mutex& ctx_mutex,
U SSL_connect_or_accept, V setup) {
SSL* ssl =
nullptr
;
{
std::lock_guard<std::mutex> guard(ctx_mutex);
ssl = SSL_new(ctx);
}
if
(ssl) {
set_nonblocking(sock,
true
);
auto
bio = BIO_new_socket(
static_cast
<
int
>(sock), BIO_NOCLOSE);
BIO_set_nbio(bio, 1);
SSL_set_bio(ssl, bio, bio);
if
(!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
SSL_shutdown(ssl);
{
std::lock_guard<std::mutex> guard(ctx_mutex);
SSL_free(ssl);
}
set_nonblocking(sock,
false
);
return
nullptr
;
}
BIO_set_nbio(bio, 0);
set_nonblocking(sock,
false
);
}
return
ssl;
}
inline
void
ssl_delete(std::mutex& ctx_mutex, SSL* ssl,
bool
shutdown_gracefully) {
if
(shutdown_gracefully) { SSL_shutdown(ssl); }
std::lock_guard<std::mutex> guard(ctx_mutex);
SSL_free(ssl);
}
template
<
typename
U>
bool
ssl_connect_or_accept_nonblocking(socket_t sock, SSL* ssl,
U ssl_connect_or_accept,
time_t
timeout_sec,
time_t
timeout_usec) {
int
res = 0;
while
((res = ssl_connect_or_accept(ssl)) != 1) {
auto
err = SSL_get_error(ssl, res);
switch
(err) {
case
SSL_ERROR_WANT_READ:
if
(select_read(sock, timeout_sec, timeout_usec) > 0) {
continue
; }
break
;
case
SSL_ERROR_WANT_WRITE:
if
(select_write(sock, timeout_sec, timeout_usec) > 0) {
continue
; }
break
;
default
:
break
;
}
return
false
;
}
return
true
;
}
template
<
typename
T>
inline
bool
process_server_socket_ssl(SSL* ssl, socket_t sock,
size_t
keep_alive_max_count,
time_t
keep_alive_timeout_sec,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec,
T callback) {
return
process_server_socket_core(
sock, keep_alive_max_count, keep_alive_timeout_sec,
[&](
bool
close_connection,
bool
& connection_closed) {
SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
write_timeout_sec, write_timeout_usec);
return
callback(strm, close_connection, connection_closed);
});
}
template
<
typename
T>
inline
bool
process_client_socket_ssl(SSL* ssl, socket_t sock,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec, T callback) {
SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
write_timeout_sec, write_timeout_usec);
return
callback(strm);
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static
std::shared_ptr<std::vector<std::mutex>> openSSL_locks_;
class
SSLThreadLocks {
public
:
SSLThreadLocks() {
openSSL_locks_ =
std::make_shared<std::vector<std::mutex>>(CRYPTO_num_locks());
CRYPTO_set_locking_callback(locking_callback);
}
~SSLThreadLocks() { CRYPTO_set_locking_callback(
nullptr
); }
private
:
static
void
locking_callback(
int
mode,
int
type,
const
char
*
,
int
) {
auto
& lk = (*openSSL_locks_)[
static_cast
<
size_t
>(type)];
if
(mode & CRYPTO_LOCK) {
lk.lock();
}
else
{
lk.unlock();
}
}
};
#endif
class
SSLInit {
public
:
SSLInit() {
#if OPENSSL_VERSION_NUMBER < 0x1010001fL
SSL_load_error_strings();
SSL_library_init();
#else
OPENSSL_init_ssl(
OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
#endif
}
~SSLInit() {
#if OPENSSL_VERSION_NUMBER < 0x1010001fL
ERR_free_strings();
#endif
}
private
:
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSLThreadLocks thread_init_;
#endif
};
inline
SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl,
time_t
read_timeout_sec,
time_t
read_timeout_usec,
time_t
write_timeout_sec,
time_t
write_timeout_usec)
: sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
read_timeout_usec_(read_timeout_usec),
write_timeout_sec_(write_timeout_sec),
write_timeout_usec_(write_timeout_usec) {
SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
}
inline
SSLSocketStream::~SSLSocketStream() {}
inline
bool
SSLSocketStream::is_readable()
const
{
return
detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
}
inline
bool
SSLSocketStream::is_writable()
const
{
return
detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) >
0;
}
inline
ssize_t SSLSocketStream::read(
char
* ptr,
size_t
size) {
if
(SSL_pending(ssl_) > 0) {
return
SSL_read(ssl_, ptr,
static_cast
<
int
>(size));
}
else
if
(is_readable()) {
auto
ret = SSL_read(ssl_, ptr,
static_cast
<
int
>(size));
if
(ret < 0) {
auto
err = SSL_get_error(ssl_, ret);
while
(err == SSL_ERROR_WANT_READ) {
if
(SSL_pending(ssl_) > 0) {
return
SSL_read(ssl_, ptr,
static_cast
<
int
>(size));
}
else
if
(is_readable()) {
ret = SSL_read(ssl_, ptr,
static_cast
<
int
>(size));
if
(ret >= 0) {
return
ret; }
err = SSL_get_error(ssl_, ret);
}
else
{
return
-1;
}
}
}
return
ret;
}
return
-1;
}
inline
ssize_t SSLSocketStream::write(
const
char
* ptr,
size_t
size) {
if
(is_writable()) {
return
SSL_write(ssl_, ptr,
static_cast
<
int
>(size)); }
return
-1;
}
inline
void
SSLSocketStream::get_remote_ip_and_port(std::string& ip,
int
& port)
const
{
detail::get_remote_ip_and_port(sock_, ip, port);
}
inline
socket_t SSLSocketStream::socket()
const
{
return
sock_; }
static
SSLInit sslinit_;
}
inline
SSLServer::SSLServer(
const
char
* cert_path,
const
char
* private_key_path,
const
char
* client_ca_cert_file_path,
const
char
* client_ca_cert_dir_path) {
ctx_ = SSL_CTX_new(SSLv23_server_method());
if
(ctx_) {
SSL_CTX_set_options(ctx_,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
if
(SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
1) {
SSL_CTX_free(ctx_);
ctx_ =
nullptr
;
}
else
if
(client_ca_cert_file_path || client_ca_cert_dir_path) {
SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,
client_ca_cert_dir_path);
SSL_CTX_set_verify(
ctx_,
SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr
);
}
}
}
inline
SSLServer::SSLServer(X509* cert, EVP_PKEY* private_key,
X509_STORE* client_ca_cert_store) {
ctx_ = SSL_CTX_new(SSLv23_server_method());
if
(ctx_) {
SSL_CTX_set_options(ctx_,
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_NO_COMPRESSION |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
if
(SSL_CTX_use_certificate(ctx_, cert) != 1 ||
SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
SSL_CTX_free(ctx_);
ctx_ =
nullptr
;
}
else
if
(client_ca_cert_store) {
SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
SSL_CTX_set_verify(
ctx_,
SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr
);
}
}
}
inline
SSLServer::~SSLServer() {
if
(ctx_) { SSL_CTX_free(ctx_); }
}
inline
bool
SSLServer::is_valid()
const
{
return
ctx_; }
inline
bool
SSLServer::process_and_close_socket(socket_t sock) {
auto
ssl = detail::ssl_new(
sock, ctx_, ctx_mutex_,
[&](SSL* ssl) {
return
detail::ssl_connect_or_accept_nonblocking(
sock, ssl, SSL_accept, read_timeout_sec_, read_timeout_usec_);
},
[](SSL*
) {
return
true
; });
bool
ret =
false
;
if
(ssl) {
ret = detail::process_server_socket_ssl(
ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
write_timeout_usec_,
[
this
, ssl](Stream& strm,
bool
close_connection,
bool
& connection_closed) {
return
process_request(strm, close_connection, connection_closed,
[&](Request& req) { req.ssl = ssl; });
});
const
bool
shutdown_gracefully = ret;
detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
}
detail::shutdown_socket(sock);
detail::close_socket(sock);
return
ret;
}
inline
SSLClient::SSLClient(
const
std::string& host)
: SSLClient(host, 443, std::string(), std::string()) {}
inline
SSLClient::SSLClient(
const
std::string& host,
int
port)
: SSLClient(host, port, std::string(), std::string()) {}
inline
SSLClient::SSLClient(
const
std::string& host,
int
port,
const
std::string& client_cert_path,
const
std::string& client_key_path)
: ClientImpl(host, port, client_cert_path, client_key_path) {
ctx_ = SSL_CTX_new(SSLv23_client_method());
detail::split(&host_[0], &host_[host_.size()],
'.'
,
[&](
const
char
* b,
const
char
* e) {
host_components_.emplace_back(std::string(b, e));
});
if
(!client_cert_path.empty() && !client_key_path.empty()) {
if
(SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),
SSL_FILETYPE_PEM) != 1 ||
SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),
SSL_FILETYPE_PEM) != 1) {
SSL_CTX_free(ctx_);
ctx_ =
nullptr
;
}
}
}
inline
SSLClient::SSLClient(
const
std::string& host,
int
port,
X509* client_cert, EVP_PKEY* client_key)
: ClientImpl(host, port) {
ctx_ = SSL_CTX_new(SSLv23_client_method());
detail::split(&host_[0], &host_[host_.size()],
'.'
,
[&](
const
char
* b,
const
char
* e) {
host_components_.emplace_back(std::string(b, e));
});
if
(client_cert !=
nullptr
&& client_key !=
nullptr
) {
if
(SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
SSL_CTX_free(ctx_);
ctx_ =
nullptr
;
}
}
}
inline
SSLClient::~SSLClient() {
if
(ctx_) { SSL_CTX_free(ctx_); }
SSLClient::shutdown_ssl(socket_,
true
);
}
inline
bool
SSLClient::is_valid()
const
{
return
ctx_; }
inline
void
SSLClient::set_ca_cert_path(
const
char
* ca_cert_file_path,
const
char
* ca_cert_dir_path) {
if
(ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; }
if
(ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
}
inline
void
SSLClient::set_ca_cert_store(X509_STORE* ca_cert_store) {
if
(ca_cert_store) {
if
(ctx_) {
if
(SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
SSL_CTX_set_cert_store(ctx_, ca_cert_store);
}
}
else
{
X509_STORE_free(ca_cert_store);
}
}
}
inline
long
SSLClient::get_openssl_verify_result()
const
{
return
verify_result_;
}
inline
SSL_CTX* SSLClient::ssl_context()
const
{
return
ctx_; }
inline
bool
SSLClient::create_and_connect_socket(Socket& socket, Error& error) {
return
is_valid() && ClientImpl::create_and_connect_socket(socket, error);
}
inline
bool
SSLClient::connect_with_proxy(Socket& socket, Response& res,
bool
& success, Error& error) {
success =
true
;
Response res2;
if
(!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_,
write_timeout_sec_, write_timeout_usec_, [&](Stream& strm) {
Request req2;
req2.method =
"CONNECT"
;
req2.path = host_and_port_;
return
process_request(strm, req2, res2,
false
, error);
})) {
shutdown_ssl(socket,
true
);
shutdown_socket(socket);
close_socket(socket);
success =
false
;
return
false
;
}
if
(res2.status == 407) {
if
(!proxy_digest_auth_username_.empty() &&
!proxy_digest_auth_password_.empty()) {
std::map<std::string, std::string> auth;
if
(detail::parse_www_authenticate(res2, auth,
true
)) {
Response res3;
if
(!detail::process_client_socket(
socket.sock, read_timeout_sec_, read_timeout_usec_,
write_timeout_sec_, write_timeout_usec_, [&](Stream& strm) {
Request req3;
req3.method =
"CONNECT"
;
req3.path = host_and_port_;
req3.headers.insert(detail::make_digest_authentication_header(
req3, auth, 1, detail::random_string(10),
proxy_digest_auth_username_, proxy_digest_auth_password_,
true
));
return
process_request(strm, req3, res3,
false
, error);
})) {
shutdown_ssl(socket,
true
);
shutdown_socket(socket);
close_socket(socket);
success =
false
;
return
false
;
}
}
}
else
{
res = res2;
return
false
;
}
}
return
true
;
}
inline
bool
SSLClient::load_certs() {
bool
ret =
true
;
std::call_once(initialize_cert_, [&]() {
std::lock_guard<std::mutex> guard(ctx_mutex_);
if
(!ca_cert_file_path_.empty()) {
if
(!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
nullptr
)) {
ret =
false
;
}
}
else
if
(!ca_cert_dir_path_.empty()) {
if
(!SSL_CTX_load_verify_locations(ctx_,
nullptr
,
ca_cert_dir_path_.c_str())) {
ret =
false
;
}
}
else
{
#ifdef _WIN32
detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
#else
SSL_CTX_set_default_verify_paths(ctx_);
#endif
}
});
return
ret;
}
inline
bool
SSLClient::initialize_ssl(Socket& socket, Error& error) {
auto
ssl = detail::ssl_new(
socket.sock, ctx_, ctx_mutex_,
[&](SSL* ssl) {
if
(server_certificate_verification_) {
if
(!load_certs()) {
error = Error::SSLLoadingCerts;
return
false
;
}
SSL_set_verify(ssl, SSL_VERIFY_NONE,
nullptr
);
}
if
(!detail::ssl_connect_or_accept_nonblocking(
socket.sock, ssl, SSL_connect, connection_timeout_sec_,
connection_timeout_usec_)) {
error = Error::SSLConnection;
return
false
;
}
if
(server_certificate_verification_) {
verify_result_ = SSL_get_verify_result(ssl);
if
(verify_result_ != X509_V_OK) {
error = Error::SSLServerVerification;
return
false
;
}
auto
server_cert = SSL_get_peer_certificate(ssl);
if
(server_cert ==
nullptr
) {
error = Error::SSLServerVerification;
return
false
;
}
if
(!verify_host(server_cert)) {
X509_free(server_cert);
error = Error::SSLServerVerification;
return
false
;
}
X509_free(server_cert);
}
return
true
;
},
[&](SSL* ssl) {
SSL_set_tlsext_host_name(ssl, host_.c_str());
return
true
;
});
if
(ssl) {
socket.ssl = ssl;
return
true
;
}
shutdown_socket(socket);
close_socket(socket);
return
false
;
}
inline
void
SSLClient::shutdown_ssl(Socket& socket,
bool
shutdown_gracefully) {
if
(socket.sock == INVALID_SOCKET) {
assert
(socket.ssl ==
nullptr
);
return
;
}
if
(socket.ssl) {
detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
socket.ssl =
nullptr
;
}
assert
(socket.ssl ==
nullptr
);
}
inline
bool
SSLClient::process_socket(
const
Socket& socket,
std::function<
bool
(Stream& strm)> callback) {
assert
(socket.ssl);
return
detail::process_client_socket_ssl(
socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
write_timeout_sec_, write_timeout_usec_, std::move(callback));
}
inline
bool
SSLClient::is_ssl()
const
{
return
true
; }
inline
bool
SSLClient::verify_host(X509* server_cert)
const
{
return
verify_host_with_subject_alt_name(server_cert) ||
verify_host_with_common_name(server_cert);
}
inline
bool
SSLClient::verify_host_with_subject_alt_name(X509* server_cert)
const
{
auto
ret =
false
;
auto
type = GEN_DNS;
struct
in6_addr addr6;
struct
in_addr addr;
size_t
addr_len = 0;
#ifndef __MINGW32__
if
(inet_pton(AF_INET6, host_.c_str(), &addr6)) {
type = GEN_IPADD;
addr_len =
sizeof
(
struct
in6_addr);
}
else
if
(inet_pton(AF_INET, host_.c_str(), &addr)) {
type = GEN_IPADD;
addr_len =
sizeof
(
struct
in_addr);
}
#endif
auto
alt_names =
static_cast
<
const
struct
stack_st_GENERAL_NAME*>(
X509_get_ext_d2i(server_cert, NID_subject_alt_name,
nullptr
,
nullptr
));
if
(alt_names) {
auto
dsn_matched =
false
;
auto
ip_mached =
false
;
auto
count = sk_GENERAL_NAME_num(alt_names);
for
(
decltype
(count) i = 0; i < count && !dsn_matched; i++) {
auto
val = sk_GENERAL_NAME_value(alt_names, i);
if
(val->type == type) {
auto
name = (
const
char
*)ASN1_STRING_get0_data(val->d.ia5);
auto
name_len = (
size_t
)ASN1_STRING_length(val->d.ia5);
switch
(type) {
case
GEN_DNS: dsn_matched = check_host_name(name, name_len);
break
;
case
GEN_IPADD:
if
(!
memcmp
(&addr6, name, addr_len) ||
!
memcmp
(&addr, name, addr_len)) {
ip_mached =
true
;
}
break
;
}
}
}
if
(dsn_matched || ip_mached) { ret =
true
; }
}
GENERAL_NAMES_free((STACK_OF(GENERAL_NAME)*)alt_names);
return
ret;
}
inline
bool
SSLClient::verify_host_with_common_name(X509* server_cert)
const
{
const
auto
subject_name = X509_get_subject_name(server_cert);
if
(subject_name !=
nullptr
) {
char
name[BUFSIZ];
auto
name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
name,
sizeof
(name));
if
(name_len != -1) {
return
check_host_name(name,
static_cast
<
size_t
>(name_len));
}
}
return
false
;
}
inline
bool
SSLClient::check_host_name(
const
char
* pattern,
size_t
pattern_len)
const
{
if
(host_.size() == pattern_len && host_ == pattern) {
return
true
; }
std::vector<std::string> pattern_components;
detail::split(&pattern[0], &pattern[pattern_len],
'.'
,
[&](
const
char
* b,
const
char
* e) {
pattern_components.emplace_back(std::string(b, e));
});
if
(host_components_.size() != pattern_components.size()) {
return
false
; }
auto
itr = pattern_components.begin();
for
(
const
auto
& h : host_components_) {
auto
& p = *itr;
if
(p != h && p !=
"*"
) {
auto
partial_match = (p.size() > 0 && p[p.size() - 1] ==
'*'
&&
!p.compare(0, p.size() - 1, h));
if
(!partial_match) {
return
false
; }
}
++itr;
}
return
true
;
}
#endif
inline
Client::Client(
const
char
* scheme_host_port)
: Client(scheme_host_port, std::string(), std::string()) {}
inline
Client::Client(
const
char
* scheme_host_port,
const
std::string& client_cert_path,
const
std::string& client_key_path) {
const
static
std::regex re(R
"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)"
);
std::cmatch m;
if
(std::regex_match(scheme_host_port, m, re)) {
auto
scheme = m[1].str();
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if
(!scheme.empty() && (scheme !=
"http"
&& scheme !=
"https"
)) {
#else
if
(!scheme.empty() && scheme !=
"http"
) {
#endif
std::string msg =
"'"
+ scheme +
"' scheme is not supported."
;
throw
std::invalid_argument(msg);
return
;
}
auto
is_ssl = scheme ==
"https"
;
auto
host = m[2].str();
auto
port_str = m[3].str();
auto
port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
if
(is_ssl) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
client_cert_path, client_key_path);
is_ssl_ = is_ssl;
#endif
}
else
{
cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
client_cert_path, client_key_path);
}
}
else
{
cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
client_cert_path, client_key_path);
}
}
inline
Client::Client(
const
std::string & host,
int
port)
: cli_(detail::make_unique<ClientImpl>(host, port)) {}
inline
Client::Client(
const
std::string & host,
int
port,
const
std::string & client_cert_path,
const
std::string & client_key_path)
: cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
client_key_path)) {}
inline
Client::~Client() {}
inline
bool
Client::is_valid()
const
{
return
cli_ !=
nullptr
&& cli_->is_valid();
}
inline
Result Client::Get(
const
char
* path) {
return
cli_->Get(path); }
inline
Result Client::Get(
const
char
* path,
const
Headers & headers) {
return
cli_->Get(path, headers);
}
inline
Result Client::Get(
const
char
* path, Progress progress) {
return
cli_->Get(path, std::move(progress));
}
inline
Result Client::Get(
const
char
* path,
const
Headers & headers,
Progress progress) {
return
cli_->Get(path, headers, std::move(progress));
}
inline
Result Client::Get(
const
char
* path, ContentReceiver content_receiver) {
return
cli_->Get(path, std::move(content_receiver));
}
inline
Result Client::Get(
const
char
* path,
const
Headers & headers,
ContentReceiver content_receiver) {
return
cli_->Get(path, headers, std::move(content_receiver));
}
inline
Result Client::Get(
const
char
* path, ContentReceiver content_receiver,
Progress progress) {
return
cli_->Get(path, std::move(content_receiver), std::move(progress));
}
inline
Result Client::Get(
const
char
* path,
const
Headers & headers,
ContentReceiver content_receiver, Progress progress) {
return
cli_->Get(path, headers, std::move(content_receiver),
std::move(progress));
}
inline
Result Client::Get(
const
char
* path, ResponseHandler response_handler,
ContentReceiver content_receiver) {
return
cli_->Get(path, std::move(response_handler),
std::move(content_receiver));
}
inline
Result Client::Get(
const
char
* path,
const
Headers & headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
return
cli_->Get(path, headers, std::move(response_handler),
std::move(content_receiver));
}
inline
Result Client::Get(
const
char
* path, ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress) {
return
cli_->Get(path, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline
Result Client::Get(
const
char
* path,
const
Headers & headers,
ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress) {
return
cli_->Get(path, headers, std::move(response_handler),
std::move(content_receiver), std::move(progress));
}
inline
Result Client::Get(
const
char
* path,
const
Params & params,
const
Headers & headers, Progress progress) {
return
cli_->Get(path, params, headers, progress);
}
inline
Result Client::Get(
const
char
* path,
const
Params & params,
const
Headers & headers,
ContentReceiver content_receiver, Progress progress) {
return
cli_->Get(path, params, headers, content_receiver, progress);
}
inline
Result Client::Get(
const
char
* path,
const
Params & params,
const
Headers & headers,
ResponseHandler response_handler,
ContentReceiver content_receiver, Progress progress) {
return
cli_->Get(path, params, headers, response_handler, content_receiver,
progress);
}
inline
Result Client::Head(
const
char
* path) {
return
cli_->Head(path); }
inline
Result Client::Head(
const
char
* path,
const
Headers & headers) {
return
cli_->Head(path, headers);
}
inline
Result Client::Post(
const
char
* path) {
return
cli_->Post(path); }
inline
Result Client::Post(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Post(path, body, content_length, content_type);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Post(path, headers, body, content_length, content_type);
}
inline
Result Client::Post(
const
char
* path,
const
std::string & body,
const
char
* content_type) {
return
cli_->Post(path, body, content_type);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
const
std::string & body,
const
char
* content_type) {
return
cli_->Post(path, headers, body, content_type);
}
inline
Result Client::Post(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
cli_->Post(path, content_length, std::move(content_provider),
content_type);
}
inline
Result Client::Post(
const
char
* path,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
cli_->Post(path, std::move(content_provider), content_type);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
cli_->Post(path, headers, content_length, std::move(content_provider),
content_type);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
cli_->Post(path, headers, std::move(content_provider), content_type);
}
inline
Result Client::Post(
const
char
* path,
const
Params & params) {
return
cli_->Post(path, params);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
const
Params & params) {
return
cli_->Post(path, headers, params);
}
inline
Result Client::Post(
const
char
* path,
const
MultipartFormDataItems & items) {
return
cli_->Post(path, items);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
const
MultipartFormDataItems & items) {
return
cli_->Post(path, headers, items);
}
inline
Result Client::Post(
const
char
* path,
const
Headers & headers,
const
MultipartFormDataItems & items,
const
std::string & boundary) {
return
cli_->Post(path, headers, items, boundary);
}
inline
Result Client::Put(
const
char
* path) {
return
cli_->Put(path); }
inline
Result Client::Put(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Put(path, body, content_length, content_type);
}
inline
Result Client::Put(
const
char
* path,
const
Headers & headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Put(path, headers, body, content_length, content_type);
}
inline
Result Client::Put(
const
char
* path,
const
std::string & body,
const
char
* content_type) {
return
cli_->Put(path, body, content_type);
}
inline
Result Client::Put(
const
char
* path,
const
Headers & headers,
const
std::string & body,
const
char
* content_type) {
return
cli_->Put(path, headers, body, content_type);
}
inline
Result Client::Put(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
cli_->Put(path, content_length, std::move(content_provider),
content_type);
}
inline
Result Client::Put(
const
char
* path,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
cli_->Put(path, std::move(content_provider), content_type);
}
inline
Result Client::Put(
const
char
* path,
const
Headers & headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
cli_->Put(path, headers, content_length, std::move(content_provider),
content_type);
}
inline
Result Client::Put(
const
char
* path,
const
Headers & headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
cli_->Put(path, headers, std::move(content_provider), content_type);
}
inline
Result Client::Put(
const
char
* path,
const
Params & params) {
return
cli_->Put(path, params);
}
inline
Result Client::Put(
const
char
* path,
const
Headers & headers,
const
Params & params) {
return
cli_->Put(path, headers, params);
}
inline
Result Client::Patch(
const
char
* path) {
return
cli_->Patch(path); }
inline
Result Client::Patch(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Patch(path, body, content_length, content_type);
}
inline
Result Client::Patch(
const
char
* path,
const
Headers & headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Patch(path, headers, body, content_length, content_type);
}
inline
Result Client::Patch(
const
char
* path,
const
std::string & body,
const
char
* content_type) {
return
cli_->Patch(path, body, content_type);
}
inline
Result Client::Patch(
const
char
* path,
const
Headers & headers,
const
std::string & body,
const
char
* content_type) {
return
cli_->Patch(path, headers, body, content_type);
}
inline
Result Client::Patch(
const
char
* path,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
cli_->Patch(path, content_length, std::move(content_provider),
content_type);
}
inline
Result Client::Patch(
const
char
* path,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
cli_->Patch(path, std::move(content_provider), content_type);
}
inline
Result Client::Patch(
const
char
* path,
const
Headers & headers,
size_t
content_length,
ContentProvider content_provider,
const
char
* content_type) {
return
cli_->Patch(path, headers, content_length, std::move(content_provider),
content_type);
}
inline
Result Client::Patch(
const
char
* path,
const
Headers & headers,
ContentProviderWithoutLength content_provider,
const
char
* content_type) {
return
cli_->Patch(path, headers, std::move(content_provider), content_type);
}
inline
Result Client::Delete(
const
char
* path) {
return
cli_->Delete(path); }
inline
Result Client::Delete(
const
char
* path,
const
Headers & headers) {
return
cli_->Delete(path, headers);
}
inline
Result Client::Delete(
const
char
* path,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Delete(path, body, content_length, content_type);
}
inline
Result Client::Delete(
const
char
* path,
const
Headers & headers,
const
char
* body,
size_t
content_length,
const
char
* content_type) {
return
cli_->Delete(path, headers, body, content_length, content_type);
}
inline
Result Client::Delete(
const
char
* path,
const
std::string & body,
const
char
* content_type) {
return
cli_->Delete(path, body, content_type);
}
inline
Result Client::Delete(
const
char
* path,
const
Headers & headers,
const
std::string & body,
const
char
* content_type) {
return
cli_->Delete(path, headers, body, content_type);
}
inline
Result Client::Options(
const
char
* path) {
return
cli_->Options(path); }
inline
Result Client::Options(
const
char
* path,
const
Headers & headers) {
return
cli_->Options(path, headers);
}
inline
bool
Client::send(Request & req, Response & res, Error & error) {
return
cli_->send(req, res, error);
}
inline
Result Client::send(
const
Request & req) {
return
cli_->send(req); }
inline
size_t
Client::is_socket_open()
const
{
return
cli_->is_socket_open(); }
inline
void
Client::stop() { cli_->stop(); }
inline
void
Client::set_default_headers(Headers headers) {
cli_->set_default_headers(std::move(headers));
}
inline
void
Client::set_tcp_nodelay(
bool
on) { cli_->set_tcp_nodelay(on); }
inline
void
Client::set_socket_options(SocketOptions socket_options) {
cli_->set_socket_options(std::move(socket_options));
}
inline
void
Client::set_connection_timeout(
time_t
sec,
time_t
usec) {
cli_->set_connection_timeout(sec, usec);
}
inline
void
Client::set_read_timeout(
time_t
sec,
time_t
usec) {
cli_->set_read_timeout(sec, usec);
}
inline
void
Client::set_write_timeout(
time_t
sec,
time_t
usec) {
cli_->set_write_timeout(sec, usec);
}
inline
void
Client::set_basic_auth(
const
char
* username,
const
char
* password) {
cli_->set_basic_auth(username, password);
}
inline
void
Client::set_bearer_token_auth(
const
char
* token) {
cli_->set_bearer_token_auth(token);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
Client::set_digest_auth(
const
char
* username,
const
char
* password) {
cli_->set_digest_auth(username, password);
}
#endif
inline
void
Client::set_keep_alive(
bool
on) { cli_->set_keep_alive(on); }
inline
void
Client::set_follow_location(
bool
on) {
cli_->set_follow_location(on);
}
inline
void
Client::set_compress(
bool
on) { cli_->set_compress(on); }
inline
void
Client::set_decompress(
bool
on) { cli_->set_decompress(on); }
inline
void
Client::set_interface(
const
char
* intf) {
cli_->set_interface(intf);
}
inline
void
Client::set_proxy(
const
char
* host,
int
port) {
cli_->set_proxy(host, port);
}
inline
void
Client::set_proxy_basic_auth(
const
char
* username,
const
char
* password) {
cli_->set_proxy_basic_auth(username, password);
}
inline
void
Client::set_proxy_bearer_token_auth(
const
char
* token) {
cli_->set_proxy_bearer_token_auth(token);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
Client::set_proxy_digest_auth(
const
char
* username,
const
char
* password) {
cli_->set_proxy_digest_auth(username, password);
}
#endif
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
Client::enable_server_certificate_verification(
bool
enabled) {
cli_->enable_server_certificate_verification(enabled);
}
#endif
inline
void
Client::set_logger(Logger logger) { cli_->set_logger(logger); }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline
void
Client::set_ca_cert_path(
const
char
* ca_cert_file_path,
const
char
* ca_cert_dir_path) {
if
(is_ssl_) {
static_cast
<SSLClient&>(*cli_).set_ca_cert_path(ca_cert_file_path,
ca_cert_dir_path);
}
}
inline
void
Client::set_ca_cert_store(X509_STORE * ca_cert_store) {
if
(is_ssl_) {
static_cast
<SSLClient&>(*cli_).set_ca_cert_store(ca_cert_store);
}
}
inline
long
Client::get_openssl_verify_result()
const
{
if
(is_ssl_) {
return
static_cast
<SSLClient&>(*cli_).get_openssl_verify_result();
}
return
-1;
}
inline
SSL_CTX* Client::ssl_context()
const
{
if
(is_ssl_) {
return
static_cast
<SSLClient&>(*cli_).ssl_context(); }
return
nullptr
;
}
#endif
}
#endif // CPPHTTPLIB_HTTPLIB_H