MCPcopy Index your code
hub / github.com/RustPython/RustPython / ssl_read

Function ssl_read

crates/stdlib/src/ssl/compat.rs:1611–1806  ·  view source on GitHub ↗

Equivalent to OpenSSL's SSL_read() Reads application data from TLS connection. Automatically handles TLS record I/O as needed. = SSL_read_ex()

(
    conn: &mut TlsConnection,
    buf: &mut [u8],
    socket: &PySSLSocket,
    vm: &VirtualMachine,
)

Source from the content-addressed store, hash-verified

1609///
1610/// = SSL_read_ex()
1611pub(super) fn ssl_read(
1612 conn: &mut TlsConnection,
1613 buf: &mut [u8],
1614 socket: &PySSLSocket,
1615 vm: &VirtualMachine,
1616) -> SslResult<usize> {
1617 let is_bio = socket.is_bio_mode();
1618
1619 // Get socket timeout and calculate deadline (= _PyDeadline_Init)
1620 let deadline = if !is_bio {
1621 match socket.get_socket_timeout(vm).map_err(SslError::Py)? {
1622 Some(timeout) if !timeout.is_zero() => Some(std::time::Instant::now() + timeout),
1623 _ => None, // None = blocking (no deadline), Some(0) = non-blocking (handled below)
1624 }
1625 } else {
1626 None // BIO mode has no deadline
1627 };
1628
1629 // CRITICAL: Flush any pending TLS output before reading
1630 // This ensures data from previous write() calls is sent before we wait for response.
1631 // Without this, write() may leave data in pending_tls_output (if socket buffer was full),
1632 // and read() would timeout waiting for a response that the server never received.
1633 if !is_bio {
1634 socket
1635 .flush_pending_tls_output(vm, deadline)
1636 .map_err(SslError::Py)?;
1637 }
1638
1639 // Loop to handle TLS records and post-handshake messages
1640 // Matches SSL_read behavior which loops until data is available
1641 // - CPython uses OpenSSL's SSL_read which loops on SSL_ERROR_WANT_READ/WANT_WRITE
1642 // - We use rustls which requires manual read_tls/process_new_packets loop
1643 // - No iteration limit: relies on deadline and blocking I/O
1644 // - Blocking sockets: sock_select() and recv() wait at kernel level (no CPU busy-wait)
1645 // - Non-blocking sockets: immediate return on first WantRead
1646 // - Deadline prevents timeout issues
1647
1648 loop {
1649 // Check deadline
1650 if let Some(deadline) = deadline
1651 && std::time::Instant::now() >= deadline
1652 {
1653 // Timeout expired
1654 return Err(SslError::Timeout(
1655 "The read operation timed out".to_string(),
1656 ));
1657 }
1658 // Check if we need to read more TLS records BEFORE trying plaintext read
1659 // This ensures we don't miss data that's already been processed
1660 let needs_more_tls = conn.wants_read();
1661
1662 // Try to read plaintext from rustls buffer
1663 if let Some(n) = try_read_plaintext(conn, buf)? {
1664 if n == 0 {
1665 // EOF from TLS - close_notify received
1666 // Return ZeroReturn so Python raises SSLZeroReturnError
1667 return Err(SslError::ZeroReturn);
1668 }

Callers 1

readMethod · 0.85

Calls 15

TimeoutClass · 0.85
try_read_plaintextFunction · 0.85
ssl_write_tls_recordsFunction · 0.85
send_all_bytesFunction · 0.85
ssl_read_tls_recordsFunction · 0.85
is_bio_modeMethod · 0.80
get_socket_timeoutMethod · 0.80
to_stringMethod · 0.80
wants_readMethod · 0.80

Tested by

no test coverage detected