@@ -44,6 +44,10 @@ auto const kDefaultInitialReconnectDelay = std::chrono::seconds(1);
44
44
// Maximum duration between backoff attempts.
45
45
auto const kDefaultMaxBackoffDelay = std::chrono::seconds(30 );
46
46
47
+ // Maximum time to spend gracefully terminating the SSL session before
48
+ // closing the TCP socket.
49
+ auto const kGracefulShutdownTimeout = std::chrono::seconds(1 );
50
+
47
51
static boost::optional<net::ssl::context&> ToOptRef (
48
52
std::optional<net::ssl::context>& maybe_val) {
49
53
if (maybe_val) {
@@ -340,8 +344,28 @@ class FoxyClient : public Client,
340
344
shutting_down_ = true ;
341
345
// If any backoff is taking place, cancel that as well.
342
346
backoff_timer_.cancel ();
343
-
344
- boost::system::error_code ec = {};
347
+
348
+ // Cancels the outstanding read.
349
+ if (session_->stream .is_ssl ()) {
350
+ session_->stream .ssl ().next_layer ().cancel ();
351
+ } else {
352
+ session_->stream .plain ().cancel ();
353
+ }
354
+
355
+ // Ideally we would call session_->async_shutdown() here to gracefully
356
+ // terminate the SSL session. For unknown reasons, this call appears to
357
+ // hang indefinitely and never complete until the SDK client is
358
+ // destroyed.
359
+ //
360
+ // A workaround is to set a timeout on the operation, say 1 second. This
361
+ // gives the opportunity to shutdown gracefully and then if that fails,
362
+ // we could close the socket directly. But that also doesn't seem to
363
+ // work: even with the timeout, the operation still doesn't complete.
364
+ //
365
+ // So the most robust solution appears to be closing the socket
366
+ // directly. This is not ideal because it doesn't send a close_notify to
367
+ // the server.
368
+ boost::system::error_code ec;
345
369
session_->stream .plain ().shutdown (
346
370
boost::asio::ip::tcp::socket::shutdown_both, ec);
347
371
session_->stream .plain ().close (ec);
0 commit comments