(&self, vm: &VirtualMachine)
| 4030 | |
| 4031 | #[pymethod] |
| 4032 | fn shutdown(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> { |
| 4033 | // Check current shutdown state |
| 4034 | let current_state = *self.shutdown_state.lock(); |
| 4035 | |
| 4036 | // If already completed, return immediately |
| 4037 | if current_state == ShutdownState::Completed { |
| 4038 | if self.is_bio_mode() { |
| 4039 | return Ok(vm.ctx.none()); |
| 4040 | } |
| 4041 | return Ok(self.sock.clone()); |
| 4042 | } |
| 4043 | |
| 4044 | // Get connection |
| 4045 | let mut conn_guard = self.connection.lock(); |
| 4046 | let conn = conn_guard |
| 4047 | .as_mut() |
| 4048 | .ok_or_else(|| vm.new_value_error("Connection not established"))?; |
| 4049 | |
| 4050 | let is_bio = self.is_bio_mode(); |
| 4051 | |
| 4052 | // Step 1: Send our close_notify if not already sent |
| 4053 | if current_state == ShutdownState::NotStarted { |
| 4054 | // First, flush ALL pending TLS data BEFORE sending close_notify |
| 4055 | // This is CRITICAL - close_notify must come AFTER all application data |
| 4056 | // Otherwise data loss occurs when peer receives close_notify first |
| 4057 | |
| 4058 | // Step 1a: Flush any pending TLS records from rustls internal buffer |
| 4059 | // This ensures all application data is converted to TLS records |
| 4060 | while conn.wants_write() { |
| 4061 | let mut buf = Vec::new(); |
| 4062 | conn.write_tls(&mut buf) |
| 4063 | .map_err(|e| vm.new_os_error(format!("TLS write failed: {e}")))?; |
| 4064 | if !buf.is_empty() { |
| 4065 | self.send_tls_output(buf, vm)?; |
| 4066 | } |
| 4067 | } |
| 4068 | |
| 4069 | // Step 1b: Flush pending_tls_output buffer to socket |
| 4070 | if !is_bio { |
| 4071 | // Socket mode: blocking flush to ensure data order |
| 4072 | // Must complete before sending close_notify |
| 4073 | self.blocking_flush_all_pending(vm)?; |
| 4074 | } else { |
| 4075 | // BIO mode: non-blocking flush (caller handles pending data) |
| 4076 | let _ = self.flush_pending_tls_output(vm, None); |
| 4077 | } |
| 4078 | |
| 4079 | conn.send_close_notify(); |
| 4080 | |
| 4081 | // Write close_notify to outgoing buffer/BIO |
| 4082 | self.write_pending_tls(conn, vm)?; |
| 4083 | // Ensure close_notify and any pending TLS data are flushed |
| 4084 | if !is_bio { |
| 4085 | self.flush_pending_tls_output(vm, None)?; |
| 4086 | } |
| 4087 | |
| 4088 | // Update state |
| 4089 | *self.shutdown_state.lock() = ShutdownState::SentCloseNotify; |
nothing calls this directly
no test coverage detected