Skip to content

Commit c8c9227

Browse files
sridmadras0219-msft
authored andcommitted
Improve error handling in on_accept (microsoft#750)
* Improve error handling in on_accept * Move lock to the top of the function * Lock shared data at the right locations. * [http_listener] improve refcount and lifetime management by using RAII.
1 parent c214c81 commit c8c9227

File tree

1 file changed

+75
-23
lines changed

1 file changed

+75
-23
lines changed

Release/src/http/listener/http_server_asio.cpp

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ namespace
200200
}
201201

202202
private:
203-
void on_accept(boost::asio::ip::tcp::socket* socket, const boost::system::error_code& ec);
203+
void on_accept(std::unique_ptr<boost::asio::ip::tcp::socket> socket, const boost::system::error_code& ec);
204204

205205
};
206206

@@ -333,20 +333,41 @@ class asio_server_connection
333333
std::unique_ptr<boost::asio::ssl::context> m_ssl_context;
334334
std::unique_ptr<ssl_stream> m_ssl_stream;
335335

336-
public:
337336
asio_server_connection(std::unique_ptr<boost::asio::ip::tcp::socket> socket, http_linux_server* server, hostport_listener* parent)
338337
: m_socket(std::move(socket))
339338
, m_request_buf()
340339
, m_response_buf()
341340
, m_p_server(server)
342341
, m_p_parent(parent)
343342
, m_close(false)
343+
, m_chunked(false)
344344
, m_refs(1)
345345
{
346346
}
347347

348-
will_deref_and_erase_t start(bool is_https, const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
348+
struct Dereferencer
349+
{
350+
void operator()(asio_server_connection* conn) const { conn->deref(); }
351+
};
352+
353+
public:
354+
using refcount_ptr = std::unique_ptr<asio_server_connection, Dereferencer>;
355+
356+
static refcount_ptr create(std::unique_ptr<boost::asio::ip::tcp::socket> socket, http_linux_server* server, hostport_listener* parent)
357+
{
358+
return refcount_ptr(new asio_server_connection(std::move(socket), server, parent));
359+
}
360+
361+
refcount_ptr get_reference()
349362
{
363+
++m_refs;
364+
return refcount_ptr(this);
365+
}
366+
367+
will_erase_from_parent_t start_connection(bool is_https, const std::function<void(boost::asio::ssl::context&)>& ssl_context_callback)
368+
{
369+
auto unique_reference = this->get_reference();
370+
350371
if (is_https)
351372
{
352373
m_ssl_context = make_unique<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
@@ -360,11 +381,14 @@ class asio_server_connection
360381
{
361382
(will_deref_and_erase_t)this->start_request_response();
362383
});
363-
return will_deref_and_erase_t{};
384+
unique_reference.release();
385+
return will_erase_from_parent_t{};
364386
}
365387
else
366388
{
367-
return start_request_response();
389+
(will_deref_and_erase_t)start_request_response();
390+
unique_reference.release();
391+
return will_erase_from_parent_t{};
368392
}
369393
}
370394

@@ -385,7 +409,7 @@ class asio_server_connection
385409
will_deref_and_erase_t dispatch_request_to_listener();
386410
will_erase_from_parent_t do_response()
387411
{
388-
++m_refs;
412+
auto unique_reference = this->get_reference();
389413
m_request.get_response().then([=](pplx::task<http_response> r_task)
390414
{
391415
http_response response;
@@ -406,11 +430,12 @@ class asio_server_connection
406430
(will_deref_and_erase_t)this->async_write(&asio_server_connection::handle_headers_written, response);
407431
});
408432
});
433+
unique_reference.release();
409434
return will_erase_from_parent_t{};
410435
}
411436
will_erase_from_parent_t do_bad_response()
412437
{
413-
++m_refs;
438+
auto unique_reference = this->get_reference();
414439
m_request.get_response().then([=](pplx::task<http_response> r_task)
415440
{
416441
http_response response;
@@ -428,6 +453,7 @@ class asio_server_connection
428453

429454
(will_deref_and_erase_t)async_write(&asio_server_connection::handle_headers_written, response);
430455
});
456+
unique_reference.release();
431457
return will_erase_from_parent_t{};
432458
}
433459

@@ -495,10 +521,13 @@ void hostport_listener::start()
495521
m_acceptor->listen(0 != m_backlog ? m_backlog : socket_base::max_connections);
496522

497523
auto socket = new ip::tcp::socket(service);
524+
std::unique_ptr<ip::tcp::socket> usocket(socket);
498525
m_acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec)
499526
{
500-
this->on_accept(socket, ec);
527+
std::unique_ptr<ip::tcp::socket> usocket(socket);
528+
this->on_accept(std::move(usocket), ec);
501529
});
530+
usocket.release();
502531
}
503532

504533
void asio_server_connection::close()
@@ -538,30 +567,53 @@ will_deref_and_erase_t asio_server_connection::start_request_response()
538567
return will_deref_and_erase_t{};
539568
}
540569

541-
void hostport_listener::on_accept(ip::tcp::socket* socket, const boost::system::error_code& ec)
570+
void hostport_listener::on_accept(std::unique_ptr<ip::tcp::socket> socket, const boost::system::error_code& ec)
542571
{
543-
std::unique_ptr<ip::tcp::socket> usocket(std::move(socket));
572+
// Listener closed
573+
if (ec == boost::asio::error::operation_aborted)
574+
{
575+
return;
576+
}
577+
578+
std::lock_guard<std::mutex> lock(m_connections_lock);
544579

580+
// Handle successful accept
545581
if (!ec)
546582
{
547-
auto conn = new asio_server_connection(std::move(usocket), m_p_server, this);
583+
auto conn = asio_server_connection::create(std::move(socket), m_p_server, this);
548584

549-
std::lock_guard<std::mutex> lock(m_connections_lock);
550-
m_connections.insert(conn);
551-
conn->start(m_is_https, m_ssl_context_callback);
552-
if (m_connections.size() == 1)
553-
m_all_connections_complete.reset();
585+
m_connections.insert(conn.get());
586+
try
587+
{
588+
(will_erase_from_parent_t)conn->start_connection(m_is_https, m_ssl_context_callback);
589+
// at this point an asynchronous task has been launched which will call
590+
// m_connections.erase(conn.get()) eventually
554591

555-
if (m_acceptor)
592+
// the following cannot throw
593+
if (m_connections.size() == 1)
594+
m_all_connections_complete.reset();
595+
}
596+
catch (boost::system::system_error&)
556597
{
557-
// spin off another async accept
558-
auto newSocket = new ip::tcp::socket(crossplat::threadpool::shared_instance().service());
559-
m_acceptor->async_accept(*newSocket, [this, newSocket](const boost::system::error_code& ec)
560-
{
561-
this->on_accept(newSocket, ec);
562-
});
598+
// boost ssl apis throw boost::system::system_error.
599+
// Exception indicates something went wrong setting ssl context.
600+
// Drop connection and continue handling other connections.
601+
m_connections.erase(conn.get());
563602
}
564603
}
604+
605+
if (m_acceptor)
606+
{
607+
// spin off another async accept
608+
auto newSocket = new ip::tcp::socket(crossplat::threadpool::shared_instance().service());
609+
std::unique_ptr<ip::tcp::socket> usocket(newSocket);
610+
m_acceptor->async_accept(*newSocket, [this, newSocket](const boost::system::error_code& ec)
611+
{
612+
std::unique_ptr<ip::tcp::socket> usocket(newSocket);
613+
this->on_accept(std::move(usocket), ec);
614+
});
615+
usocket.release();
616+
}
565617
}
566618

567619
will_deref_and_erase_t asio_server_connection::handle_http_line(const boost::system::error_code& ec)

0 commit comments

Comments
 (0)