Support asynchronous requests with internal I/O context

This commit is contained in:
Jens Moeller 2020-02-18 22:00:02 +00:00
commit 49e2bb9261
2 changed files with 34 additions and 17 deletions

View file

@ -216,6 +216,7 @@ namespace SimpleWeb {
/// If you reuse the io_service for other tasks, use the asynchronous request functions instead. /// If you reuse the io_service for other tasks, use the asynchronous request functions instead.
/// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead. /// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead.
std::shared_ptr<Response> request(const std::string &method, const std::string &path = {"/"}, string_view content = {}, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr<Response> request(const std::string &method, const std::string &path = {"/"}, string_view content = {}, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
start_internal_io_service_handler_thread();
std::promise<std::shared_ptr<Response>> response_promise; std::promise<std::shared_ptr<Response>> response_promise;
std::future<std::shared_ptr<Response>> response_future = response_promise.get_future(); std::future<std::shared_ptr<Response>> response_future = response_promise.get_future();
std::shared_ptr<Response> response; std::shared_ptr<Response> response;
@ -251,6 +252,7 @@ namespace SimpleWeb {
/// If you reuse the io_service for other tasks, use the asynchronous request functions instead. /// If you reuse the io_service for other tasks, use the asynchronous request functions instead.
/// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead. /// When requesting Server-Sent Events: will throw on error::eof, please use asynchronous request functions instead.
std::shared_ptr<Response> request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) { std::shared_ptr<Response> request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
start_internal_io_service_handler_thread();
std::promise<std::shared_ptr<Response>> response_promise; std::promise<std::shared_ptr<Response>> response_promise;
std::future<std::shared_ptr<Response>> response_future = response_promise.get_future(); std::future<std::shared_ptr<Response>> response_future = response_promise.get_future();
std::shared_ptr<Response> response; std::shared_ptr<Response> response;
@ -465,8 +467,7 @@ namespace SimpleWeb {
port = parsed_host_port.second; port = parsed_host_port.second;
} }
std::shared_ptr<Connection> get_connection() noexcept { void start_internal_io_service_handler_thread() noexcept {
std::shared_ptr<Connection> connection;
LockGuard lock(connections_mutex); LockGuard lock(connections_mutex);
if(!io_service) { if(!io_service) {
@ -477,6 +478,15 @@ namespace SimpleWeb {
io_service->run(); io_service->run();
})); }));
} }
}
std::shared_ptr<Connection> get_connection() noexcept {
std::shared_ptr<Connection> connection;
LockGuard lock(connections_mutex);
if(!io_service) {
io_service = std::make_shared<asio::io_service>();
}
for(auto it = connections.begin(); it != connections.end(); ++it) { for(auto it = connections.begin(); it != connections.end(); ++it) {
if(!(*it)->in_use) { if(!(*it)->in_use) {

View file

@ -389,12 +389,16 @@ int main() {
// Test large responses // Test large responses
{ {
HttpClient client("localhost:8080"); // Don't mix synchronous and asynchronous on the same client
client.config.max_response_streambuf_size = 400; HttpClient sync_request_client("localhost:8080");
HttpClient async_request_client("localhost:8080");
sync_request_client.config.max_response_streambuf_size = 400;
async_request_client.config.max_response_streambuf_size = 400;
async_request_client.io_service = std::make_shared<SimpleWeb::io_context>();
{ {
bool thrown = false; bool thrown = false;
try { try {
auto r = client.request("GET", "/long-response"); auto r = sync_request_client.request("GET", "/long-response");
} }
catch(...) { catch(...) {
thrown = true; thrown = true;
@ -405,7 +409,7 @@ int main() {
size_t calls = 0; size_t calls = 0;
bool end = false; bool end = false;
std::string content; std::string content;
client.request("GET", "/long-response", [&calls, &content, &end](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) { async_request_client.request("GET", "/long-response", [&calls, &content, &end](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
ASSERT(!ec); ASSERT(!ec);
content += response->content.string(); content += response->content.string();
calls++; calls++;
@ -413,8 +417,8 @@ int main() {
ASSERT(response->content.end == false); ASSERT(response->content.end == false);
end = response->content.end; end = response->content.end;
}); });
SimpleWeb::restart(*client.io_service); SimpleWeb::restart(*async_request_client.io_service);
client.io_service->run(); async_request_client.io_service->run();
ASSERT(content == long_response); ASSERT(content == long_response);
ASSERT(calls > 2); ASSERT(calls > 2);
ASSERT(end == true); ASSERT(end == true);
@ -422,15 +426,15 @@ int main() {
{ {
size_t calls = 0; size_t calls = 0;
std::string content; std::string content;
client.request("GET", "/long-response", [&calls, &content](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) { async_request_client.request("GET", "/long-response", [&calls, &content](shared_ptr<HttpClient::Response> response, const SimpleWeb::error_code &ec) {
if(calls == 0) if(calls == 0)
ASSERT(!ec); ASSERT(!ec);
content += response->content.string(); content += response->content.string();
calls++; calls++;
response->close(); response->close();
}); });
SimpleWeb::restart(*client.io_service); SimpleWeb::restart(*async_request_client.io_service);
client.io_service->run(); async_request_client.io_service->run();
ASSERT(!content.empty()); ASSERT(!content.empty());
ASSERT(calls >= 2); ASSERT(calls >= 2);
} }
@ -438,12 +442,15 @@ int main() {
// Test client timeout // Test client timeout
{ {
HttpClient client("localhost:8080"); // Don't mix synchronous and asynchronous on the same client
client.config.timeout = 2; HttpClient sync_request_client("localhost:8080");
HttpClient async_request_client("localhost:8080");
sync_request_client.config.timeout = 2;
async_request_client.config.timeout = 2;
{ {
bool thrown = false; bool thrown = false;
try { try {
auto r = client.request("GET", "/work"); auto r = sync_request_client.request("GET", "/work");
} }
catch(...) { catch(...) {
thrown = true; thrown = true;
@ -452,12 +459,12 @@ int main() {
} }
{ {
bool call = false; bool call = false;
client.request("GET", "/work", [&call](shared_ptr<HttpClient::Response> /*response*/, const SimpleWeb::error_code &ec) { async_request_client.request("GET", "/work", [&call](shared_ptr<HttpClient::Response> /*response*/, const SimpleWeb::error_code &ec) {
ASSERT(ec); ASSERT(ec);
call = true; call = true;
}); });
SimpleWeb::restart(*client.io_service); SimpleWeb::restart(*async_request_client.io_service);
client.io_service->run(); async_request_client.io_service->run();
ASSERT(call); ASSERT(call);
} }
} }