Skip to content

Commit fb4c584

Browse files
committed
fix more flush
1 parent 8f84261 commit fb4c584

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

crates/stdlib/src/ssl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2867,7 +2867,7 @@ mod _ssl {
28672867

28682868
/// Blocking flush of all pending TLS output data
28692869
/// Used during shutdown() to ensure all data is sent before closing
2870-
fn blocking_flush_all_pending(&self, vm: &VirtualMachine) -> PyResult<()> {
2870+
pub(crate) fn blocking_flush_all_pending(&self, vm: &VirtualMachine) -> PyResult<()> {
28712871
loop {
28722872
let pending_data = {
28732873
let pending = self.pending_tls_output.lock();

crates/stdlib/src/ssl/compat.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,15 @@ fn handle_handshake_complete(
12331233
}
12341234
}
12351235

1236+
// CRITICAL: Ensure all pending TLS data is sent before returning
1237+
// TLS 1.3 Finished must reach server before handshake is considered complete
1238+
// Without this, server may not process application data
1239+
if !socket.is_bio_mode() {
1240+
socket
1241+
.blocking_flush_all_pending(vm)
1242+
.map_err(SslError::Py)?;
1243+
}
1244+
12361245
Ok(true)
12371246
}
12381247

@@ -1307,11 +1316,12 @@ pub(super) fn ssl_do_handshake(
13071316
conn.send_close_notify();
13081317
// Flush any pending TLS data before sending close_notify
13091318
let _ = socket.flush_pending_tls_output(vm);
1310-
// Actually send the close_notify alert
1319+
// Actually send the close_notify alert using send_all_bytes
1320+
// for proper partial send handling and retry logic
13111321
if let Ok(alert_data) = ssl_write_tls_records(conn)
13121322
&& !alert_data.is_empty()
13131323
{
1314-
let _ = socket.sock_send(alert_data, vm);
1324+
let _ = send_all_bytes(socket, alert_data, vm);
13151325
}
13161326
}
13171327

@@ -1477,11 +1487,38 @@ pub(super) fn ssl_read(
14771487
// Check if connection needs to write data first (e.g., TLS key update, renegotiation)
14781488
// This mirrors the handshake logic which checks both wants_read() and wants_write()
14791489
if conn.wants_write() && !is_bio {
1490+
// Check deadline BEFORE attempting flush
1491+
if let Some(deadline) = deadline
1492+
&& std::time::Instant::now() >= deadline
1493+
{
1494+
return Err(SslError::Timeout(
1495+
"The read operation timed out".to_string(),
1496+
));
1497+
}
1498+
14801499
// Flush pending TLS data before continuing
14811500
let tls_data = ssl_write_tls_records(conn)?;
14821501
if !tls_data.is_empty() {
1483-
send_all_bytes(socket, tls_data, vm)?;
1502+
// Use best-effort send - don't fail READ just because WRITE couldn't complete
1503+
match send_all_bytes(socket, tls_data, vm) {
1504+
Ok(()) => {}
1505+
Err(SslError::WantWrite) => {
1506+
// Socket buffer full - acceptable during READ operation
1507+
// Pending data will be sent on next write/read call
1508+
}
1509+
Err(e) => return Err(e),
1510+
}
14841511
}
1512+
1513+
// Check deadline AFTER flush attempt
1514+
if let Some(deadline) = deadline
1515+
&& std::time::Instant::now() >= deadline
1516+
{
1517+
return Err(SslError::Timeout(
1518+
"The read operation timed out".to_string(),
1519+
));
1520+
}
1521+
14851522
// After flushing, rustls may want to read again - continue loop
14861523
continue;
14871524
}

0 commit comments

Comments
 (0)