Handle handshake completion for server-side TLS 1.3 Tries to send NewSessionTicket in non-blocking mode to avoid deadlocks. Returns true if handshake is complete and we should exit.
(
conn: &mut TlsConnection,
socket: &PySSLSocket,
_is_server: bool,
vm: &VirtualMachine,
)
| 1338 | /// Tries to send NewSessionTicket in non-blocking mode to avoid deadlocks. |
| 1339 | /// Returns true if handshake is complete and we should exit. |
| 1340 | fn handle_handshake_complete( |
| 1341 | conn: &mut TlsConnection, |
| 1342 | socket: &PySSLSocket, |
| 1343 | _is_server: bool, |
| 1344 | vm: &VirtualMachine, |
| 1345 | ) -> SslResult<bool> { |
| 1346 | if conn.is_handshaking() { |
| 1347 | return Ok(false); // Not complete yet |
| 1348 | } |
| 1349 | |
| 1350 | // Handshake is complete! |
| 1351 | // |
| 1352 | // Different behavior for BIO mode vs socket mode: |
| 1353 | // |
| 1354 | // BIO mode (CPython-compatible): |
| 1355 | // - Python code calls outgoing.read() to get pending data |
| 1356 | // - We just return here and let Python handle the data |
| 1357 | // |
| 1358 | // Socket mode (rustls-specific): |
| 1359 | // - OpenSSL automatically writes to socket in SSL_do_handshake() |
| 1360 | // - We must explicitly call write_tls() to send pending data |
| 1361 | // - Without this, client hangs waiting for server's NewSessionTicket |
| 1362 | |
| 1363 | if socket.is_bio_mode() { |
| 1364 | // BIO mode: Write pending data to outgoing BIO (one-time drain) |
| 1365 | // Python's ssl_io_loop will read from outgoing BIO |
| 1366 | if conn.wants_write() { |
| 1367 | // Call write_tls ONCE to drain pending data |
| 1368 | // Do NOT loop on wants_write() - avoid infinite loop/deadlock |
| 1369 | let tls_data = ssl_write_tls_records(conn)?; |
| 1370 | if !tls_data.is_empty() { |
| 1371 | send_all_bytes(socket, tls_data, vm, None)?; |
| 1372 | } |
| 1373 | |
| 1374 | // IMPORTANT: Don't check wants_write() again! |
| 1375 | // Python's ssl_io_loop will call do_handshake() again if needed |
| 1376 | } |
| 1377 | } else if conn.wants_write() { |
| 1378 | // Send all pending data (e.g., TLS 1.3 NewSessionTicket) to socket |
| 1379 | // Must drain ALL rustls buffer - don't break on WantWrite |
| 1380 | while conn.wants_write() { |
| 1381 | let tls_data = ssl_write_tls_records(conn)?; |
| 1382 | if tls_data.is_empty() { |
| 1383 | break; |
| 1384 | } |
| 1385 | match send_all_bytes(socket, tls_data, vm, None) { |
| 1386 | Ok(()) => {} |
| 1387 | Err(SslError::WantWrite) => { |
| 1388 | // Socket buffer full, data saved to pending_tls_output |
| 1389 | // Flush pending and continue draining rustls buffer |
| 1390 | socket |
| 1391 | .blocking_flush_all_pending(vm) |
| 1392 | .map_err(SslError::Py)?; |
| 1393 | } |
| 1394 | Err(e) => return Err(e), |
| 1395 | } |
| 1396 | } |
| 1397 | } |
no test coverage detected