@@ -4119,45 +4119,115 @@ mod _ssl {
41194119 peer_closed = true ;
41204120 }
41214121 } else if let Some ( timeout) = timeout_mode {
4122- // All socket modes (blocking, timeout, non-blocking):
4123- // Return immediately after sending our close_notify.
4124- //
4125- // This matches CPython/OpenSSL behavior where SSL_shutdown()
4126- // returns after sending close_notify, allowing the app to
4127- // close the socket without waiting for peer's close_notify.
4128- //
4129- // Waiting for peer's close_notify can cause deadlock with
4130- // asyncore-based servers where both sides wait for the other's
4131- // close_notify before closing the connection.
4132-
4133- // Ensure all pending TLS data is sent before returning
4134- // This prevents data loss when rustls drains its buffer
4135- // but the socket couldn't accept all data immediately
4136- drop ( conn_guard) ;
4137-
4138- // Respect socket timeout settings for flushing pending TLS data
41394122 match timeout {
41404123 Some ( 0.0 ) => {
4141- // Non-blocking: best-effort flush, ignore errors
4142- // to avoid deadlock with asyncore-based servers
4124+ // Non-blocking: return immediately after sending close_notify.
4125+ // Don't wait for peer's close_notify to avoid blocking.
4126+ drop ( conn_guard) ;
4127+ // Best-effort flush; WouldBlock is expected in non-blocking mode.
4128+ // Other errors indicate close_notify may not have been sent,
4129+ // but we still complete shutdown to avoid inconsistent state.
41434130 let _ = self . flush_pending_tls_output ( vm, None ) ;
4131+ * self . shutdown_state . lock ( ) = ShutdownState :: Completed ;
4132+ * self . connection . lock ( ) = None ;
4133+ return Ok ( self . sock . clone ( ) ) ;
41444134 }
4145- Some ( _t) => {
4146- // Timeout mode: use flush with socket's timeout
4147- // Errors (including timeout) are propagated to caller
4148- self . flush_pending_tls_output ( vm, None ) ?;
4149- }
4150- None => {
4151- // Blocking mode: wait until all pending data is sent
4152- self . blocking_flush_all_pending ( vm) ?;
4135+ _ => {
4136+ // Blocking or timeout mode: wait for peer's close_notify.
4137+ // This is proper TLS shutdown - we should receive peer's
4138+ // close_notify before closing the connection.
4139+ drop ( conn_guard) ;
4140+
4141+ // Flush our close_notify first
4142+ if timeout. is_none ( ) {
4143+ self . blocking_flush_all_pending ( vm) ?;
4144+ } else {
4145+ self . flush_pending_tls_output ( vm, None ) ?;
4146+ }
4147+
4148+ // Calculate deadline for timeout mode
4149+ let deadline = timeout. map ( |t| {
4150+ std:: time:: Instant :: now ( ) + std:: time:: Duration :: from_secs_f64 ( t)
4151+ } ) ;
4152+
4153+ // Wait for peer's close_notify
4154+ loop {
4155+ // Re-acquire connection lock for each iteration
4156+ let mut conn_guard = self . connection . lock ( ) ;
4157+ let conn = match conn_guard. as_mut ( ) {
4158+ Some ( c) => c,
4159+ None => break , // Connection already closed
4160+ } ;
4161+
4162+ // Check if peer already sent close_notify
4163+ if self . check_peer_closed ( conn, vm) ? {
4164+ break ;
4165+ }
4166+
4167+ drop ( conn_guard) ;
4168+
4169+ // Check timeout
4170+ let remaining_timeout = if let Some ( dl) = deadline {
4171+ let now = std:: time:: Instant :: now ( ) ;
4172+ if now >= dl {
4173+ // Timeout reached - raise TimeoutError
4174+ return Err ( vm. new_exception_msg (
4175+ vm. ctx . exceptions . timeout_error . to_owned ( ) ,
4176+ "The read operation timed out" . to_owned ( ) ,
4177+ ) ) ;
4178+ }
4179+ Some ( dl - now)
4180+ } else {
4181+ None // Blocking mode: no timeout
4182+ } ;
4183+
4184+ // Wait for socket to be readable
4185+ let timed_out = self . sock_wait_for_io_with_timeout (
4186+ SelectKind :: Read ,
4187+ remaining_timeout,
4188+ vm,
4189+ ) ?;
4190+
4191+ if timed_out {
4192+ // Timeout waiting for peer's close_notify
4193+ // Raise TimeoutError
4194+ return Err ( vm. new_exception_msg (
4195+ vm. ctx . exceptions . timeout_error . to_owned ( ) ,
4196+ "The read operation timed out" . to_owned ( ) ,
4197+ ) ) ;
4198+ }
4199+
4200+ // Try to read data from socket
4201+ let mut conn_guard = self . connection . lock ( ) ;
4202+ let conn = match conn_guard. as_mut ( ) {
4203+ Some ( c) => c,
4204+ None => break ,
4205+ } ;
4206+
4207+ // Read and process any incoming TLS data
4208+ match self . try_read_close_notify ( conn, vm) {
4209+ Ok ( closed) => {
4210+ if closed {
4211+ break ;
4212+ }
4213+ // Check again after processing
4214+ if self . check_peer_closed ( conn, vm) ? {
4215+ break ;
4216+ }
4217+ }
4218+ Err ( _) => {
4219+ // Socket error - peer likely closed connection
4220+ break ;
4221+ }
4222+ }
4223+ }
4224+
4225+ // Shutdown complete
4226+ * self . shutdown_state . lock ( ) = ShutdownState :: Completed ;
4227+ * self . connection . lock ( ) = None ;
4228+ return Ok ( self . sock . clone ( ) ) ;
41534229 }
41544230 }
4155-
4156- // Set shutdown_state first to ensure atomic visibility
4157- // This prevents read/write race conditions during shutdown
4158- * self . shutdown_state . lock ( ) = ShutdownState :: Completed ;
4159- * self . connection . lock ( ) = None ;
4160- return Ok ( self . sock . clone ( ) ) ;
41614231 }
41624232
41634233 // Step 3: Check again if peer has sent close_notify (non-blocking/BIO mode only)
0 commit comments