Ensure TLS data is available for reading Returns the number of bytes read from the socket
(
conn: &mut TlsConnection,
socket: &PySSLSocket,
vm: &VirtualMachine,
)
| 2115 | /// Ensure TLS data is available for reading |
| 2116 | /// Returns the number of bytes read from the socket |
| 2117 | fn ssl_ensure_data_available( |
| 2118 | conn: &mut TlsConnection, |
| 2119 | socket: &PySSLSocket, |
| 2120 | vm: &VirtualMachine, |
| 2121 | ) -> SslResult<usize> { |
| 2122 | // Unlike OpenSSL's SSL_read, rustls requires explicit I/O |
| 2123 | if conn.wants_read() { |
| 2124 | let is_bio = socket.is_bio_mode(); |
| 2125 | |
| 2126 | // For non-BIO mode (regular sockets), check if socket is ready first |
| 2127 | // PERFORMANCE OPTIMIZATION: Only use select for sockets with timeout |
| 2128 | // - Blocking sockets (timeout=None): Skip select, recv() will block naturally |
| 2129 | // - Timeout sockets: Use select to enforce timeout |
| 2130 | // - Non-blocking sockets: Skip select, recv() will return EAGAIN immediately |
| 2131 | if !is_bio { |
| 2132 | let timeout = socket.get_socket_timeout(vm).map_err(SslError::Py)?; |
| 2133 | |
| 2134 | // Only use select if socket has a positive timeout |
| 2135 | if let Some(t) = timeout |
| 2136 | && !t.is_zero() |
| 2137 | { |
| 2138 | // Socket has timeout - use select to enforce it |
| 2139 | let timed_out = socket |
| 2140 | .sock_wait_for_io_impl(SelectKind::Read, vm) |
| 2141 | .map_err(SslError::Py)?; |
| 2142 | if timed_out { |
| 2143 | // Socket not ready within timeout - raise socket.timeout |
| 2144 | return Err(SslError::Timeout( |
| 2145 | "The read operation timed out".to_string(), |
| 2146 | )); |
| 2147 | } |
| 2148 | } |
| 2149 | // else: non-blocking socket (timeout=0) or blocking socket (timeout=None) - skip select |
| 2150 | } |
| 2151 | |
| 2152 | // Read one TLS record at a time for non-BIO sockets (matching |
| 2153 | // OpenSSL's default no-read-ahead behaviour). This prevents |
| 2154 | // consuming a close_notify that arrives alongside application data, |
| 2155 | // keeping it in the kernel buffer where select() can detect it. |
| 2156 | let data = if !is_bio { |
| 2157 | recv_one_tls_record_for_data(conn, socket, vm)? |
| 2158 | } else { |
| 2159 | match socket.sock_recv(2048, vm) { |
| 2160 | Ok(data) => data, |
| 2161 | Err(e) => { |
| 2162 | if is_blocking_io_error(&e, vm) { |
| 2163 | return Err(SslError::WantRead); |
| 2164 | } |
| 2165 | if let Err(rustls_err) = conn.process_new_packets() { |
| 2166 | return Err(SslError::from_rustls(rustls_err)); |
| 2167 | } |
| 2168 | if is_connection_closed_error(&e, vm) { |
| 2169 | return Err(SslError::Eof); |
| 2170 | } |
| 2171 | return Err(SslError::Py(e)); |
| 2172 | } |
| 2173 | } |
| 2174 | }; |
no test coverage detected