Skip to content

Commit 6771a55

Browse files
committed
Added connection timeout check to HTTP client.
1 parent 4e0add7 commit 6771a55

File tree

6 files changed

+99
-21
lines changed

6 files changed

+99
-21
lines changed

http/src/http/v2/client/client.cpp

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@ namespace network {
3333
boost::asio::streambuf request_buffer_;
3434
boost::asio::streambuf response_buffer_;
3535

36-
// TODO configure chunked transfer encoding
37-
bool chunked_;
38-
39-
// TODO configure deadline timer for timeouts
40-
bool timedout_;
41-
4236
std::uint64_t total_bytes_written_, total_bytes_read_;
4337

4438
request_context(
@@ -47,8 +41,6 @@ namespace network {
4741
: connection_(connection),
4842
request_(request),
4943
options_(options),
50-
chunked_(false),
51-
timedout_(false),
5244
total_bytes_written_(0),
5345
total_bytes_read_(0) {}
5446
};
@@ -63,8 +55,14 @@ namespace network {
6355

6456
~impl();
6557

58+
void set_error(const boost::system::error_code &ec,
59+
std::shared_ptr<request_context> context);
60+
6661
std::future<response> execute(std::shared_ptr<request_context> context);
6762

63+
void timeout(const boost::system::error_code &ec,
64+
std::shared_ptr<request_context> context);
65+
6866
void connect(const boost::system::error_code &ec,
6967
tcp::resolver::iterator endpoint_iterator,
7068
std::shared_ptr<request_context> context);
@@ -101,7 +99,11 @@ namespace network {
10199
boost::asio::io_service::strand strand_;
102100
std::unique_ptr<client_connection::async_resolver> resolver_;
103101
std::shared_ptr<client_connection::async_connection> mock_connection_;
102+
// TODO configure deadline timer for timeouts
103+
bool timedout_;
104+
boost::asio::deadline_timer timer_;
104105
std::thread lifetime_thread_;
106+
105107
};
106108

107109
client::impl::impl(client_options options)
@@ -110,6 +112,8 @@ namespace network {
110112
strand_(io_service_),
111113
resolver_(new client_connection::tcp_resolver(
112114
io_service_, options_.cache_resolved())),
115+
timedout_(false),
116+
timer_(io_service_),
113117
lifetime_thread_([=]() { io_service_.run(); }) {}
114118

115119
client::impl::impl(
@@ -120,13 +124,23 @@ namespace network {
120124
sentinel_(new boost::asio::io_service::work(io_service_)),
121125
strand_(io_service_),
122126
resolver_(std::move(mock_resolver)),
127+
timedout_(false),
128+
timer_(io_service_),
123129
lifetime_thread_([=]() { io_service_.run(); }) {}
124130

125131
client::impl::~impl() {
126132
sentinel_.reset();
127133
lifetime_thread_.join();
128134
}
129135

136+
137+
void client::impl::set_error(const boost::system::error_code &ec,
138+
std::shared_ptr<request_context> context) {
139+
context->response_promise_.set_exception(std::make_exception_ptr(
140+
std::system_error(ec.value(), std::system_category())));
141+
timer_.cancel();
142+
}
143+
130144
std::future<response> client::impl::execute(
131145
std::shared_ptr<request_context> context) {
132146
std::future<response> res = context->response_promise_.get_future();
@@ -153,15 +167,29 @@ namespace network {
153167
connect(ec, endpoint_iterator, context);
154168
}));
155169

170+
if (options_.timeout() > std::chrono::milliseconds(0)) {
171+
timer_.expires_from_now(boost::posix_time::milliseconds(options_.timeout().count()));
172+
timer_.async_wait(strand_.wrap([=](const boost::system::error_code &ec) {
173+
timeout(ec, context);
174+
}));
175+
}
176+
156177
return res;
157178
}
158179

180+
void client::impl::timeout(const boost::system::error_code &ec,
181+
std::shared_ptr<request_context> context) {
182+
if (!ec) {
183+
context->connection_->disconnect();
184+
}
185+
timedout_ = true;
186+
}
187+
159188
void client::impl::connect(const boost::system::error_code &ec,
160189
tcp::resolver::iterator endpoint_iterator,
161190
std::shared_ptr<request_context> context) {
162191
if (ec) {
163-
context->response_promise_.set_exception(std::make_exception_ptr(
164-
std::system_error(ec.value(), std::system_category())));
192+
set_error(ec, context);
165193
return;
166194
}
167195

@@ -188,9 +216,13 @@ namespace network {
188216
void client::impl::write_request(
189217
const boost::system::error_code &ec,
190218
std::shared_ptr<request_context> context) {
219+
if (timedout_) {
220+
set_error(boost::asio::error::timed_out, context);
221+
return;
222+
}
223+
191224
if (ec) {
192-
context->response_promise_.set_exception(std::make_exception_ptr(
193-
std::system_error(ec.value(), std::system_category())));
225+
set_error(ec, context);
194226
return;
195227
}
196228

@@ -200,6 +232,7 @@ namespace network {
200232
if (!request_stream) {
201233
context->response_promise_.set_exception(std::make_exception_ptr(
202234
client_exception(client_error::invalid_request)));
235+
timer_.cancel();
203236
}
204237

205238
context->connection_->async_write(
@@ -213,9 +246,13 @@ namespace network {
213246
void client::impl::write_body(const boost::system::error_code &ec,
214247
std::size_t bytes_written,
215248
std::shared_ptr<request_context> context) {
249+
if (timedout_) {
250+
set_error(boost::asio::error::timed_out, context);
251+
return;
252+
}
253+
216254
if (ec) {
217-
context->response_promise_.set_exception(std::make_exception_ptr(
218-
std::system_error(ec.value(), std::system_category())));
255+
set_error(ec, context);
219256
return;
220257
}
221258

@@ -245,9 +282,13 @@ namespace network {
245282
void client::impl::read_response(
246283
const boost::system::error_code &ec, std::size_t bytes_written,
247284
std::shared_ptr<request_context> context) {
285+
if (timedout_) {
286+
set_error(boost::asio::error::timed_out, context);
287+
return;
288+
}
289+
248290
if (ec) {
249-
context->response_promise_.set_exception(std::make_exception_ptr(
250-
std::system_error(ec.value(), std::system_category())));
291+
set_error(ec, context);
251292
return;
252293
}
253294

@@ -272,9 +313,13 @@ namespace network {
272313
const boost::system::error_code &ec, std::size_t,
273314
std::shared_ptr<request_context> context,
274315
std::shared_ptr<response> res) {
316+
if (timedout_) {
317+
set_error(boost::asio::error::timed_out, context);
318+
return;
319+
}
320+
275321
if (ec) {
276-
context->response_promise_.set_exception(std::make_exception_ptr(
277-
std::system_error(ec.value(), std::system_category())));
322+
set_error(ec, context);
278323
return;
279324
}
280325

@@ -304,9 +349,13 @@ namespace network {
304349
const boost::system::error_code &ec, std::size_t,
305350
std::shared_ptr<request_context> context,
306351
std::shared_ptr<response> res) {
352+
if (timedout_) {
353+
set_error(boost::asio::error::timed_out, context);
354+
return;
355+
}
356+
307357
if (ec) {
308-
context->response_promise_.set_exception(std::make_exception_ptr(
309-
std::system_error(ec.value(), std::system_category())));
358+
set_error(ec, context);
310359
return;
311360
}
312361

@@ -370,6 +419,7 @@ namespace network {
370419
// If there's no data else to read, then set the response and exit.
371420
if (bytes_read == 0) {
372421
context->response_promise_.set_value(*res);
422+
timer_.cancel();
373423
return;
374424
}
375425

http/src/network/http/v2/client/client.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <vector>
2222
#include <chrono>
2323
#include <boost/asio/io_service.hpp>
24+
#include <boost/asio/deadline_timer.hpp>
2425
#include <boost/optional.hpp>
2526
#include <network/config.hpp>
2627
#include <network/version.hpp>

http/src/network/http/v2/client/connection/async_connection.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ namespace network {
9494
virtual void async_read(boost::asio::streambuf &command_streambuf,
9595
read_callback callback) = 0;
9696

97+
/**
98+
* \brief Breaks the connection.
99+
*/
100+
virtual void disconnect() = 0;
101+
97102
/**
98103
* \brief Cancels an operation on a connection.
99104
*/

http/src/network/http/v2/client/connection/normal_connection.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,16 @@ namespace network {
7575
boost::asio::transfer_at_least(1), callback);
7676
}
7777

78+
virtual void disconnect() {
79+
if (socket_ && socket_->is_open()) {
80+
boost::system::error_code ec;
81+
socket_->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
82+
if (!ec) {
83+
socket_->close(ec);
84+
}
85+
}
86+
}
87+
7888
virtual void cancel() {
7989
socket_->cancel();
8090
}

http/src/network/http/v2/client/connection/ssl_connection.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,18 @@ namespace network {
100100
boost::asio::transfer_at_least(1), callback);
101101
}
102102

103+
virtual void disconnect() {
104+
if (socket_ && socket_->lowest_layer().is_open()) {
105+
boost::system::error_code ec;
106+
socket_->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
107+
if (!ec) {
108+
socket_->lowest_layer().close(ec);
109+
}
110+
}
111+
}
112+
103113
virtual void cancel() {
104-
//socket_->cancel();
114+
socket_->lowest_layer().cancel();
105115
}
106116

107117
private:

http/test/v2/client/units/client_resolution_test.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class fake_async_connection : public http_cc::async_connection {
5151
virtual void async_read(boost::asio::streambuf &,
5252
read_callback) { }
5353

54+
virtual void disconnect() { }
55+
5456
virtual void cancel() { }
5557

5658
};

0 commit comments

Comments
 (0)