@@ -68,8 +68,9 @@ socket::~socket() NOEXCEPT
6868
6969// Stop.
7070// ----------------------------------------------------------------------------
71- // Internal stop must call stop() or async_stop() .
71+ // The socket does not (must not) stop itself .
7272
73+ // Immediate stop (no graceful websocket closing).
7374void socket::stop () NOEXCEPT
7475{
7576 if (stopped_.load ())
@@ -88,6 +89,9 @@ void socket::do_stop() NOEXCEPT
8889{
8990 BC_ASSERT (stranded ());
9091
92+ // Release the callback closure before shutdown/close.
93+ if (websocket ()) websocket_->control_callback ();
94+
9195 boost_code ignore{};
9296 auto & socket = get_transport ();
9397
@@ -99,16 +103,14 @@ void socket::do_stop() NOEXCEPT
99103 // Any asynchronous send, receive or connect operations are canceled
100104 // immediately, and will complete with the operation_aborted error.
101105 socket.close (ignore);
102-
103- // Discard the optional.
104- websocket_.reset ();
105106}
106107
107- // Called internally from stranded handle_ws_event, when peer closes websocket.
108- // That ensures derived proxy has a chance to invoke stopping, and dispatch
109- // here ensures websocket_->async_close() will get invoked before thread stop.
108+ // Lazy stop (graceful websocket closing).
110109void socket::async_stop () NOEXCEPT
111110{
111+ if (stopped_.load ())
112+ return ;
113+
112114 // Stop flag accelerates work stoppage, as it does not wait on strand.
113115 stopped_.store (true );
114116
@@ -152,7 +154,6 @@ asio::socket& socket::get_transport() NOEXCEPT
152154void socket::accept (asio::acceptor& acceptor,
153155 result_handler&& handler) NOEXCEPT
154156{
155- BC_ASSERT_MSG (!websocket_.has_value (), " socket is upgraded" );
156157 BC_ASSERT_MSG (!socket_.is_open (), " accept on open socket" );
157158
158159 // Closure of the acceptor, not the socket, releases this handler.
@@ -428,6 +429,11 @@ void socket::do_ws_write(const asio::const_buffer& in, bool binary,
428429void socket::do_ws_event (ws::frame_type kind,
429430 const std::string_view& data) NOEXCEPT
430431{
432+ // Must not post to the iocontext once closed, and this is under control of
433+ // the websocket, so must be guarded here. Otherwise the socket will leak.
434+ if (stopped ())
435+ return ;
436+
431437 // Takes ownership of the string.
432438 boost::asio::dispatch (strand_,
433439 std::bind (&socket::handle_ws_event,
@@ -526,8 +532,7 @@ void socket::handle_http_read(const boost_code& ec, size_t size,
526532
527533 if (!ec && beast::websocket::is_upgrade (request.get ()))
528534 {
529- set_websocket (request.get ());
530- handler (error::upgraded, size);
535+ handler (set_websocket (request.get ()), size);
531536 return ;
532537 }
533538
@@ -611,8 +616,9 @@ void socket::handle_ws_event(ws::frame_type kind,
611616 const std::string& data) NOEXCEPT
612617{
613618 BC_ASSERT (stranded ());
614- // //BC_ASSERT(websocket());
615619
620+ // Beast sends the necessary responses during our read.
621+ // Close will be picked up in our async read/write handlers.
616622 switch (kind)
617623 {
618624 case ws::frame_type::ping:
@@ -623,7 +629,6 @@ void socket::handle_ws_event(ws::frame_type kind,
623629 break ;
624630 case ws::frame_type::close:
625631 LOGX (" WS close [" << authority () << " ] " << websocket_->reason ());
626- async_stop ();
627632 break ;
628633 }
629634}
@@ -680,23 +685,36 @@ bool socket::websocket() const NOEXCEPT
680685 return websocket_.has_value ();
681686}
682687
683- void socket::set_websocket (const http::request& request) NOEXCEPT
688+ code socket::set_websocket (const http::request& request) NOEXCEPT
684689{
685690 BC_ASSERT (stranded ());
686691 BC_ASSERT (!websocket ());
687692
688- websocket_.emplace (std::move (socket_));
689- websocket_->set_option (ws::decorator{[](http::fields& header) NOEXCEPT
693+ try
690694 {
691- // Customize the response header.
692- header.set (http::field::server, " libbitcoin/4.0" );
693- }});
694- websocket_->accept (request);
695- websocket_->binary (true );
695+ websocket_.emplace (std::move (socket_));
696+ websocket_->set_option (ws::decorator
697+ {
698+ [](http::fields& header) NOEXCEPT
699+ {
700+ // Customize the response header.
701+ header.set (http::field::server, " libbitcoin/4.0" );
702+ }
703+ });
696704
697- // Handle ping, pong, close.
698- websocket_->control_callback (std::bind (&socket::do_ws_event,
699- shared_from_this (), _1, _2));
705+ // Handle ping, pong, close - must be cleared on stop.
706+ websocket_->control_callback (std::bind (&socket::do_ws_event,
707+ shared_from_this (), _1, _2));
708+
709+ websocket_->binary (true );
710+ websocket_->accept (request);
711+ return error::upgraded;
712+ }
713+ catch (const std::exception& LOG_ONLY (e))
714+ {
715+ LOGF (" Exception @ set_websocket: " << e.what ());
716+ return error::operation_failed;
717+ }
700718}
701719
702720BC_POP_WARNING ()
0 commit comments