Fixes #86: can now set timeout on client requests
This commit is contained in:
parent
2f32a2b52f
commit
8a73cb381a
5 changed files with 77 additions and 30 deletions
|
|
@ -48,6 +48,18 @@ namespace SimpleWeb {
|
||||||
Response(): content(&content_buffer) {}
|
Response(): content(&content_buffer) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
friend class ClientBase<socket_type>;
|
||||||
|
private:
|
||||||
|
Config() {}
|
||||||
|
public:
|
||||||
|
/// Set timeout on requests in seconds. Default value: 0 (no timeout).
|
||||||
|
size_t timeout=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Set before calling request
|
||||||
|
Config config;
|
||||||
|
|
||||||
std::shared_ptr<Response> request(const std::string& request_type, const std::string& path="/", boost::string_ref content="",
|
std::shared_ptr<Response> request(const std::string& request_type, const std::string& path="/", boost::string_ref content="",
|
||||||
const std::map<std::string, std::string>& header=std::map<std::string, std::string>()) {
|
const std::map<std::string, std::string>& header=std::map<std::string, std::string>()) {
|
||||||
std::string corrected_path=path;
|
std::string corrected_path=path;
|
||||||
|
|
@ -67,12 +79,18 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
connect();
|
connect();
|
||||||
|
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_write(*socket, write_buffer,
|
boost::asio::async_write(*socket, write_buffer,
|
||||||
[this, &content](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
[this, &content, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
if(!content.empty()) {
|
if(!content.empty()) {
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_write(*socket, boost::asio::buffer(content.data(), content.size()),
|
boost::asio::async_write(*socket, boost::asio::buffer(content.data(), content.size()),
|
||||||
[this](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
[this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(ec) {
|
if(ec) {
|
||||||
socket=nullptr;
|
socket=nullptr;
|
||||||
throw boost::system::system_error(ec);
|
throw boost::system::system_error(ec);
|
||||||
|
|
@ -114,8 +132,11 @@ namespace SimpleWeb {
|
||||||
if(content_length>0)
|
if(content_length>0)
|
||||||
write_stream << content.rdbuf();
|
write_stream << content.rdbuf();
|
||||||
|
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_write(*socket, write_buffer,
|
boost::asio::async_write(*socket, write_buffer,
|
||||||
[this](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
[this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(ec) {
|
if(ec) {
|
||||||
socket=nullptr;
|
socket=nullptr;
|
||||||
throw boost::system::system_error(ec);
|
throw boost::system::system_error(ec);
|
||||||
|
|
@ -153,6 +174,22 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
virtual void connect()=0;
|
virtual void connect()=0;
|
||||||
|
|
||||||
|
std::shared_ptr<boost::asio::deadline_timer> get_timeout_timer() {
|
||||||
|
if(config.timeout==0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto timer=std::make_shared<boost::asio::deadline_timer>(io_service);
|
||||||
|
timer->expires_from_now(boost::posix_time::seconds(config.timeout));
|
||||||
|
timer->async_wait([this](const boost::system::error_code& ec) {
|
||||||
|
if(!ec) {
|
||||||
|
boost::system::error_code ec;
|
||||||
|
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||||
|
socket->lowest_layer().close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
void parse_response_header(const std::shared_ptr<Response> &response) const {
|
void parse_response_header(const std::shared_ptr<Response> &response) const {
|
||||||
std::string line;
|
std::string line;
|
||||||
getline(response->content, line);
|
getline(response->content, line);
|
||||||
|
|
@ -184,8 +221,11 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
boost::asio::streambuf chunked_streambuf;
|
boost::asio::streambuf chunked_streambuf;
|
||||||
|
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n",
|
boost::asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n",
|
||||||
[this, &response, &chunked_streambuf](const boost::system::error_code& ec, size_t bytes_transferred) {
|
[this, &response, &chunked_streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
size_t num_additional_bytes=response->content_buffer.size()-bytes_transferred;
|
size_t num_additional_bytes=response->content_buffer.size()-bytes_transferred;
|
||||||
|
|
||||||
|
|
@ -195,9 +235,12 @@ namespace SimpleWeb {
|
||||||
if(header_it!=response->header.end()) {
|
if(header_it!=response->header.end()) {
|
||||||
auto content_length=stoull(header_it->second);
|
auto content_length=stoull(header_it->second);
|
||||||
if(content_length>num_additional_bytes) {
|
if(content_length>num_additional_bytes) {
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_read(*socket, response->content_buffer,
|
boost::asio::async_read(*socket, response->content_buffer,
|
||||||
boost::asio::transfer_exactly(content_length-num_additional_bytes),
|
boost::asio::transfer_exactly(content_length-num_additional_bytes),
|
||||||
[this](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
[this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(ec) {
|
if(ec) {
|
||||||
socket=nullptr;
|
socket=nullptr;
|
||||||
throw boost::system::system_error(ec);
|
throw boost::system::system_error(ec);
|
||||||
|
|
@ -221,8 +264,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void request_read_chunked(const std::shared_ptr<Response> &response, boost::asio::streambuf &streambuf) {
|
void request_read_chunked(const std::shared_ptr<Response> &response, boost::asio::streambuf &streambuf) {
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_read_until(*socket, response->content_buffer, "\r\n",
|
boost::asio::async_read_until(*socket, response->content_buffer, "\r\n",
|
||||||
[this, &response, &streambuf](const boost::system::error_code& ec, size_t bytes_transferred) {
|
[this, &response, &streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
std::string line;
|
std::string line;
|
||||||
getline(response->content, line);
|
getline(response->content, line);
|
||||||
|
|
@ -251,9 +297,12 @@ namespace SimpleWeb {
|
||||||
};
|
};
|
||||||
|
|
||||||
if((2+length)>num_additional_bytes) {
|
if((2+length)>num_additional_bytes) {
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
boost::asio::async_read(*socket, response->content_buffer,
|
boost::asio::async_read(*socket, response->content_buffer,
|
||||||
boost::asio::transfer_exactly(2+length-num_additional_bytes),
|
boost::asio::transfer_exactly(2+length-num_additional_bytes),
|
||||||
[this, post_process](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
[this, post_process, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
post_process();
|
post_process();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,11 @@ namespace SimpleWeb {
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
boost::asio::ip::tcp::no_delay option(true);
|
||||||
socket->lowest_layer().set_option(option);
|
socket->lowest_layer().set_option(option);
|
||||||
|
|
||||||
|
auto timer=get_timeout_timer();
|
||||||
socket->async_handshake(boost::asio::ssl::stream_base::client,
|
socket->async_handshake(boost::asio::ssl::stream_base::client,
|
||||||
[this](const boost::system::error_code& ec) {
|
[this, timer](const boost::system::error_code& ec) {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
if(ec) {
|
if(ec) {
|
||||||
socket=nullptr;
|
socket=nullptr;
|
||||||
throw boost::system::system_error(ec);
|
throw boost::system::system_error(ec);
|
||||||
|
|
|
||||||
|
|
@ -212,8 +212,11 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
virtual void accept()=0;
|
virtual void accept()=0;
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::deadline_timer> set_timeout_on_socket(const std::shared_ptr<socket_type> &socket, long seconds) {
|
std::shared_ptr<boost::asio::deadline_timer> get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds) {
|
||||||
std::shared_ptr<boost::asio::deadline_timer> timer(new boost::asio::deadline_timer(*io_service));
|
if(seconds==0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto timer=std::make_shared<boost::asio::deadline_timer>(*io_service);
|
||||||
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
||||||
timer->async_wait([socket](const boost::system::error_code& ec){
|
timer->async_wait([socket](const boost::system::error_code& ec){
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
|
|
@ -239,13 +242,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
//Set timeout on the following boost::asio::async-read or write function
|
||||||
std::shared_ptr<boost::asio::deadline_timer> timer;
|
auto timer=get_timeout_timer(socket, timeout_request);
|
||||||
if(timeout_request>0)
|
|
||||||
timer=set_timeout_on_socket(socket, timeout_request);
|
|
||||||
|
|
||||||
boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n",
|
boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n",
|
||||||
[this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
|
[this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
|
||||||
if(timeout_request>0)
|
if(timer)
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
||||||
|
|
@ -261,9 +262,7 @@ namespace SimpleWeb {
|
||||||
auto it=request->header.find("Content-Length");
|
auto it=request->header.find("Content-Length");
|
||||||
if(it!=request->header.end()) {
|
if(it!=request->header.end()) {
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
//Set timeout on the following boost::asio::async-read or write function
|
||||||
std::shared_ptr<boost::asio::deadline_timer> timer;
|
auto timer=get_timeout_timer(socket, timeout_content);
|
||||||
if(timeout_content>0)
|
|
||||||
timer=set_timeout_on_socket(socket, timeout_content);
|
|
||||||
unsigned long long content_length;
|
unsigned long long content_length;
|
||||||
try {
|
try {
|
||||||
content_length=stoull(it->second);
|
content_length=stoull(it->second);
|
||||||
|
|
@ -278,14 +277,14 @@ namespace SimpleWeb {
|
||||||
boost::asio::transfer_exactly(content_length-num_additional_bytes),
|
boost::asio::transfer_exactly(content_length-num_additional_bytes),
|
||||||
[this, socket, request, timer]
|
[this, socket, request, timer]
|
||||||
(const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
(const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||||
if(timeout_content>0)
|
if(timer)
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
if(!ec)
|
if(!ec)
|
||||||
find_resource(socket, request);
|
find_resource(socket, request);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(timeout_content>0)
|
if(timer)
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
find_resource(socket, request);
|
find_resource(socket, request);
|
||||||
}
|
}
|
||||||
|
|
@ -362,15 +361,13 @@ namespace SimpleWeb {
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>& resource_function) {
|
std::shared_ptr<typename ServerBase<socket_type>::Request>)>& resource_function) {
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
//Set timeout on the following boost::asio::async-read or write function
|
||||||
std::shared_ptr<boost::asio::deadline_timer> timer;
|
auto timer=get_timeout_timer(socket, timeout_content);
|
||||||
if(timeout_content>0)
|
|
||||||
timer=set_timeout_on_socket(socket, timeout_content);
|
|
||||||
|
|
||||||
auto response=std::shared_ptr<Response>(new Response(socket), [this, request, timer](Response *response_ptr) {
|
auto response=std::shared_ptr<Response>(new Response(socket), [this, request, timer](Response *response_ptr) {
|
||||||
auto response=std::shared_ptr<Response>(response_ptr);
|
auto response=std::shared_ptr<Response>(response_ptr);
|
||||||
send(response, [this, response, request, timer](const boost::system::error_code& ec) {
|
send(response, [this, response, request, timer](const boost::system::error_code& ec) {
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
if(timeout_content>0)
|
if(timer)
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
float http_version;
|
float http_version;
|
||||||
try {
|
try {
|
||||||
|
|
@ -419,7 +416,7 @@ namespace SimpleWeb {
|
||||||
void accept() {
|
void accept() {
|
||||||
//Create new socket for this connection
|
//Create new socket for this connection
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
||||||
std::shared_ptr<HTTP> socket(new HTTP(*io_service));
|
auto socket=std::make_shared<HTTP>(*io_service);
|
||||||
|
|
||||||
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec){
|
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec){
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace SimpleWeb {
|
||||||
void accept() {
|
void accept() {
|
||||||
//Create new socket for this connection
|
//Create new socket for this connection
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
||||||
std::shared_ptr<HTTPS> socket(new HTTPS(*io_service, context));
|
auto socket=std::make_shared<HTTPS>(*io_service, context);
|
||||||
|
|
||||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code& ec) {
|
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code& ec) {
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
||||||
|
|
@ -41,12 +41,10 @@ namespace SimpleWeb {
|
||||||
socket->lowest_layer().set_option(option);
|
socket->lowest_layer().set_option(option);
|
||||||
|
|
||||||
//Set timeout on the following boost::asio::ssl::stream::async_handshake
|
//Set timeout on the following boost::asio::ssl::stream::async_handshake
|
||||||
std::shared_ptr<boost::asio::deadline_timer> timer;
|
auto timer=get_timeout_timer(socket, timeout_request);
|
||||||
if(timeout_request>0)
|
|
||||||
timer=set_timeout_on_socket(socket, timeout_request);
|
|
||||||
(*socket).async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
|
(*socket).async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
|
||||||
(const boost::system::error_code& ec) {
|
(const boost::system::error_code& ec) {
|
||||||
if(timeout_request>0)
|
if(timer)
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
if(!ec)
|
if(!ec)
|
||||||
read_request_and_content(socket);
|
read_request_and_content(socket);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue