Compare commits

..

14 Commits

Author SHA1 Message Date
yhirose
a444b612af V0.5.0 2019-12-22 12:52:08 -05:00
yhirose
ed6d949f42 Fix #299 2019-12-22 12:50:25 -05:00
yhirose
d28cd3f937 Code cleanup 2019-12-21 23:20:30 -05:00
yhirose
8cc3e6c434 Merge pull request #296 from yhirose/connect
CONNECT method support on client
2019-12-21 23:09:10 -05:00
yhirose
26fbc1b7c0 Merge pull request #297 from hyperxor/fix_progress_redundant_copying
Fix redundant Progress copy in Get methods
2019-12-21 07:43:45 -05:00
hyperxor
0dc653f45a Fix redundant Progress copy in Get methods 2019-12-21 10:57:06 +03:00
yhirose
7a58c0a430 Updated README regarding regex issue in g++ 4.8 and below 2019-12-20 23:16:05 -05:00
yhirose
dabaa51a7d Updated README 2019-12-20 23:12:24 -05:00
yhirose
a1cfc0f377 Fixed problem with redirect 2019-12-20 13:25:11 -05:00
yhirose
eb4fcb5003 CONNECT method support on client 2019-12-20 06:59:59 -05:00
yhirose
ae43c96984 Merge pull request #295 from yhirose/timeout
Fix #294
2019-12-18 17:57:23 -05:00
yhirose
9c81693801 Fix #294 2019-12-18 17:47:36 -05:00
yhirose
80202c9f62 Merge pull request #292 from Bendr0id/fix_socket_create_on_older_windows_systems
Adds workaround for socket creation on older Windows variants
2019-12-18 07:09:48 -05:00
Ben Gräf
094a6a614a Adds workaround for socket creation on older Windows variants
Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 and above the socket creation fails on older Windows Systems.
     
Let's try to create a socket the old way in this case.
     
Reference:
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
     
WSA_FLAG_NO_HANDLE_INHERIT:
This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with SP1, and later
2019-12-18 07:49:36 +01:00
3 changed files with 387 additions and 177 deletions

View File

@@ -74,7 +74,7 @@ svr.set_logger([](const auto& req, const auto& res) {
```cpp
svr.set_error_handler([](const auto& req, auto& res) {
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
char buf[BUFSIZ];
snprintf(buf, sizeof(buf), fmt, res.status);
res.set_content(buf, "text/html");
@@ -301,7 +301,7 @@ res = cli.Options("/resource/foo");
### Connection Timeout
```c++
httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds
cli.set_timeout_sec(5); // timeouts in 5 seconds
```
### With Progress Callback
@@ -325,23 +325,30 @@ This feature was contributed by [underscorediscovery](https://github.com/yhirose
### Authentication
NOTE: OpenSSL is required for Digest Authentication, since cpp-httplib uses message digest functions in OpenSSL.
```cpp
// Basic Authentication
cli.set_basic_auth("user", "pass");
// Digest Authentication
cli.set_digest_auth("user", "pass");
```
NOTE: OpenSSL is required for Digest Authentication.
### Proxy server support
```cpp
httplib::Client cli("httplib.org");
cli.set_auth("user", "pass");
cli.set_proxy("host", port);
// Basic
auto res = cli.Get("/basic-auth/user/pass");
// res->status should be 200
// res->body should be "{\n \"authenticated\": true, \n \"user\": \"user\"\n}\n".
// Basic Authentication
cli.set_proxy_basic_auth("user", "pass");
// Digest
res = cli.Get("/digest-auth/auth/user/pass/SHA-256");
// res->status should be 200
// res->body should be "{\n \"authenticated\": true, \n \"user\": \"user\"\n}\n".
// Digest Authentication
cli.set_proxy_digest_auth("user", "pass");
```
NOTE: OpenSSL is required for Digest Authentication.
### Range
```cpp
@@ -450,7 +457,7 @@ httplib.h httplib.cc
NOTE
----
g++ 4.8 cannot build this library since `<regex>` in g++4.8 is [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
License
-------

428
httplib.h
View File

@@ -190,8 +190,6 @@ struct ci {
} // namespace detail
enum class HttpVersion { v1_0 = 0, v1_1 };
using Headers = std::multimap<std::string, std::string, detail::ci>;
using Params = std::multimap<std::string, std::string>;
@@ -505,12 +503,13 @@ public:
};
#endif
using Logger = std::function<void(const Request &, const Response &)>;
class Server {
public:
using Handler = std::function<void(const Request &, Response &)>;
using HandlerWithContentReader = std::function<void(
const Request &, Response &, const ContentReader &content_reader)>;
using Logger = std::function<void(const Request &, const Response &)>;
Server();
@@ -614,7 +613,9 @@ private:
class Client {
public:
explicit Client(const char *host, int port = 80, time_t timeout_sec = 300);
explicit Client(const std::string &host, int port = 80,
const std::string &client_cert_path = std::string(),
const std::string &client_key_path = std::string());
virtual ~Client();
@@ -734,11 +735,15 @@ public:
bool send(const std::vector<Request> &requests,
std::vector<Response> &responses);
void set_keep_alive_max_count(size_t count);
void set_timeout_sec(time_t timeout_sec);
void set_read_timeout(time_t sec, time_t usec);
void set_auth(const char *username, const char *password);
void set_keep_alive_max_count(size_t count);
void set_basic_auth(const char *username, const char *password);
void set_digest_auth(const char *username, const char *password);
void set_follow_location(bool on);
@@ -746,23 +751,76 @@ public:
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_digest_auth(const char *username, const char *password);
void set_logger(Logger logger);
protected:
bool process_request(Stream &strm, const Request &req, Response &res,
bool last_connection, bool &connection_close);
const std::string host_;
const int port_;
time_t timeout_sec_;
const std::string host_and_port_;
size_t keep_alive_max_count_;
time_t read_timeout_sec_;
time_t read_timeout_usec_;
bool follow_location_;
std::string username_;
std::string password_;
bool compress_;
// Settings
std::string client_cert_path_;
std::string client_key_path_;
time_t timeout_sec_ = 300;
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
std::string basic_auth_username_;
std::string basic_auth_password_;
std::string digest_auth_username_;
std::string digest_auth_password_;
bool follow_location_ = false;
bool compress_ = false;
std::string interface_;
std::string proxy_host_;
int proxy_port_;
std::string proxy_basic_auth_username_;
std::string proxy_basic_auth_password_;
std::string proxy_digest_auth_username_;
std::string proxy_digest_auth_password_;
Logger logger_;
void copy_settings(const Client &rhs) {
client_cert_path_ = rhs.client_cert_path_;
client_key_path_ = rhs.client_key_path_;
timeout_sec_ = rhs.timeout_sec_;
read_timeout_sec_ = rhs.read_timeout_sec_;
read_timeout_usec_ = rhs.read_timeout_usec_;
keep_alive_max_count_ = rhs.keep_alive_max_count_;
basic_auth_username_ = rhs.basic_auth_username_;
basic_auth_password_ = rhs.basic_auth_password_;
digest_auth_username_ = rhs.digest_auth_username_;
digest_auth_password_ = rhs.digest_auth_password_;
follow_location_ = rhs.follow_location_;
compress_ = rhs.compress_;
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_digest_auth_username_ = rhs.proxy_digest_auth_username_;
proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
logger_ = rhs.logger_;
}
private:
socket_t create_client_socket() const;
bool read_response_line(Stream &strm, Response &res);
@@ -852,9 +910,9 @@ private:
class SSLClient : public Client {
public:
SSLClient(const char *host, int port = 443, time_t timeout_sec = 300,
const char *client_cert_path = nullptr,
const char *client_key_path = nullptr);
SSLClient(const std::string &host, int port = 443,
const std::string &client_cert_path = std::string(),
const std::string &client_key_path = std::string());
virtual ~SSLClient();
@@ -862,6 +920,7 @@ public:
void set_ca_cert_path(const char *ca_ceert_file_path,
const char *ca_cert_dir_path = nullptr);
void enable_server_certificate_verification(bool enabled);
long get_openssl_verify_result() const;
@@ -884,6 +943,7 @@ private:
SSL_CTX *ctx_;
std::mutex ctx_mutex_;
std::vector<std::string> host_components_;
std::string ca_cert_file_path_;
std::string ca_cert_dir_path_;
bool server_certificate_verification_ = false;
@@ -1228,10 +1288,9 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
}
template <typename T>
inline bool process_and_close_socket(bool is_client_request, socket_t sock,
size_t keep_alive_max_count,
time_t read_timeout_sec,
time_t read_timeout_usec, T callback) {
inline bool process_socket(bool is_client_request, socket_t sock,
size_t keep_alive_max_count, time_t read_timeout_sec,
time_t read_timeout_usec, T callback) {
assert(keep_alive_max_count > 0);
bool ret = false;
@@ -1257,6 +1316,16 @@ inline bool process_and_close_socket(bool is_client_request, socket_t sock,
ret = callback(strm, true, dummy_connection_close);
}
return ret;
}
template <typename T>
inline bool process_and_close_socket(bool is_client_request, socket_t sock,
size_t keep_alive_max_count,
time_t read_timeout_sec,
time_t read_timeout_usec, T callback) {
auto ret = process_socket(is_client_request, sock, keep_alive_max_count,
read_timeout_sec, read_timeout_usec, callback);
close_socket(sock);
return ret;
}
@@ -1302,6 +1371,23 @@ socket_t create_socket(const char *host, int port, Fn fn,
#ifdef _WIN32
auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol,
nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT);
/**
* Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1
* and above the socket creation fails on older Windows Systems.
*
* Let's try to create a socket the old way in this case.
*
* Reference:
* https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa
*
* WSA_FLAG_NO_HANDLE_INHERIT:
* This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with
* SP1, and later
*
*/
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
@@ -1475,6 +1561,7 @@ inline const char *find_content_type(const std::string &path) {
inline const char *status_message(int status) {
switch (status) {
case 200: return "OK";
case 204: return "No Content";
case 206: return "Partial Content";
case 301: return "Moved Permanently";
case 302: return "Found";
@@ -1858,17 +1945,12 @@ write_content_chunked(Stream &strm,
template <typename T>
inline bool redirect(T &cli, const Request &req, Response &res,
const std::string &path) {
Request new_req;
new_req.method = req.method;
Request new_req = req;
new_req.path = path;
new_req.headers = req.headers;
new_req.body = req.body;
new_req.redirect_count = req.redirect_count - 1;
new_req.response_handler = req.response_handler;
new_req.content_receiver = req.content_receiver;
new_req.progress = req.progress;
new_req.redirect_count -= 1;
Response new_res;
auto ret = cli.send(new_req, new_res);
if (ret) { res = new_res; }
return ret;
@@ -2394,16 +2476,17 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
inline std::pair<std::string, std::string>
make_basic_authentication_header(const std::string &username,
const std::string &password) {
const std::string &password, bool is_proxy = false) {
auto field = "Basic " + detail::base64_encode(username + ":" + password);
return std::make_pair("Authorization", field);
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
}
#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) {
const std::string &password, bool is_proxy = false) {
using namespace std;
string nc;
@@ -2420,10 +2503,11 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
qop = "auth";
}
std::string algo = "MD5";
if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
string response;
{
auto algo = auth.at("algorithm");
auto H = algo == "SHA-256"
? detail::SHA_256
: algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
@@ -2439,25 +2523,26 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") +
"\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
"\", algorithm=" + auth.at("algorithm") + ", qop=" + qop +
", nc=\"" + nc + "\", cnonce=\"" + cnonce + "\", response=\"" +
response + "\"";
"\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + nc +
"\", cnonce=\"" + cnonce + "\", response=\"" + response + "\"";
return make_pair("Authorization", field);
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
}
#endif
inline int
parse_www_authenticate(const httplib::Response &res,
std::map<std::string, std::string> &digest_auth) {
if (res.has_header("WWW-Authenticate")) {
inline bool parse_www_authenticate(const httplib::Response &res,
std::map<std::string, std::string> &auth,
bool is_proxy) {
auto key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
if (res.has_header(key)) {
static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
auto s = res.get_header_value("WWW-Authenticate");
auto s = res.get_header_value(key);
auto pos = s.find(' ');
if (pos != std::string::npos) {
auto type = s.substr(0, pos);
if (type == "Basic") {
return 1;
return false;
} else if (type == "Digest") {
s = s.substr(pos + 1);
auto beg = std::sregex_iterator(s.begin(), s.end(), re);
@@ -2466,13 +2551,13 @@ parse_www_authenticate(const httplib::Response &res,
auto key = s.substr(m.position(1), m.length(1));
auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2))
: s.substr(m.position(3), m.length(3));
digest_auth[key] = val;
auth[key] = val;
}
return 2;
return true;
}
}
}
return 0;
return false;
}
// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240
@@ -3355,19 +3440,22 @@ inline bool Server::process_and_close_socket(socket_t sock) {
}
// HTTP client implementation
inline Client::Client(const char *host, int port, time_t timeout_sec)
: host_(host), port_(port), timeout_sec_(timeout_sec),
inline Client::Client(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_)),
keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
follow_location_(false), compress_(false) {}
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
inline Client::~Client() {}
inline bool Client::is_valid() const { return true; }
inline socket_t Client::create_client_socket() const {
if (!proxy_host_.empty()) {
return detail::create_client_socket(proxy_host_.c_str(), proxy_port_,
timeout_sec_, interface_);
}
return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
interface_);
}
@@ -3396,54 +3484,99 @@ inline bool Client::send(const Request &req, Response &res) {
auto sock = create_client_socket();
if (sock == INVALID_SOCKET) { return false; }
auto ret = process_and_close_socket(
sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) {
return process_request(strm, req, res, last_connection,
connection_close);
});
if (ret && follow_location_ && (300 < res.status && res.status < 400)) {
ret = redirect(req, res);
}
if (ret && !username_.empty() && !password_.empty() && res.status == 401) {
int type;
std::map<std::string, std::string> digest_auth;
if ((type = parse_www_authenticate(res, digest_auth)) > 0) {
std::pair<std::string, std::string> header;
if (type == 1) {
header = make_basic_authentication_header(username_, password_);
} else if (type == 2) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
size_t cnonce_count = 1;
auto cnonce = random_string(10);
// CONNECT
if (is_ssl() && !proxy_host_.empty()) {
Response res2;
if (!detail::process_socket(
true, sock, 1, read_timeout_sec_, read_timeout_usec_,
[&](Stream &strm, bool /*last_connection*/,
bool &connection_close) {
Request req2;
req2.method = "CONNECT";
req2.path = host_and_port_;
return process_request(strm, req2, res2, false, connection_close);
})) {
return false;
}
header = make_digest_authentication_header(
req, digest_auth, cnonce_count, cnonce, username_, password_);
#endif
if (res2.status == 407 && !proxy_digest_auth_username_.empty() &&
!proxy_digest_auth_password_.empty()) {
std::map<std::string, std::string> auth;
if (parse_www_authenticate(res2, auth, true)) {
detail::close_socket(sock);
sock = create_client_socket();
if (sock == INVALID_SOCKET) { return false; }
Response res2;
if (!detail::process_socket(
true, sock, 1, read_timeout_sec_, read_timeout_usec_,
[&](Stream &strm, bool /*last_connection*/,
bool &connection_close) {
Request req2;
req2.method = "CONNECT";
req2.path = host_and_port_;
req2.headers.insert(make_digest_authentication_header(
req2, auth, 1, random_string(10),
proxy_digest_auth_username_, proxy_digest_auth_password_,
true));
return process_request(strm, req2, res2, false,
connection_close);
})) {
return false;
}
}
Request new_req;
new_req.method = req.method;
new_req.path = req.path;
new_req.headers = req.headers;
new_req.body = req.body;
new_req.response_handler = req.response_handler;
new_req.content_receiver = req.content_receiver;
new_req.progress = req.progress;
new_req.headers.insert(header);
Response new_res;
auto ret = send(new_req, new_res);
if (ret) { res = new_res; }
return ret;
}
}
#endif
return ret;
if (!process_and_close_socket(
sock, 1,
[&](Stream &strm, bool last_connection, bool &connection_close) {
if (!is_ssl() && !proxy_host_.empty()) {
auto req2 = req;
req2.path = "http://" + host_and_port_ + req.path;
return process_request(strm, req2, res, last_connection,
connection_close);
}
return process_request(strm, req, res, last_connection,
connection_close);
})) {
return false;
}
if (300 < res.status && res.status < 400 && follow_location_) {
return redirect(req, res);
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (res.status == 401 || res.status == 407) {
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 (parse_www_authenticate(res, auth, is_proxy)) {
Request new_req = req;
auto key = is_proxy ? "Proxy-Authorization" : "WWW-Authorization";
new_req.headers.erase(key);
new_req.headers.insert(make_digest_authentication_header(
req, auth, 1, random_string(10), username, password, is_proxy));
Response new_res;
auto ret = send(new_req, new_res);
if (ret) { res = new_res; }
return ret;
}
}
}
#endif
return true;
}
inline bool Client::send(const std::vector<Request> &requests,
@@ -3493,28 +3626,30 @@ inline bool Client::redirect(const Request &req, Response &res) {
std::smatch m;
if (!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 next_path = m[3].str();
if (next_scheme.empty()) { next_scheme = scheme; }
if (next_scheme.empty()) { next_scheme = scheme; }
if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; }
auto scheme = is_ssl() ? "https" : "http";
if (next_scheme == scheme && next_host == host_) {
return detail::redirect(*this, req, res, next_path);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str());
cli.set_follow_location(true);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, next_path);
#else
return false;
#endif
} else {
Client cli(next_host.c_str());
cli.set_follow_location(true);
cli.copy_settings(*this);
return detail::redirect(cli, req, res, next_path);
}
}
@@ -3526,7 +3661,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
// Request line
const static std::regex re(
R"(^([^:/?#]+://[^/?#]*)?([^?#]*(?:\?[^#]*)?(?:#.*)?))");
R"(^((?:[^:/?#]+://)?(?:[^/?#]*)?)?([^?#]*(?:\?[^#]*)?(?:#.*)?))");
std::smatch m;
if (!regex_match(req.path, m, re)) { return false; }
@@ -3558,7 +3693,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
if (!req.has_header("User-Agent")) {
headers.emplace("User-Agent", "cpp-httplib/0.2");
headers.emplace("User-Agent", "cpp-httplib/0.5");
}
if (req.body.empty()) {
@@ -3579,6 +3714,17 @@ inline bool Client::write_request(Stream &strm, const Request &req,
}
}
if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) {
headers.insert(make_basic_authentication_header(
basic_auth_username_, basic_auth_password_, false));
}
if (!proxy_basic_auth_username_.empty() &&
!proxy_basic_auth_password_.empty()) {
headers.insert(make_basic_authentication_header(
proxy_basic_auth_username_, proxy_basic_auth_password_, true));
}
detail::write_headers(bstrm, req, headers);
// Flush buffer
@@ -3671,7 +3817,7 @@ inline bool Client::process_request(Stream &strm, const Request &req,
}
// Body
if (req.method != "HEAD") {
if (req.method != "HEAD" && req.method != "CONNECT") {
ContentReceiver out = [&](const char *buf, size_t n) {
if (res.body.size() + n > res.body.max_size()) { return false; }
res.body.append(buf, n);
@@ -3691,6 +3837,9 @@ inline bool Client::process_request(Stream &strm, const Request &req,
}
}
// Log
if (logger_) { logger_(req, res); }
return true;
}
@@ -3708,8 +3857,7 @@ inline bool Client::process_and_close_socket(
inline bool Client::is_ssl() const { return false; }
inline std::shared_ptr<Response> Client::Get(const char *path) {
Progress dummy;
return Get(path, Headers(), dummy);
return Get(path, Headers(), Progress());
}
inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3719,8 +3867,7 @@ inline std::shared_ptr<Response> Client::Get(const char *path,
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers) {
Progress dummy;
return Get(path, headers, dummy);
return Get(path, headers, Progress());
}
inline std::shared_ptr<Response>
@@ -3737,37 +3884,33 @@ Client::Get(const char *path, const Headers &headers, Progress progress) {
inline std::shared_ptr<Response> Client::Get(const char *path,
ContentReceiver content_receiver) {
Progress dummy;
return Get(path, Headers(), nullptr, std::move(content_receiver), dummy);
return Get(path, Headers(), nullptr, std::move(content_receiver), Progress());
}
inline std::shared_ptr<Response> Client::Get(const char *path,
ContentReceiver content_receiver,
Progress progress) {
return Get(path, Headers(), nullptr, std::move(content_receiver), progress);
return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress));
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ContentReceiver content_receiver) {
Progress dummy;
return Get(path, headers, nullptr, std::move(content_receiver), dummy);
return Get(path, headers, nullptr, std::move(content_receiver), Progress());
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ContentReceiver content_receiver,
Progress progress) {
return Get(path, headers, nullptr, std::move(content_receiver), progress);
return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress));
}
inline std::shared_ptr<Response> Client::Get(const char *path,
const Headers &headers,
ResponseHandler response_handler,
ContentReceiver content_receiver) {
Progress dummy;
return Get(path, headers, std::move(response_handler), content_receiver,
dummy);
return Get(path, headers, std::move(response_handler), content_receiver, Progress());
}
inline std::shared_ptr<Response> Client::Get(const char *path,
@@ -3988,8 +4131,8 @@ inline std::shared_ptr<Response> Client::Options(const char *path,
return send(req, *res) ? res : nullptr;
}
inline void Client::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
inline void Client::set_timeout_sec(time_t timeout_sec) {
timeout_sec_ = timeout_sec;
}
inline void Client::set_read_timeout(time_t sec, time_t usec) {
@@ -3997,9 +4140,19 @@ inline void Client::set_read_timeout(time_t sec, time_t usec) {
read_timeout_usec_ = usec;
}
inline void Client::set_auth(const char *username, const char *password) {
username_ = username;
password_ = password;
inline void Client::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
}
inline void Client::set_basic_auth(const char *username, const char *password) {
basic_auth_username_ = username;
basic_auth_password_ = password;
}
inline void Client::set_digest_auth(const char *username,
const char *password) {
digest_auth_username_ = username;
digest_auth_password_ = password;
}
inline void Client::set_follow_location(bool on) { follow_location_ = on; }
@@ -4008,6 +4161,25 @@ inline void Client::set_compress(bool on) { compress_ = on; }
inline void Client::set_interface(const char *intf) { interface_ = intf; }
inline void Client::set_proxy(const char *host, int port) {
proxy_host_ = host;
proxy_port_ = port;
}
inline void Client::set_proxy_basic_auth(const char *username,
const char *password) {
proxy_basic_auth_username_ = username;
proxy_basic_auth_password_ = password;
}
inline void Client::set_proxy_digest_auth(const char *username,
const char *password) {
proxy_digest_auth_username_ = username;
proxy_digest_auth_password_ = password;
}
inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); }
/*
* SSL Implementation
*/
@@ -4227,21 +4399,21 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
}
// SSL HTTP client implementation
inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec,
const char *client_cert_path,
const char *client_key_path)
: Client(host, port, timeout_sec) {
inline SSLClient::SSLClient(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
: Client(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 && client_key_path) {
if (SSL_CTX_use_certificate_file(ctx_, client_cert_path,
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, 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;
}

View File

@@ -204,15 +204,15 @@ TEST(ParseHeaderValueTest, Range) {
TEST(ChunkedEncodingTest, FromHTTPWatch) {
auto host = "www.httpwatch.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(2);
auto res =
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137");
@@ -227,15 +227,15 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
TEST(ChunkedEncodingTest, WithContentReceiver) {
auto host = "www.httpwatch.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(2);
std::string body;
auto res =
@@ -255,15 +255,15 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
auto host = "www.httpwatch.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(2);
std::string body;
auto res = cli.Get(
@@ -287,15 +287,15 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
TEST(RangeTest, FromHTTPBin) {
auto host = "httpbin.org";
auto sec = 5;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(5);
{
httplib::Headers headers;
@@ -347,15 +347,15 @@ TEST(RangeTest, FromHTTPBin) {
TEST(ConnectionErrorTest, InvalidHost) {
auto host = "-abcde.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(2);
auto res = cli.Get("/");
ASSERT_TRUE(res == nullptr);
@@ -363,15 +363,15 @@ TEST(ConnectionErrorTest, InvalidHost) {
TEST(ConnectionErrorTest, InvalidPort) {
auto host = "localhost";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 44380;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 8080;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(2);
auto res = cli.Get("/");
ASSERT_TRUE(res == nullptr);
@@ -379,15 +379,15 @@ TEST(ConnectionErrorTest, InvalidPort) {
TEST(ConnectionErrorTest, Timeout) {
auto host = "google.com";
auto sec = 2;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 44380;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 8080;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(2);
auto res = cli.Get("/");
ASSERT_TRUE(res == nullptr);
@@ -395,15 +395,15 @@ TEST(ConnectionErrorTest, Timeout) {
TEST(CancelTest, NoCancel) {
auto host = "httpbin.org";
auto sec = 5;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(5);
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
ASSERT_TRUE(res != nullptr);
@@ -413,31 +413,31 @@ TEST(CancelTest, NoCancel) {
TEST(CancelTest, WithCancelSmallPayload) {
auto host = "httpbin.org";
auto sec = 5;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
cli.set_timeout_sec(5);
ASSERT_TRUE(res == nullptr);
}
TEST(CancelTest, WithCancelLargePayload) {
auto host = "httpbin.org";
auto sec = 5;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
httplib::SSLClient cli(host, port, sec);
httplib::SSLClient cli(host, port);
#else
auto port = 80;
httplib::Client cli(host, port, sec);
httplib::Client cli(host, port);
#endif
cli.set_timeout_sec(5);
uint32_t count = 0;
httplib::Headers headers;
@@ -474,13 +474,27 @@ TEST(BaseAuthTest, FromHTTPWatch) {
}
{
cli.set_auth("hello", "world");
cli.set_basic_auth("hello", "world");
auto res = cli.Get("/basic-auth/hello/world");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(res->body,
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
EXPECT_EQ(200, res->status);
}
{
cli.set_basic_auth("hello", "bad");
auto res = cli.Get("/basic-auth/hello/world");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(401, res->status);
}
{
cli.set_basic_auth("bad", "world");
auto res = cli.Get("/basic-auth/hello/world");
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(401, res->status);
}
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -504,7 +518,7 @@ TEST(DigestAuthTest, FromHTTPWatch) {
"/digest-auth/auth-int/hello/world/MD5",
};
cli.set_auth("hello", "world");
cli.set_digest_auth("hello", "world");
for (auto path : paths) {
auto res = cli.Get(path.c_str());
ASSERT_TRUE(res != nullptr);
@@ -512,6 +526,20 @@ TEST(DigestAuthTest, FromHTTPWatch) {
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
EXPECT_EQ(200, res->status);
}
cli.set_digest_auth("hello", "bad");
for (auto path : paths) {
auto res = cli.Get(path.c_str());
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(400, res->status);
}
cli.set_digest_auth("bad", "world");
for (auto path : paths) {
auto res = cli.Get(path.c_str());
ASSERT_TRUE(res != nullptr);
EXPECT_EQ(400, res->status);
}
}
}
#endif
@@ -2090,9 +2118,10 @@ TEST(SSLClientServerTest, ClientCertPresent) {
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
msleep(1);
httplib::SSLClient cli(HOST, PORT, 30, CLIENT_CERT_FILE,
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE,
CLIENT_PRIVATE_KEY_FILE);
auto res = cli.Get("/test");
cli.set_timeout_sec(30);
ASSERT_TRUE(res != nullptr);
ASSERT_EQ(200, res->status);
@@ -2109,8 +2138,9 @@ TEST(SSLClientServerTest, ClientCertMissing) {
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
msleep(1);
httplib::SSLClient cli(HOST, PORT, 30);
httplib::SSLClient cli(HOST, PORT);
auto res = cli.Get("/test");
cli.set_timeout_sec(30);
ASSERT_TRUE(res == nullptr);
svr.stop();
@@ -2130,9 +2160,10 @@ TEST(SSLClientServerTest, TrustDirOptional) {
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
msleep(1);
httplib::SSLClient cli(HOST, PORT, 30, CLIENT_CERT_FILE,
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE,
CLIENT_PRIVATE_KEY_FILE);
auto res = cli.Get("/test");
cli.set_timeout_sec(30);
ASSERT_TRUE(res != nullptr);
ASSERT_EQ(200, res->status);