mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b845425cd0 | |||
| 89519c88e2 | |||
| ff813bf99d | |||
| cf475bcb50 | |||
| bc80d7c789 | |||
| b7566f6961 | |||
| 0542fdb8e4 | |||
| 78c474c744 | |||
| 88411a1f52 | |||
| ae6cf70bc4 |
@@ -7,7 +7,7 @@ A C++11 single-file header-only cross platform HTTP/HTTPS library.
|
||||
|
||||
It's extremely easy to setup. Just include the **httplib.h** file in your code!
|
||||
|
||||
NOTE: This is a 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
|
||||
NOTE: This is a multi-threaded 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
|
||||
|
||||
Simple examples
|
||||
---------------
|
||||
@@ -177,15 +177,28 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
});
|
||||
```
|
||||
|
||||
### Exception handler
|
||||
The exception handler gets called if a user routing handler throws an error.
|
||||
|
||||
```cpp
|
||||
svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
|
||||
res.status = 500;
|
||||
auto fmt = "<h1>Error 500</h1><p>%s</p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||
res.set_content(buf, "text/html");
|
||||
});
|
||||
```
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
```cpp
|
||||
svr.set_pre_routing_handler([](const auto& req, auto& res) -> bool {
|
||||
if (req.path == "/hello") {
|
||||
res.set_content("world", "text/html");
|
||||
return true; // This request is handled
|
||||
return Server::HandlerResponse::Handled;
|
||||
}
|
||||
return false; // Let the router handle this request
|
||||
return Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ using socket_t = int;
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
#include <set>
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include <openssl/err.h>
|
||||
@@ -390,6 +391,9 @@ struct Request {
|
||||
Match matches;
|
||||
|
||||
// for client
|
||||
ResponseHandler response_handler;
|
||||
ContentReceiverWithProgress content_receiver;
|
||||
Progress progress;
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
const SSL *ssl;
|
||||
#endif
|
||||
@@ -413,12 +417,9 @@ struct Request {
|
||||
|
||||
// private members...
|
||||
size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;
|
||||
ResponseHandler response_handler_;
|
||||
ContentReceiverWithProgress content_receiver_;
|
||||
size_t content_length_ = 0;
|
||||
ContentProvider content_provider_;
|
||||
bool is_chunked_content_provider_ = false;
|
||||
Progress progress_;
|
||||
size_t authorization_count_ = 0;
|
||||
};
|
||||
|
||||
@@ -598,6 +599,9 @@ 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,
|
||||
@@ -652,6 +656,7 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
@@ -762,6 +767,7 @@ private:
|
||||
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_;
|
||||
@@ -789,8 +795,11 @@ enum Error {
|
||||
|
||||
class Result {
|
||||
public:
|
||||
Result(std::unique_ptr<Response> res, Error err)
|
||||
: res_(std::move(res)), err_(err) {}
|
||||
Result(std::unique_ptr<Response> &&res, Error err,
|
||||
Headers &&request_headers = Headers{})
|
||||
: res_(std::move(res)), err_(err),
|
||||
request_headers_(std::move(request_headers)) {}
|
||||
// Response
|
||||
operator bool() const { return res_ != nullptr; }
|
||||
bool operator==(std::nullptr_t) const { return res_ == nullptr; }
|
||||
bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
|
||||
@@ -800,11 +809,21 @@ public:
|
||||
Response &operator*() { return *res_; }
|
||||
const Response *operator->() const { return res_.get(); }
|
||||
Response *operator->() { return res_.get(); }
|
||||
|
||||
// Error
|
||||
Error error() const { return err_; }
|
||||
|
||||
// Request Headers
|
||||
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 {
|
||||
@@ -934,7 +953,7 @@ public:
|
||||
Result Options(const char *path);
|
||||
Result Options(const char *path, const Headers &headers);
|
||||
|
||||
bool send(const Request &req, Response &res, Error &error);
|
||||
bool send(Request &req, Response &res, Error &error);
|
||||
Result send(const Request &req);
|
||||
|
||||
size_t is_socket_open() const;
|
||||
@@ -988,6 +1007,8 @@ protected:
|
||||
bool is_open() const { return sock != INVALID_SOCKET; }
|
||||
};
|
||||
|
||||
Result send_(Request &&req);
|
||||
|
||||
virtual bool create_and_connect_socket(Socket &socket, Error &error);
|
||||
|
||||
// All of:
|
||||
@@ -1005,7 +1026,7 @@ protected:
|
||||
// concurrently with a DIFFERENT thread sending requests from the socket
|
||||
void lock_socket_and_shutdown_and_close();
|
||||
|
||||
bool process_request(Stream &strm, const Request &req, Response &res,
|
||||
bool process_request(Stream &strm, Request &req, Response &res,
|
||||
bool close_connection, Error &error);
|
||||
|
||||
bool write_content_with_provider(Stream &strm, const Request &req,
|
||||
@@ -1081,13 +1102,14 @@ protected:
|
||||
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, const Request &req, bool close_connection,
|
||||
bool write_request(Stream &strm, Request &req, bool close_connection,
|
||||
Error &error);
|
||||
bool redirect(const Request &req, Response &res, Error &error);
|
||||
bool handle_request(Stream &strm, const Request &req, Response &res,
|
||||
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(
|
||||
const char *method, const char *path, const Headers &headers,
|
||||
Request &req,
|
||||
// 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, Error &error);
|
||||
@@ -1233,7 +1255,7 @@ public:
|
||||
Result Options(const char *path);
|
||||
Result Options(const char *path, const Headers &headers);
|
||||
|
||||
bool send(const Request &req, Response &res, Error &error);
|
||||
bool send(Request &req, Response &res, Error &error);
|
||||
Result send(const Request &req);
|
||||
|
||||
size_t is_socket_open() const;
|
||||
@@ -2484,7 +2506,7 @@ public:
|
||||
strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
|
||||
|
||||
ret = deflate(&strm_, flush);
|
||||
assert(ret != Z_STREAM_ERROR);
|
||||
if (ret == Z_STREAM_ERROR) { return false; }
|
||||
|
||||
if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
|
||||
return false;
|
||||
@@ -2917,17 +2939,8 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ssize_t write_headers(Stream &strm, const T &info,
|
||||
const Headers &headers) {
|
||||
inline ssize_t write_headers(Stream &strm, const Headers &headers) {
|
||||
ssize_t write_len = 0;
|
||||
for (const auto &x : info.headers) {
|
||||
if (x.first == "EXCEPTION_WHAT") { continue; }
|
||||
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;
|
||||
}
|
||||
for (const auto &x : headers) {
|
||||
auto len =
|
||||
strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
|
||||
@@ -3114,7 +3127,7 @@ inline bool write_content_chunked(Stream &strm,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool redirect(T &cli, const Request &req, Response &res,
|
||||
inline bool redirect(T &cli, Request &req, Response &res,
|
||||
const std::string &path, const std::string &location,
|
||||
Error &error) {
|
||||
Request new_req = req;
|
||||
@@ -3131,8 +3144,9 @@ inline bool redirect(T &cli, const Request &req, Response &res,
|
||||
|
||||
auto ret = cli.send(new_req, new_res, error);
|
||||
if (ret) {
|
||||
new_res.location = location;
|
||||
req = new_req;
|
||||
res = new_res;
|
||||
res.location = location;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -3158,7 +3172,14 @@ inline std::string append_query_params(const char *path, const Params ¶ms) {
|
||||
}
|
||||
|
||||
inline void parse_query_text(const std::string &s, Params ¶ms) {
|
||||
std::set<std::string> cache;
|
||||
split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
|
||||
std::string kv(b, e);
|
||||
if (cache.find(kv) != cache.end()) {
|
||||
return;
|
||||
}
|
||||
cache.insert(kv);
|
||||
|
||||
std::string key;
|
||||
std::string val;
|
||||
split(b, e, '=', [&](const char *b2, const char *e2) {
|
||||
@@ -3762,7 +3783,7 @@ inline std::string random_string(size_t length) {
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[static_cast<size_t>(rand()) % max_index];
|
||||
return charset[static_cast<size_t>(std::rand()) % max_index];
|
||||
};
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
@@ -3973,7 +3994,27 @@ inline void Response::set_chunked_content_provider(
|
||||
is_chunked_content_provider_ = true;
|
||||
}
|
||||
|
||||
// Rstream implementation
|
||||
// Result implementation
|
||||
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));
|
||||
}
|
||||
|
||||
// Stream implementation
|
||||
inline ssize_t Stream::write(const char *ptr) {
|
||||
return write(ptr, strlen(ptr));
|
||||
}
|
||||
@@ -4281,6 +4322,11 @@ inline Server &Server::set_error_handler(Handler handler) {
|
||||
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;
|
||||
@@ -4463,7 +4509,7 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
|
||||
if (!detail::write_headers(bstrm, res.headers)) { return false; }
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
@@ -4835,22 +4881,14 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
|
||||
inline bool Server::dispatch_request(Request &req, Response &res,
|
||||
const Handlers &handlers) {
|
||||
try {
|
||||
for (const auto &x : handlers) {
|
||||
const auto &pattern = x.first;
|
||||
const auto &handler = x.second;
|
||||
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;
|
||||
}
|
||||
if (std::regex_match(req.path, req.matches, pattern)) {
|
||||
handler(req, res);
|
||||
return true;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
res.status = 500;
|
||||
res.set_header("EXCEPTION_WHAT", ex.what());
|
||||
} catch (...) {
|
||||
res.status = 500;
|
||||
res.set_header("EXCEPTION_WHAT", "UNKNOWN");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -5064,7 +5102,23 @@ Server::process_request(Stream &strm, bool close_connection,
|
||||
}
|
||||
|
||||
// Rounting
|
||||
if (routing(req, res, strm)) {
|
||||
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 {
|
||||
@@ -5211,7 +5265,7 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
|
||||
|
||||
if (!line_reader.getline()) { return false; }
|
||||
|
||||
const static std::regex re("(HTTP/1\\.[01]) (\\d{3}) (.*?)\r\n");
|
||||
const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
|
||||
|
||||
std::cmatch m;
|
||||
if (!std::regex_match(line_reader.ptr(), m, re)) {
|
||||
@@ -5235,7 +5289,7 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ClientImpl::send(const Request &req, Response &res, Error &error) {
|
||||
inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
|
||||
std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
|
||||
|
||||
{
|
||||
@@ -5288,6 +5342,12 @@ inline bool ClientImpl::send(const Request &req, Response &res, Error &error) {
|
||||
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);
|
||||
@@ -5318,13 +5378,18 @@ inline bool ClientImpl::send(const Request &req, Response &res, Error &error) {
|
||||
}
|
||||
|
||||
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};
|
||||
return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};
|
||||
}
|
||||
|
||||
inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
|
||||
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
|
||||
Response &res, bool close_connection,
|
||||
Error &error) {
|
||||
if (req.path.empty()) {
|
||||
@@ -5332,12 +5397,16 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
|
||||
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);
|
||||
}
|
||||
@@ -5345,6 +5414,7 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
|
||||
if (!ret) { return false; }
|
||||
|
||||
if (300 < res.status && res.status < 400 && follow_location_) {
|
||||
req = req_save;
|
||||
ret = redirect(req, res, error);
|
||||
}
|
||||
|
||||
@@ -5380,8 +5450,7 @@ inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline bool ClientImpl::redirect(const Request &req, Response &res,
|
||||
Error &error) {
|
||||
inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
||||
if (req.redirect_count_ == 0) {
|
||||
error = Error::ExceedRedirectCount;
|
||||
return false;
|
||||
@@ -5458,75 +5527,74 @@ inline bool ClientImpl::write_content_with_provider(Stream &strm,
|
||||
}
|
||||
} // namespace httplib
|
||||
|
||||
inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
||||
inline bool ClientImpl::write_request(Stream &strm, Request &req,
|
||||
bool close_connection, Error &error) {
|
||||
// Prepare additional headers
|
||||
Headers headers;
|
||||
if (close_connection) { headers.emplace("Connection", "close"); }
|
||||
if (close_connection) { req.headers.emplace("Connection", "close"); }
|
||||
|
||||
if (!req.has_header("Host")) {
|
||||
if (is_ssl()) {
|
||||
if (port_ == 443) {
|
||||
headers.emplace("Host", host_);
|
||||
req.headers.emplace("Host", host_);
|
||||
} else {
|
||||
headers.emplace("Host", host_and_port_);
|
||||
req.headers.emplace("Host", host_and_port_);
|
||||
}
|
||||
} else {
|
||||
if (port_ == 80) {
|
||||
headers.emplace("Host", host_);
|
||||
req.headers.emplace("Host", host_);
|
||||
} else {
|
||||
headers.emplace("Host", host_and_port_);
|
||||
req.headers.emplace("Host", host_and_port_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
|
||||
if (!req.has_header("Accept")) { req.headers.emplace("Accept", "*/*"); }
|
||||
|
||||
if (!req.has_header("User-Agent")) {
|
||||
headers.emplace("User-Agent", "cpp-httplib/0.7");
|
||||
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_);
|
||||
headers.emplace("Content-Length", length);
|
||||
req.headers.emplace("Content-Length", length);
|
||||
}
|
||||
} else {
|
||||
if (req.method == "POST" || req.method == "PUT" ||
|
||||
req.method == "PATCH") {
|
||||
headers.emplace("Content-Length", "0");
|
||||
req.headers.emplace("Content-Length", "0");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!req.has_header("Content-Type")) {
|
||||
headers.emplace("Content-Type", "text/plain");
|
||||
req.headers.emplace("Content-Type", "text/plain");
|
||||
}
|
||||
|
||||
if (!req.has_header("Content-Length")) {
|
||||
auto length = std::to_string(req.body.size());
|
||||
headers.emplace("Content-Length", length);
|
||||
req.headers.emplace("Content-Length", length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!basic_auth_password_.empty()) {
|
||||
headers.insert(make_basic_authentication_header(
|
||||
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()) {
|
||||
headers.insert(make_basic_authentication_header(
|
||||
req.headers.insert(make_basic_authentication_header(
|
||||
proxy_basic_auth_username_, proxy_basic_auth_password_, true));
|
||||
}
|
||||
|
||||
if (!bearer_token_auth_token_.empty()) {
|
||||
headers.insert(make_bearer_token_authentication_header(
|
||||
req.headers.insert(make_bearer_token_authentication_header(
|
||||
bearer_token_auth_token_, false));
|
||||
}
|
||||
|
||||
if (!proxy_bearer_token_auth_token_.empty()) {
|
||||
headers.insert(make_bearer_token_authentication_header(
|
||||
req.headers.insert(make_bearer_token_authentication_header(
|
||||
proxy_bearer_token_auth_token_, true));
|
||||
}
|
||||
|
||||
@@ -5537,7 +5605,7 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
||||
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);
|
||||
detail::write_headers(bstrm, req.headers);
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
@@ -5558,16 +5626,16 @@ inline bool ClientImpl::write_request(Stream &strm, const Request &req,
|
||||
}
|
||||
|
||||
inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
|
||||
const char *method, const char *path, const Headers &headers,
|
||||
Request &req,
|
||||
// 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, Error &error) {
|
||||
|
||||
Request req;
|
||||
req.method = method;
|
||||
req.headers = default_headers_;
|
||||
req.headers.insert(headers.begin(), headers.end());
|
||||
req.path = path;
|
||||
// Request req;
|
||||
// req.method = method;
|
||||
// req.headers = headers;
|
||||
// req.path = path;
|
||||
|
||||
if (content_type) { req.headers.emplace("Content-Type", content_type); }
|
||||
|
||||
@@ -5649,14 +5717,23 @@ inline Result ClientImpl::send_with_content_provider(
|
||||
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(
|
||||
method, path, headers, body, content_length, std::move(content_provider),
|
||||
req,
|
||||
// method, path, headers,
|
||||
body, content_length, std::move(content_provider),
|
||||
std::move(content_provider_without_length), content_type, error);
|
||||
return Result{std::move(res), error};
|
||||
|
||||
return Result{std::move(res), error, std::move(req.headers)};
|
||||
}
|
||||
|
||||
inline bool ClientImpl::process_request(Stream &strm, const Request &req,
|
||||
inline bool ClientImpl::process_request(Stream &strm, Request &req,
|
||||
Response &res, bool close_connection,
|
||||
Error &error) {
|
||||
// Send request
|
||||
@@ -5669,8 +5746,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req.response_handler_) {
|
||||
if (!req.response_handler_(res)) {
|
||||
if (req.response_handler) {
|
||||
if (!req.response_handler(res)) {
|
||||
error = Error::Canceled;
|
||||
return false;
|
||||
}
|
||||
@@ -5679,10 +5756,10 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
|
||||
// Body
|
||||
if ((res.status != 204) && req.method != "HEAD" && req.method != "CONNECT") {
|
||||
auto out =
|
||||
req.content_receiver_
|
||||
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);
|
||||
auto ret = req.content_receiver(buf, n, off, len);
|
||||
if (!ret) { error = Error::Canceled; }
|
||||
return ret;
|
||||
})
|
||||
@@ -5697,8 +5774,8 @@ inline bool ClientImpl::process_request(Stream &strm, const Request &req,
|
||||
});
|
||||
|
||||
auto progress = [&](uint64_t current, uint64_t total) {
|
||||
if (!req.progress_) { return true; }
|
||||
auto ret = req.progress_(current, total);
|
||||
if (!req.progress) { return true; }
|
||||
auto ret = req.progress(current, total);
|
||||
if (!ret) { error = Error::Canceled; }
|
||||
return ret;
|
||||
};
|
||||
@@ -5760,11 +5837,10 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
|
||||
Request req;
|
||||
req.method = "GET";
|
||||
req.path = path;
|
||||
req.headers = default_headers_;
|
||||
req.headers.insert(headers.begin(), headers.end());
|
||||
req.progress_ = std::move(progress);
|
||||
req.headers = headers;
|
||||
req.progress = std::move(progress);
|
||||
|
||||
return send(req);
|
||||
return send_(std::move(req));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Get(const char *path,
|
||||
@@ -5820,17 +5896,16 @@ inline Result ClientImpl::Get(const char *path, const Headers &headers,
|
||||
Request req;
|
||||
req.method = "GET";
|
||||
req.path = path;
|
||||
req.headers = default_headers_;
|
||||
req.headers.insert(headers.begin(), headers.end());
|
||||
req.response_handler_ = std::move(response_handler);
|
||||
req.content_receiver_ =
|
||||
req.headers = headers;
|
||||
req.response_handler = std::move(response_handler);
|
||||
req.content_receiver =
|
||||
[content_receiver](const char *data, size_t data_length,
|
||||
uint64_t /*offset*/, uint64_t /*total_length*/) {
|
||||
return content_receiver(data, data_length);
|
||||
};
|
||||
req.progress_ = std::move(progress);
|
||||
req.progress = std::move(progress);
|
||||
|
||||
return send(req);
|
||||
return send_(std::move(req));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Get(const char *path, const Params ¶ms,
|
||||
@@ -5858,7 +5933,7 @@ inline Result ClientImpl::Get(const char *path, const Params ¶ms,
|
||||
}
|
||||
|
||||
std::string path_with_query = detail::append_query_params(path, params);
|
||||
return Get(path_with_query.c_str(), params, headers, response_handler,
|
||||
return Get(path_with_query.c_str(), headers, response_handler,
|
||||
content_receiver, progress);
|
||||
}
|
||||
|
||||
@@ -5869,11 +5944,10 @@ inline Result ClientImpl::Head(const char *path) {
|
||||
inline Result ClientImpl::Head(const char *path, const Headers &headers) {
|
||||
Request req;
|
||||
req.method = "HEAD";
|
||||
req.headers = default_headers_;
|
||||
req.headers.insert(headers.begin(), headers.end());
|
||||
req.headers = headers;
|
||||
req.path = path;
|
||||
|
||||
return send(req);
|
||||
return send_(std::move(req));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Post(const char *path) {
|
||||
@@ -6133,14 +6207,13 @@ inline Result ClientImpl::Delete(const char *path, const Headers &headers,
|
||||
const char *content_type) {
|
||||
Request req;
|
||||
req.method = "DELETE";
|
||||
req.headers = default_headers_;
|
||||
req.headers.insert(headers.begin(), headers.end());
|
||||
req.headers = headers;
|
||||
req.path = path;
|
||||
|
||||
if (content_type) { req.headers.emplace("Content-Type", content_type); }
|
||||
req.body.assign(body, content_length);
|
||||
|
||||
return send(req);
|
||||
return send_(std::move(req));
|
||||
}
|
||||
|
||||
inline Result ClientImpl::Delete(const char *path, const std::string &body,
|
||||
@@ -6161,11 +6234,10 @@ inline Result ClientImpl::Options(const char *path) {
|
||||
inline Result ClientImpl::Options(const char *path, const Headers &headers) {
|
||||
Request req;
|
||||
req.method = "OPTIONS";
|
||||
req.headers = default_headers_;
|
||||
req.headers.insert(headers.begin(), headers.end());
|
||||
req.headers = headers;
|
||||
req.path = path;
|
||||
|
||||
return send(req);
|
||||
return send_(std::move(req));
|
||||
}
|
||||
|
||||
inline size_t ClientImpl::is_socket_open() const {
|
||||
@@ -7285,7 +7357,7 @@ inline Result Client::Options(const char *path, const Headers &headers) {
|
||||
return cli_->Options(path, headers);
|
||||
}
|
||||
|
||||
inline bool Client::send(const Request &req, Response &res, Error &error) {
|
||||
inline bool Client::send(Request &req, Response &res, Error &error) {
|
||||
return cli_->send(req, res, error);
|
||||
}
|
||||
|
||||
|
||||
+201
-78
@@ -5,6 +5,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
@@ -416,16 +417,16 @@ TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
||||
cli.set_connection_timeout(2);
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get(
|
||||
"/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
|
||||
[&](const Response &response) {
|
||||
EXPECT_EQ(200, response.status);
|
||||
return true;
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
auto res =
|
||||
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
|
||||
[&](const Response &response) {
|
||||
EXPECT_EQ(200, response.status);
|
||||
return true;
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(res);
|
||||
|
||||
std::string out;
|
||||
@@ -548,12 +549,11 @@ TEST(ConnectionErrorTest, InvalidHost2) {
|
||||
|
||||
TEST(ConnectionErrorTest, InvalidPort) {
|
||||
auto host = "localhost";
|
||||
auto port = 44380;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 44380;
|
||||
SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 8080;
|
||||
Client cli(host, port);
|
||||
#endif
|
||||
cli.set_connection_timeout(2);
|
||||
@@ -978,6 +978,42 @@ TEST(ErrorHandlerTest, ContentLength) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(ExceptionHandlerTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
svr.set_exception_handler(
|
||||
[](const Request & /*req*/, Response &res, std::exception & /*e*/) {
|
||||
res.status = 500;
|
||||
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
||||
"text/html"); // <= Content-Length still 13
|
||||
});
|
||||
|
||||
svr.Get("/hi", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content("Hello World!\n", "text/plain");
|
||||
throw std::runtime_error("abc");
|
||||
});
|
||||
|
||||
auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
|
||||
|
||||
// Give GET time to get a few messages.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
{
|
||||
Client cli(HOST, PORT);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(500, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("26", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("abcdefghijklmnopqrstuvwxyz", res->body);
|
||||
}
|
||||
|
||||
svr.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(NoContentTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
@@ -1608,6 +1644,7 @@ TEST_F(ServerTest, GetMethod200) {
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ("HTTP/1.1", res->version);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("OK", res->reason);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ(1, res->get_header_value_count("Content-Type"));
|
||||
EXPECT_EQ("Hello World!", res->body);
|
||||
@@ -2279,7 +2316,8 @@ TEST_F(ServerTest, ClientStop) {
|
||||
auto res = cli_.Get("/streamed-cancel",
|
||||
[&](const char *, uint64_t) { return true; });
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_TRUE(res.error() == Error::Canceled || res.error() == Error::Read);
|
||||
EXPECT_TRUE(res.error() == Error::Canceled ||
|
||||
res.error() == Error::Read || res.error() == Error::Write);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -2424,13 +2462,13 @@ TEST_F(ServerTest, SlowPost) {
|
||||
char buffer[64 * 1024];
|
||||
memset(buffer, 0x42, sizeof(buffer));
|
||||
|
||||
auto res = cli_.Post(
|
||||
"/slowpost", 64 * 1024 * 1024,
|
||||
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(buffer, sizeof(buffer));
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
auto res =
|
||||
cli_.Post("/slowpost", 64 * 1024 * 1024,
|
||||
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(buffer, sizeof(buffer));
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -2441,13 +2479,13 @@ TEST_F(ServerTest, SlowPostFail) {
|
||||
memset(buffer, 0x42, sizeof(buffer));
|
||||
|
||||
cli_.set_write_timeout(0, 0);
|
||||
auto res = cli_.Post(
|
||||
"/slowpost", 64 * 1024 * 1024,
|
||||
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(buffer, sizeof(buffer));
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
auto res =
|
||||
cli_.Post("/slowpost", 64 * 1024 * 1024,
|
||||
[&](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(buffer, sizeof(buffer));
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::Write, res.error());
|
||||
@@ -2461,14 +2499,13 @@ TEST_F(ServerTest, Put) {
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutWithContentProvider) {
|
||||
auto res = cli_.Put(
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
auto res = cli_.Put("/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -2476,27 +2513,24 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostWithContentProviderAbort) {
|
||||
auto res = cli_.Post(
|
||||
"/post", 42,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
|
||||
return false;
|
||||
},
|
||||
"text/plain");
|
||||
auto res = cli_.Post("/post", 42,
|
||||
[](size_t /*offset*/, size_t /*length*/,
|
||||
DataSink & /*sink*/) { return false; },
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutWithContentProviderWithoutLength) {
|
||||
auto res = cli_.Put(
|
||||
"/put",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
auto res = cli_.Put("/put",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -2515,14 +2549,13 @@ TEST_F(ServerTest, PostWithContentProviderWithoutLengthAbort) {
|
||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put(
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
auto res = cli_.Put("/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -2531,12 +2564,10 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
|
||||
TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Post(
|
||||
"/post", 42,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink & /*sink*/) {
|
||||
return false;
|
||||
},
|
||||
"text/plain");
|
||||
auto res = cli_.Post("/post", 42,
|
||||
[](size_t /*offset*/, size_t /*length*/,
|
||||
DataSink & /*sink*/) { return false; },
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(!res);
|
||||
EXPECT_EQ(Error::Canceled, res.error());
|
||||
@@ -2544,15 +2575,14 @@ TEST_F(ServerTest, PostWithContentProviderWithGzipAbort) {
|
||||
|
||||
TEST_F(ServerTest, PutWithContentProviderWithoutLengthWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put(
|
||||
"/put",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
auto res = cli_.Put("/put",
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.os << "PUT";
|
||||
sink.done();
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
@@ -2578,6 +2608,24 @@ TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||
EXPECT_EQ(LARGE_DATA, res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutLargeFileWithGzip2) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
Client cli("https://localhost:1234");
|
||||
cli.enable_server_certificate_verification(false);
|
||||
#else
|
||||
Client cli("http://localhost:1234");
|
||||
#endif
|
||||
cli.set_compress(true);
|
||||
|
||||
auto res = cli.Put("/put-large", LARGE_DATA, "text/plain");
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(LARGE_DATA, res->body);
|
||||
EXPECT_EQ(101942u, res.get_request_header_value<uint64_t>("Content-Length"));
|
||||
EXPECT_EQ("gzip", res.get_request_header_value("Content-Encoding"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutContentWithDeflate) {
|
||||
cli_.set_compress(false);
|
||||
Headers headers;
|
||||
@@ -2854,9 +2902,8 @@ TEST_F(ServerTest, KeepAlive) {
|
||||
EXPECT_EQ("empty", res->body);
|
||||
EXPECT_EQ("close", res->get_header_value("Connection"));
|
||||
|
||||
res = cli_.Post(
|
||||
"/empty", 0, [&](size_t, size_t, DataSink &) { return true; },
|
||||
"text/plain");
|
||||
res = cli_.Post("/empty", 0, [&](size_t, size_t, DataSink &) { return true; },
|
||||
"text/plain");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
@@ -3369,7 +3416,8 @@ TEST(ExceptionTest, ThrowExceptionInHandler) {
|
||||
auto res = cli.Get("/hi");
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(500, res->status);
|
||||
ASSERT_FALSE(res->has_header("EXCEPTION_WHAT"));
|
||||
ASSERT_TRUE(res->has_header("EXCEPTION_WHAT"));
|
||||
EXPECT_EQ("exception...", res->get_header_value("EXCEPTION_WHAT"));
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
@@ -3447,6 +3495,69 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(GetWithParametersTest, GetWithParameters) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [&](const Request & req, Response &res) {
|
||||
auto text = req.get_param_value("hello");
|
||||
res.set_content(text, "text/plain");
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", PORT);
|
||||
|
||||
Params params;
|
||||
params.emplace("hello", "world");
|
||||
auto res = cli.Get("/", params, Headers{});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("world", res->body);
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
TEST(GetWithParametersTest, GetWithParameters2) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [&](const Request & req, Response &res) {
|
||||
auto text = req.get_param_value("hello");
|
||||
res.set_content(text, "text/plain");
|
||||
});
|
||||
|
||||
auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
Client cli("localhost", PORT);
|
||||
|
||||
Params params;
|
||||
params.emplace("hello", "world");
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get("/", params, Headers{}, [&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("world", body);
|
||||
|
||||
svr.stop();
|
||||
listen_thread.join();
|
||||
ASSERT_FALSE(svr.is_running());
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(KeepAliveTest, ReadTimeoutSSL) {
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
@@ -3927,7 +4038,6 @@ TEST(NoSSLSupport, SimpleInterface) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(InvalidScheme, SimpleInterface) {
|
||||
ASSERT_ANY_THROW(Client cli("scheme://yahoo.com"));
|
||||
}
|
||||
@@ -3937,6 +4047,19 @@ TEST(NoScheme, SimpleInterface) {
|
||||
ASSERT_TRUE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(SendAPI, SimpleInterface) {
|
||||
Client cli("http://yahoo.com");
|
||||
|
||||
Request req;
|
||||
req.method = "GET";
|
||||
req.path = "/";
|
||||
auto res = cli.send(req);
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(301, res->status);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
Client cli("http://yahoo.com");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user