Skip to content

Commit f9d5f74

Browse files
committed
Guarding socket and ssl::stream with a lock since timeouts and cancellation can occur concurrently with request/response processing.
1 parent 834eeab commit f9d5f74

File tree

1 file changed

+28
-13
lines changed

1 file changed

+28
-13
lines changed

Release/src/http/client/http_linux.cpp

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ namespace web { namespace http
8080

8181
void close()
8282
{
83+
std::lock_guard<std::mutex> lock(m_socket_lock);
84+
8385
// Ensures closed connections owned by request_context will not be put to pool when they are released.
8486
m_keep_alive = false;
8587

@@ -90,7 +92,8 @@ namespace web { namespace http
9092

9193
boost::system::error_code cancel()
9294
{
93-
boost::system::error_code error;
95+
std::lock_guard<std::mutex> lock(m_socket_lock);
96+
boost::system::error_code error;
9497
m_socket.cancel(error);
9598
return error;
9699
}
@@ -105,13 +108,18 @@ namespace web { namespace http
105108
void set_keep_alive(bool keep_alive) { m_keep_alive = keep_alive; }
106109
bool keep_alive() const { return m_keep_alive; }
107110

108-
bool is_open() const { return m_socket.is_open(); }
111+
bool is_open()
112+
{
113+
std::lock_guard<std::mutex> lock(m_socket_lock);
114+
return m_socket.is_open();
115+
}
109116
bool is_ssl() const { return m_ssl_stream ? true : false; }
110117

111118
template <typename Iterator, typename Handler>
112119
void async_connect(const Iterator &begin, const Handler &handler)
113120
{
114-
m_socket.async_connect(begin, handler);
121+
std::lock_guard<std::mutex> lock(m_socket_lock);
122+
m_socket.async_connect(begin, handler);
115123
}
116124

117125
template <typename HandshakeHandler, typename CertificateHandler>
@@ -121,7 +129,8 @@ namespace web { namespace http
121129
const HandshakeHandler &handshake_handler,
122130
const CertificateHandler &cert_handler)
123131
{
124-
assert(is_ssl());
132+
std::lock_guard<std::mutex> lock(m_socket_lock);
133+
assert(is_ssl());
125134

126135
// Check to turn on/off server certificate verification.
127136
if (config.validate_certificates())
@@ -139,7 +148,8 @@ namespace web { namespace http
139148
template <typename ConstBufferSequence, typename Handler>
140149
void async_write(ConstBufferSequence &buffer, const Handler &writeHandler)
141150
{
142-
if (m_ssl_stream)
151+
std::lock_guard<std::mutex> lock(m_socket_lock);
152+
if (m_ssl_stream)
143153
{
144154
boost::asio::async_write(*m_ssl_stream, buffer, writeHandler);
145155
}
@@ -152,7 +162,8 @@ namespace web { namespace http
152162
template <typename MutableBufferSequence, typename CompletionCondition, typename Handler>
153163
void async_read(MutableBufferSequence &buffer, const CompletionCondition &condition, const Handler &readHandler)
154164
{
155-
if (m_ssl_stream)
165+
std::lock_guard<std::mutex> lock(m_socket_lock);
166+
if (m_ssl_stream)
156167
{
157168
boost::asio::async_read(*m_ssl_stream, buffer, condition, readHandler);
158169
}
@@ -165,6 +176,7 @@ namespace web { namespace http
165176
template <typename Handler>
166177
void async_read_until(boost::asio::streambuf &buffer, const std::string &delim, const Handler &readHandler)
167178
{
179+
std::lock_guard<std::mutex> lock(m_socket_lock);
168180
if (m_ssl_stream)
169181
{
170182
boost::asio::async_read_until(*m_ssl_stream, buffer, delim, readHandler);
@@ -191,8 +203,13 @@ namespace web { namespace http
191203

192204
void handle_pool_timer(const boost::system::error_code& ec);
193205

206+
// Guards concurrency access to socket/ssl::stream. This is necessary
207+
// because timeouts and cancellation can touch the socket at the same time
208+
// as normal request read/writing.
209+
std::mutex m_socket_lock;
194210
tcp::socket m_socket;
195211
std::unique_ptr<boost::asio::ssl::stream<tcp::socket &> > m_ssl_stream;
212+
196213
boost::asio::deadline_timer m_pool_timer;
197214
bool m_is_reused;
198215
bool m_keep_alive;
@@ -355,14 +372,14 @@ namespace web { namespace http
355372
{
356373
m_ctx = ctx;
357374
}
358-
375+
359376
void start()
360377
{
361378
assert(m_state == created);
362379
assert(!m_ctx.expired());
363380
m_state = started;
364-
365-
m_timer.expires_from_now(m_duration);
381+
382+
m_timer.expires_from_now(m_duration);
366383
auto ctx = m_ctx;
367384
m_timer.async_wait([ctx](const boost::system::error_code& ec)
368385
{
@@ -375,7 +392,7 @@ namespace web { namespace http
375392
assert(m_state == started || m_state == timedout);
376393
assert(!m_ctx.expired());
377394
if(m_timer.expires_from_now(m_duration) > 0)
378-
{
395+
{
379396
auto ctx = m_ctx;
380397
m_timer.async_wait([ctx](const boost::system::error_code& ec)
381398
{
@@ -408,7 +425,7 @@ namespace web { namespace http
408425
}
409426
}
410427
}
411-
428+
412429
enum timer_state
413430
{
414431
created,
@@ -555,8 +572,6 @@ namespace web { namespace http
555572
{
556573
if (auto ctx_lock = ctx_weak.lock())
557574
{
558-
// Cancel operations and all asio async handlers.
559-
ctx_lock->m_connection->cancel();
560575
// Shut down transmissions, close the socket and prevent connection from being pooled.
561576
ctx_lock->m_connection->close();
562577
}

0 commit comments

Comments
 (0)