diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f029729..590a8b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 213: * Fix posix_file::close handling of EINTR * basic_stream subsumes stranded_stream: +* Use timeouts in HTTP server examples -------------------------------------------------------------------------------- diff --git a/example/http/server/async-ssl/http_server_async_ssl.cpp b/example/http/server/async-ssl/http_server_async_ssl.cpp index a388bb50..678a0486 100644 --- a/example/http/server/async-ssl/http_server_async_ssl.cpp +++ b/example/http/server/async-ssl/http_server_async_ssl.cpp @@ -18,9 +18,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include @@ -250,21 +248,16 @@ class session : public std::enable_shared_from_this http::async_write( self_.stream_, *sp, - net::bind_executor( - self_.strand_, - std::bind( - &session::on_write, - self_.shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - sp->need_eof()))); + std::bind( + &session::on_write, + self_.shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + sp->need_eof())); } }; - tcp::socket socket_; - ssl::stream stream_; - net::strand< - net::io_context::executor_type> strand_; + beast::ssl_stream> stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -275,12 +268,10 @@ public: // Take ownership of the socket explicit session( - tcp::socket socket, + tcp::socket&& socket, ssl::context& ctx, std::shared_ptr const& doc_root) - : socket_(std::move(socket)) - , stream_(socket_, ctx) - , strand_(socket_.get_executor()) + : stream_(std::move(socket), ctx) , doc_root_(doc_root) , lambda_(*this) { @@ -290,15 +281,16 @@ public: void run() { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Perform the SSL handshake stream_.async_handshake( ssl::stream_base::server, - net::bind_executor( - strand_, - std::bind( - &session::on_handshake, - shared_from_this(), - std::placeholders::_1))); + std::bind( + &session::on_handshake, + shared_from_this(), + std::placeholders::_1)); } void @@ -317,15 +309,16 @@ public: // otherwise the operation behavior is undefined. req_ = {}; + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Read a request http::async_read(stream_, buffer_, req_, - net::bind_executor( - strand_, - std::bind( - &session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + std::bind( + &session::on_read, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } void @@ -374,14 +367,15 @@ public: void do_close() { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Perform the SSL shutdown stream_.async_shutdown( - net::bind_executor( - strand_, - std::bind( - &session::on_shutdown, - shared_from_this(), - std::placeholders::_1))); + std::bind( + &session::on_shutdown, + shared_from_this(), + std::placeholders::_1)); } void @@ -401,7 +395,6 @@ class listener : public std::enable_shared_from_this { ssl::context& ctx_; tcp::acceptor acceptor_; - tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -412,7 +405,6 @@ public: std::shared_ptr const& doc_root) : ctx_(ctx) , acceptor_(ioc) - , socket_(ioc) , doc_root_(doc_root) { beast::error_code ec; @@ -464,15 +456,15 @@ public: do_accept() { acceptor_.async_accept( - socket_, std::bind( &listener::on_accept, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_accept(beast::error_code ec) + on_accept(beast::error_code ec, tcp::socket socket) { if(ec) { @@ -482,7 +474,7 @@ public: { // Create the session and run it std::make_shared( - std::move(socket_), + std::move(socket), ctx_, doc_root_)->run(); } diff --git a/example/http/server/async/http_server_async.cpp b/example/http/server/async/http_server_async.cpp index 7cfb1d22..db061b2a 100644 --- a/example/http/server/async/http_server_async.cpp +++ b/example/http/server/async/http_server_async.cpp @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include #include #include @@ -244,22 +242,18 @@ class session : public std::enable_shared_from_this // Write the response http::async_write( - self_.socket_, + self_.stream_, *sp, - net::bind_executor( - self_.strand_, - std::bind( - &session::on_write, - self_.shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - sp->need_eof()))); + std::bind( + &session::on_write, + self_.shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + sp->need_eof())); } }; - tcp::socket socket_; - net::strand< - net::io_context::executor_type> strand_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -268,12 +262,10 @@ class session : public std::enable_shared_from_this public: // Take ownership of the socket - explicit session( - tcp::socket socket, + tcp::socket&& socket, std::shared_ptr const& doc_root) - : socket_(std::move(socket)) - , strand_(socket_.get_executor()) + : stream_(std::move(socket)) , doc_root_(doc_root) , lambda_(*this) { @@ -293,15 +285,16 @@ public: // otherwise the operation behavior is undefined. req_ = {}; + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + // Read a request - http::async_read(socket_, buffer_, req_, - net::bind_executor( - strand_, - std::bind( - &session::on_read, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + http::async_read(stream_, buffer_, req_, + std::bind( + &session::on_read, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } void @@ -352,7 +345,7 @@ public: { // Send a TCP shutdown beast::error_code ec; - socket_.shutdown(tcp::socket::shutdown_send, ec); + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } @@ -364,7 +357,6 @@ public: class listener : public std::enable_shared_from_this { tcp::acceptor acceptor_; - tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -373,7 +365,6 @@ public: tcp::endpoint endpoint, std::shared_ptr const& doc_root) : acceptor_(ioc) - , socket_(ioc) , doc_root_(doc_root) { beast::error_code ec; @@ -425,15 +416,15 @@ public: do_accept() { acceptor_.async_accept( - socket_, std::bind( &listener::on_accept, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_accept(beast::error_code ec) + on_accept(beast::error_code ec, tcp::socket socket) { if(ec) { @@ -443,7 +434,7 @@ public: { // Create the session and run it std::make_shared( - std::move(socket_), + std::move(socket), doc_root_)->run(); } diff --git a/example/http/server/coro-ssl/http_server_coro_ssl.cpp b/example/http/server/coro-ssl/http_server_coro_ssl.cpp index b58df1eb..89eec085 100644 --- a/example/http/server/coro-ssl/http_server_coro_ssl.cpp +++ b/example/http/server/coro-ssl/http_server_coro_ssl.cpp @@ -18,9 +18,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -208,6 +207,11 @@ handle_request( //------------------------------------------------------------------------------ +// The type of stream to use +// Stackful coroutines are already stranded. +using stream_type = + beast::ssl_stream>; + // Report a failure void fail(beast::error_code ec, char const* what) @@ -217,17 +221,15 @@ fail(beast::error_code ec, char const* what) // This is the C++11 equivalent of a generic lambda. // The function object is used to send an HTTP message. -template struct send_lambda { - Stream& stream_; + stream_type& stream_; bool& close_; beast::error_code& ec_; net::yield_context yield_; - explicit send_lambda( - Stream& stream, + stream_type& stream, bool& close, beast::error_code& ec, net::yield_context yield) @@ -256,16 +258,15 @@ struct send_lambda // Handles an HTTP server connection void do_session( - tcp::socket& socket, - ssl::context& ctx, + stream_type& stream, std::shared_ptr const& doc_root, net::yield_context yield) { bool close = false; beast::error_code ec; - // Construct the stream around the socket - ssl::stream stream{socket, ctx}; + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); // Perform the SSL handshake stream.async_handshake(ssl::stream_base::server, yield[ec]); @@ -276,10 +277,13 @@ do_session( beast::flat_buffer buffer; // This lambda is used to send messages - send_lambda> lambda{stream, close, ec, yield}; + send_lambda lambda{stream, close, ec, yield}; for(;;) { + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + // Read a request http::request req; http::async_read(stream, buffer, req, yield[ec]); @@ -300,6 +304,9 @@ do_session( } } + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + // Perform the SSL shutdown stream.async_shutdown(yield[ec]); if(ec) @@ -353,8 +360,7 @@ do_listen( acceptor.get_executor().context(), std::bind( &do_session, - std::move(socket), - std::ref(ctx), + stream_type(std::move(socket), ctx), doc_root, std::placeholders::_1)); } diff --git a/example/http/server/coro/http_server_coro.cpp b/example/http/server/coro/http_server_coro.cpp index 0a5095e3..1ad41359 100644 --- a/example/http/server/coro/http_server_coro.cpp +++ b/example/http/server/coro/http_server_coro.cpp @@ -204,6 +204,10 @@ handle_request( //------------------------------------------------------------------------------ +// The type of stream to use. +// Stackful coroutines are already stranded. +using stream_type = beast::tcp_stream; + // Report a failure void fail(beast::error_code ec, char const* what) @@ -213,17 +217,15 @@ fail(beast::error_code ec, char const* what) // This is the C++11 equivalent of a generic lambda. // The function object is used to send an HTTP message. -template struct send_lambda { - Stream& stream_; + stream_type& stream_; bool& close_; beast::error_code& ec_; net::yield_context yield_; - explicit send_lambda( - Stream& stream, + stream_type& stream, bool& close, beast::error_code& ec, net::yield_context yield) @@ -252,7 +254,7 @@ struct send_lambda // Handles an HTTP server connection void do_session( - tcp::socket& socket, + stream_type& stream, std::shared_ptr const& doc_root, net::yield_context yield) { @@ -263,13 +265,16 @@ do_session( beast::flat_buffer buffer; // This lambda is used to send messages - send_lambda lambda{socket, close, ec, yield}; + send_lambda lambda{stream, close, ec, yield}; for(;;) { + // Set the timeout. + stream.expires_after(std::chrono::seconds(30)); + // Read a request http::request req; - http::async_read(socket, buffer, req, yield[ec]); + http::async_read(stream, buffer, req, yield[ec]); if(ec == http::error::end_of_stream) break; if(ec) @@ -288,7 +293,7 @@ do_session( } // Send a TCP shutdown - socket.shutdown(tcp::socket::shutdown_send, ec); + stream.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } @@ -337,7 +342,7 @@ do_listen( acceptor.get_executor().context(), std::bind( &do_session, - std::move(socket), + stream_type(std::move(socket)), doc_root, std::placeholders::_1)); } diff --git a/example/http/server/flex/http_server_flex.cpp b/example/http/server/flex/http_server_flex.cpp index 54fec0b9..4e87fdcb 100644 --- a/example/http/server/flex/http_server_flex.cpp +++ b/example/http/server/flex/http_server_flex.cpp @@ -19,9 +19,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include @@ -210,6 +208,13 @@ handle_request( //------------------------------------------------------------------------------ +// The type of plain streams +using plain_stream_type = beast::tcp_stream; + +// The type of TLS streams +using ssl_stream_type = + beast::ssl_stream>; + // Report a failure void fail(beast::error_code ec, char const* what) @@ -261,14 +266,12 @@ class session http::async_write( self_.derived().stream(), *sp, - net::bind_executor( - self_.strand_, - std::bind( - &session::on_write, - self_.derived().shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - sp->need_eof()))); + std::bind( + &session::on_write, + self_.derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + sp->need_eof())); } }; @@ -278,20 +281,15 @@ class session send_lambda lambda_; protected: - net::strand< - net::io_context::executor_type> strand_; beast::flat_buffer buffer_; public: // Take ownership of the buffer - explicit session( - net::io_context& ioc, beast::flat_buffer buffer, std::shared_ptr const& doc_root) : doc_root_(doc_root) , lambda_(*this) - , strand_(ioc.get_executor()) , buffer_(std::move(buffer)) { } @@ -299,18 +297,20 @@ public: void do_read() { + // Set the timeout. + beast::get_lowest_layer( + derived().stream()).expires_after(std::chrono::seconds(30)); + // Read a request http::async_read( derived().stream(), buffer_, req_, - net::bind_executor( - strand_, - std::bind( - &session::on_read, - derived().shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + std::bind( + &session::on_read, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } void @@ -362,30 +362,26 @@ class plain_session : public session , public std::enable_shared_from_this { - tcp::socket socket_; - net::strand< - net::io_context::executor_type> strand_; + plain_stream_type stream_; public: // Create the session plain_session( - tcp::socket socket, + tcp::socket&& socket, beast::flat_buffer buffer, std::shared_ptr const& doc_root) : session( - socket.get_executor().context(), std::move(buffer), doc_root) - , socket_(std::move(socket)) - , strand_(socket_.get_executor()) + , stream_(std::move(socket)) { } // Called by the base class - tcp::socket& + plain_stream_type& stream() { - return socket_; + return stream_; } // Start the asynchronous operation @@ -400,7 +396,7 @@ public: { // Send a TCP shutdown beast::error_code ec; - socket_.shutdown(tcp::socket::shutdown_send, ec); + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } @@ -411,30 +407,24 @@ class ssl_session : public session , public std::enable_shared_from_this { - tcp::socket socket_; - ssl::stream stream_; - net::strand< - net::io_context::executor_type> strand_; + ssl_stream_type stream_; public: // Create the session ssl_session( - tcp::socket socket, + tcp::socket&& socket, ssl::context& ctx, beast::flat_buffer buffer, std::shared_ptr const& doc_root) : session( - socket.get_executor().context(), std::move(buffer), doc_root) - , socket_(std::move(socket)) - , stream_(socket_, ctx) - , strand_(stream_.get_executor()) + , stream_(std::move(socket), ctx) { } // Called by the base class - ssl::stream& + ssl_stream_type& stream() { return stream_; @@ -444,18 +434,19 @@ public: void run() { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Perform the SSL handshake // Note, this is the buffered version of the handshake. stream_.async_handshake( ssl::stream_base::server, buffer_.data(), - net::bind_executor( - strand_, - std::bind( - &ssl_session::on_handshake, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + std::bind( + &ssl_session::on_handshake, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } void on_handshake( @@ -474,14 +465,15 @@ public: void do_eof() { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Perform the SSL shutdown stream_.async_shutdown( - net::bind_executor( - strand_, - std::bind( - &ssl_session::on_shutdown, - shared_from_this(), - std::placeholders::_1))); + std::bind( + &ssl_session::on_shutdown, + shared_from_this(), + std::placeholders::_1)); } void @@ -499,22 +491,18 @@ public: // Detects SSL handshakes class detect_session : public std::enable_shared_from_this { - tcp::socket socket_; + plain_stream_type stream_; ssl::context& ctx_; - net::strand< - net::io_context::executor_type> strand_; std::shared_ptr doc_root_; beast::flat_buffer buffer_; public: - explicit detect_session( tcp::socket socket, ssl::context& ctx, std::shared_ptr const& doc_root) - : socket_(std::move(socket)) + : stream_(std::move(socket)) , ctx_(ctx) - , strand_(socket_.get_executor()) , doc_root_(doc_root) { } @@ -523,17 +511,18 @@ public: void run() { - async_detect_ssl( - socket_, - buffer_, - net::bind_executor( - strand_, - std::bind( - &detect_session::on_detect, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Detect a TLS handshake + async_detect_ssl( + stream_, + buffer_, + std::bind( + &detect_session::on_detect, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); } void @@ -546,7 +535,7 @@ public: { // Launch SSL session std::make_shared( - std::move(socket_), + stream_.release_socket(), ctx_, std::move(buffer_), doc_root_)->run(); @@ -555,7 +544,7 @@ public: // Launch plain session std::make_shared( - std::move(socket_), + stream_.release_socket(), std::move(buffer_), doc_root_)->run(); } @@ -565,10 +554,7 @@ public: class listener : public std::enable_shared_from_this { ssl::context& ctx_; - net::strand< - net::io_context::executor_type> strand_; tcp::acceptor acceptor_; - tcp::socket socket_; std::shared_ptr doc_root_; public: @@ -578,9 +564,7 @@ public: tcp::endpoint endpoint, std::shared_ptr const& doc_root) : ctx_(ctx) - , strand_(ioc.get_executor()) , acceptor_(ioc) - , socket_(ioc) , doc_root_(doc_root) { beast::error_code ec; @@ -632,15 +616,15 @@ public: do_accept() { acceptor_.async_accept( - socket_, std::bind( &listener::on_accept, shared_from_this(), - std::placeholders::_1)); + std::placeholders::_1, + std::placeholders::_2)); } void - on_accept(beast::error_code ec) + on_accept(beast::error_code ec, tcp::socket sock) { if(ec) { @@ -650,7 +634,7 @@ public: { // Create the detector session and run it std::make_shared( - std::move(socket_), + std::move(sock), ctx_, doc_root_)->run(); } diff --git a/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp b/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp index e0658e86..2132c56d 100644 --- a/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp +++ b/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp @@ -18,10 +18,8 @@ #include #include #include -#include +#include #include -#include -#include #include #include #include @@ -211,6 +209,10 @@ handle_request( //------------------------------------------------------------------------------ +// The type of TLS streams +using ssl_stream_type = + beast::ssl_stream>; + // Report a failure void fail(beast::error_code ec, char const* what) @@ -253,21 +255,16 @@ class session http::async_write( self_.stream_, *sp, - net::bind_executor( - self_.strand_, - std::bind( - &session::loop, - self_.shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - sp->need_eof()))); + std::bind( + &session::loop, + self_.shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + sp->need_eof())); } }; - tcp::socket socket_; - ssl::stream stream_; - net::strand< - net::io_context::executor_type> strand_; + ssl_stream_type stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -278,12 +275,10 @@ public: // Take ownership of the socket explicit session( - tcp::socket socket, + tcp::socket&& socket, ssl::context& ctx, std::shared_ptr const& doc_root) - : socket_(std::move(socket)) - , stream_(socket_, ctx) - , strand_(socket_.get_executor()) + : stream_(std::move(socket), ctx) , doc_root_(doc_root) , lambda_(*this) { @@ -306,36 +301,38 @@ public: boost::ignore_unused(bytes_transferred); reenter(*this) { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Perform the SSL handshake yield stream_.async_handshake( ssl::stream_base::server, - net::bind_executor( - strand_, - std::bind( - &session::loop, - shared_from_this(), - std::placeholders::_1, - 0, - false))); + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0, + false)); if(ec) return fail(ec, "handshake"); for(;;) { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Make the request empty before reading, // otherwise the operation behavior is undefined. req_ = {}; // Read a request yield http::async_read(stream_, buffer_, req_, - net::bind_executor( - strand_, - std::bind( - &session::loop, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - false))); + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + false)); if(ec == http::error::end_of_stream) { // The remote host closed the connection @@ -359,16 +356,17 @@ public: res_ = nullptr; } + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + // Perform the SSL shutdown yield stream_.async_shutdown( - net::bind_executor( - strand_, - std::bind( - &session::loop, - shared_from_this(), - std::placeholders::_1, - 0, - false))); + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0, + false)); if(ec) return fail(ec, "shutdown"); diff --git a/example/http/server/stackless/http_server_stackless.cpp b/example/http/server/stackless/http_server_stackless.cpp index b3d75505..6678803c 100644 --- a/example/http/server/stackless/http_server_stackless.cpp +++ b/example/http/server/stackless/http_server_stackless.cpp @@ -16,9 +16,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -207,6 +205,9 @@ handle_request( //------------------------------------------------------------------------------ +// The type of stream to use +using stream_type = beast::tcp_stream; + // Report a failure void fail(beast::error_code ec, char const* what) @@ -248,22 +249,18 @@ class session // Write the response http::async_write( - self_.socket_, + self_.stream_, *sp, - net::bind_executor( - self_.strand_, - std::bind( - &session::loop, - self_.shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - sp->need_eof()))); + std::bind( + &session::loop, + self_.shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + sp->need_eof())); } }; - tcp::socket socket_; - net::strand< - net::io_context::executor_type> strand_; + stream_type stream_; beast::flat_buffer buffer_; std::shared_ptr doc_root_; http::request req_; @@ -276,8 +273,7 @@ public: session( tcp::socket socket, std::shared_ptr const& doc_root) - : socket_(std::move(socket)) - , strand_(socket_.get_executor()) + : stream_(std::move(socket)) , doc_root_(doc_root) , lambda_(*this) { @@ -306,16 +302,17 @@ public: // otherwise the operation behavior is undefined. req_ = {}; + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + // Read a request - yield http::async_read(socket_, buffer_, req_, - net::bind_executor( - strand_, - std::bind( - &session::loop, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2, - false))); + yield http::async_read(stream_, buffer_, req_, + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + false)); if(ec == http::error::end_of_stream) { // The remote host closed the connection @@ -340,7 +337,7 @@ public: } // Send a TCP shutdown - socket_.shutdown(tcp::socket::shutdown_send, ec); + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully }