@@ -22,13 +22,17 @@ namespace server {
22
22
23
23
// this is the constructor for plain connections
24
24
connection::connection (boost::asio::io_service& io_service,
25
- connection_manager& manager, request_handler& handler, int timeout)
26
- : connection_manager_(manager),
27
- request_handler_ (handler),
28
- timeout_(timeout),
29
- timer_(io_service, boost::posix_time::seconds( timeout )),
30
- default_abandoned_timeout_(20 *60 ), // 20mn before stopping abandoned connection
31
- abandoned_timer_(io_service, boost::posix_time::seconds(default_abandoned_timeout_))
25
+ connection_manager& manager,
26
+ request_handler& handler,
27
+ int read_timeout) :
28
+ connection_manager_ (manager),
29
+ request_handler_ (handler),
30
+ read_timeout_ (read_timeout),
31
+ read_timer_ (io_service, boost::posix_time::seconds(read_timeout)),
32
+ default_abandoned_timeout_ (20 *60 ), // 20mn before stopping abandoned connection
33
+ abandoned_timer_ (io_service, boost::posix_time::seconds(default_abandoned_timeout_)),
34
+ status (" initializing" ),
35
+ stop_required (false )
32
36
{
33
37
secure_ = false ;
34
38
keepalive_ = false ;
@@ -41,13 +45,18 @@ connection::connection(boost::asio::io_service& io_service,
41
45
#ifdef WWW_ENABLE_SSL
42
46
// this is the constructor for secure connections
43
47
connection::connection (boost::asio::io_service& io_service,
44
- connection_manager& manager, request_handler& handler, int timeout, boost::asio::ssl::context& context)
45
- : connection_manager_(manager),
46
- request_handler_(handler),
47
- timeout_(timeout),
48
- timer_(io_service, boost::posix_time::seconds( timeout )),
49
- default_abandoned_timeout_(20 *60 ), // 20mn before stopping abandoned connection
50
- abandoned_timer_(io_service, boost::posix_time::seconds(default_abandoned_timeout_))
48
+ connection_manager& manager,
49
+ request_handler& handler,
50
+ int read_timeout,
51
+ boost::asio::ssl::context& context) :
52
+ connection_manager_(manager),
53
+ request_handler_(handler),
54
+ read_timeout_(read_timeout),
55
+ read_timer_(io_service, boost::posix_time::seconds(read_timeout)),
56
+ default_abandoned_timeout_(20 *60 ), // 20mn before stopping abandoned connection
57
+ abandoned_timer_(io_service, boost::posix_time::seconds(default_abandoned_timeout_)),
58
+ status(" initializing" ),
59
+ stop_required(false )
51
60
{
52
61
secure_ = true ;
53
62
keepalive_ = false ;
@@ -85,11 +94,13 @@ void connection::start()
85
94
connection_manager_.stop (shared_from_this ());
86
95
return ;
87
96
}
97
+ host_endpoint_ = endpoint.address ().to_string ();
88
98
89
99
set_abandoned_timeout ();
90
- host_endpoint_ = endpoint. address (). to_string ();
100
+
91
101
if (secure_) {
92
102
#ifdef WWW_ENABLE_SSL
103
+ status = " waiting-handshake" ;
93
104
// with ssl, we first need to complete the handshake before reading
94
105
sslsocket_->async_handshake (boost::asio::ssl::stream_base::server,
95
106
boost::bind (&connection::handle_handshake, shared_from_this (),
@@ -106,25 +117,23 @@ void connection::stop()
106
117
{
107
118
// Cancel timers
108
119
cancel_abandoned_timeout ();
109
- timer_. cancel ();
120
+ cancel_read_timeout ();
110
121
111
- // Initiate graceful connection closure.
112
- boost::system::error_code ignored_ec;
113
- socket ().shutdown (boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); // @note For portable behaviour with respect to graceful closure of a
114
- // connected socket, call shutdown() before closing the socket.
115
- socket ().close ();
116
- }
117
-
118
- void connection::handle_timeout (const boost::system::error_code& error)
119
- {
120
- if (error != boost::asio::error::operation_aborted) {
121
- connection_manager_.stop (shared_from_this ());
122
- }
122
+ try {
123
+ // Initiate graceful connection closure.
124
+ boost::system::error_code ignored_ec;
125
+ socket ().shutdown (boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); // @note For portable behaviour with respect to graceful closure of a
126
+ // connected socket, call shutdown() before closing the socket.
127
+ socket ().close (ignored_ec);
128
+ } catch (...) {
129
+ _log.Log (LOG_ERROR, " %s -> exception thrown while stopping connection" , host_endpoint_.c_str ());
130
+ }
123
131
}
124
132
125
133
#ifdef WWW_ENABLE_SSL
126
134
void connection::handle_handshake (const boost::system::error_code& error)
127
135
{
136
+ status = " handshaking" ;
128
137
if (secure_) { // assert
129
138
if (!error)
130
139
{
@@ -141,12 +150,16 @@ void connection::handle_handshake(const boost::system::error_code& error)
141
150
142
151
void connection::read_more ()
143
152
{
153
+ status = " waiting-read" ;
154
+ if (is_stopping ()) {
155
+ return ;
156
+ }
157
+
144
158
// read chunks of max 4 KB
145
159
boost::asio::streambuf::mutable_buffers_type buf = _buf.prepare (4096 );
146
160
147
161
// set timeout timer
148
- timer_.expires_from_now (boost::posix_time::seconds (timeout_));
149
- timer_.async_wait (boost::bind (&connection::handle_timeout, shared_from_this (), boost::asio::placeholders::error));
162
+ reset_read_timeout ();
150
163
151
164
if (secure_) {
152
165
#ifdef WWW_ENABLE_SSL
@@ -168,8 +181,14 @@ void connection::read_more()
168
181
169
182
void connection::handle_read (const boost::system::error_code& error, std::size_t bytes_transferred)
170
183
{
184
+ status = " reading" ;
185
+ if (is_stopping ()) {
186
+ return ;
187
+ }
188
+
171
189
// data read, no need for timeouts (RK, note: race condition)
172
- timer_.cancel ();
190
+ cancel_read_timeout ();
191
+
173
192
if (!error && bytes_transferred > 0 )
174
193
{
175
194
// ensure written bytes in the buffer
@@ -201,6 +220,12 @@ void connection::handle_read(const boost::system::error_code& error, std::size_t
201
220
request_.host = request_.host .substr (7 );
202
221
}
203
222
request_handler_.handle_request (request_, reply_);
223
+
224
+ status = " waiting-write" ;
225
+ if (is_stopping ()) {
226
+ return ;
227
+ }
228
+
204
229
if (secure_) {
205
230
#ifdef WWW_ENABLE_SSL
206
231
boost::asio::async_write (*sslsocket_, reply_.to_buffers (request_.method ),
@@ -218,6 +243,12 @@ void connection::handle_read(const boost::system::error_code& error, std::size_t
218
243
{
219
244
keepalive_ = false ;
220
245
reply_ = reply::stock_reply (reply::bad_request);
246
+
247
+ status = " writing" ;
248
+ if (is_stopping ()) {
249
+ return ;
250
+ }
251
+
221
252
if (secure_) {
222
253
#ifdef WWW_ENABLE_SSL
223
254
boost::asio::async_write (*sslsocket_, reply_.to_buffers (request_.method ),
@@ -244,7 +275,7 @@ void connection::handle_read(const boost::system::error_code& error, std::size_t
244
275
245
276
void connection::handle_write (const boost::system::error_code& error)
246
277
{
247
- if (!error && keepalive_) {
278
+ if (!error && keepalive_ && !stop_required ) {
248
279
// if a keep-alive connection is requested, we read the next request
249
280
read_more ();
250
281
} else {
@@ -261,6 +292,39 @@ connection::~connection()
261
292
#endif
262
293
}
263
294
295
+ // schedule read timeout timer
296
+ void connection::set_read_timeout () {
297
+ read_timer_.expires_from_now (boost::posix_time::seconds (read_timeout_));
298
+ read_timer_.async_wait (boost::bind (&connection::handle_read_timeout, shared_from_this (), boost::asio::placeholders::error));
299
+ }
300
+
301
+ // / simply cancel read timeout timer
302
+ void connection::cancel_read_timeout () {
303
+ try {
304
+ boost::system::error_code ignored_ec;
305
+ read_timer_.cancel (ignored_ec);
306
+ if (ignored_ec) {
307
+ _log.Log (LOG_ERROR, " %s -> exception thrown while canceling read timeout : %s" , host_endpoint_.c_str (), ignored_ec.message ().c_str ());
308
+ }
309
+ } catch (...) {
310
+ _log.Log (LOG_ERROR, " %s -> exception thrown while canceling read timeout" , host_endpoint_.c_str ());
311
+ }
312
+ }
313
+
314
+ // / reschedule read timeout timer
315
+ void connection::reset_read_timeout () {
316
+ cancel_read_timeout ();
317
+ set_read_timeout ();
318
+ }
319
+
320
+ // / stop connection on read timeout
321
+ void connection::handle_read_timeout (const boost::system::error_code& error) {
322
+ if (error != boost::asio::error::operation_aborted) {
323
+ _log.Log (LOG_STATUS, " %s -> handle read timeout" , host_endpoint_.c_str ());
324
+ connection_manager_.stop (shared_from_this ());
325
+ }
326
+ }
327
+
264
328
// / schedule abandoned timeout timer
265
329
void connection::set_abandoned_timeout () {
266
330
abandoned_timer_.expires_from_now (boost::posix_time::seconds (default_abandoned_timeout_));
@@ -269,7 +333,15 @@ void connection::set_abandoned_timeout() {
269
333
270
334
// / simply cancel abandoned timeout timer
271
335
void connection::cancel_abandoned_timeout () {
272
- abandoned_timer_.cancel ();
336
+ try {
337
+ boost::system::error_code ignored_ec;
338
+ abandoned_timer_.cancel (ignored_ec);
339
+ if (ignored_ec) {
340
+ _log.Log (LOG_ERROR, " %s -> exception thrown while canceling abandoned timeout : %s" , host_endpoint_.c_str (), ignored_ec.message ().c_str ());
341
+ }
342
+ } catch (...) {
343
+ _log.Log (LOG_ERROR, " %s -> exception thrown while canceling abandoned timeout" , host_endpoint_.c_str ());
344
+ }
273
345
}
274
346
275
347
// / reschedule abandoned timeout timer
@@ -286,5 +358,19 @@ void connection::handle_abandoned_timeout(const boost::system::error_code& error
286
358
}
287
359
}
288
360
361
+ // / Wait for all asynchronous operations to abort.
362
+ void connection::stop_gracefully () {
363
+ stop_required = true ;
364
+ }
365
+
366
+ // / stop the connection if it is required
367
+ bool connection::is_stopping () {
368
+ if (stop_required) {
369
+ connection_manager_.stop (shared_from_this ());
370
+ return true ;
371
+ }
372
+ return false ;
373
+ }
374
+
289
375
} // namespace server
290
376
} // namespace http
0 commit comments