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

Function recv_one_tls_record

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

Read exactly one TLS record from the TCP socket. OpenSSL reads one TLS record at a time (no read-ahead by default). Rustls, however, consumes all available TCP data when fed via read_tls(). If a close_notify or other control record arrives alongside application data, the eager read drains the TCP buffer, leaving the control record in rustls's internal buffer where select() cannot see it. This ca

(socket: &PySSLSocket, vm: &VirtualMachine)

Source from the content-addressed store, hash-verified

1178/// and recv() only that many bytes. Any remaining data stays in the kernel
1179/// buffer and remains visible to select().
1180fn recv_one_tls_record(socket: &PySSLSocket, vm: &VirtualMachine) -> SslResult<PyObjectRef> {
1181 // Peek at what is available without consuming it.
1182 let peeked_obj = match socket.sock_peek(SSL3_RT_MAX_PLAIN_LENGTH, vm) {
1183 Ok(d) => d,
1184 Err(e) => {
1185 if is_blocking_io_error(&e, vm) {
1186 return Err(SslError::WantRead);
1187 }
1188 return Err(SslError::Py(e));
1189 }
1190 };
1191
1192 let peeked = ArgBytesLike::try_from_object(vm, peeked_obj)
1193 .map_err(|_| SslError::Syscall("Expected bytes-like object from peek".to_string()))?;
1194 let peeked_bytes = peeked.borrow_buf();
1195
1196 if peeked_bytes.is_empty() {
1197 // Empty peek means the peer has closed the TCP connection (FIN).
1198 // Non-blocking sockets would have returned EAGAIN/EWOULDBLOCK
1199 // (caught above as WantRead), so empty bytes here always means EOF.
1200 return Err(SslError::Eof);
1201 }
1202
1203 if peeked_bytes.len() < TLS_RECORD_HEADER_SIZE {
1204 // Not enough data for a TLS record header yet.
1205 // Read all available bytes so rustls can buffer the partial header;
1206 // this avoids busy-waiting because the kernel buffer is now empty
1207 // and select() will only wake us when new data arrives.
1208 return socket.sock_recv(peeked_bytes.len(), vm).map_err(|e| {
1209 if is_blocking_io_error(&e, vm) {
1210 SslError::WantRead
1211 } else {
1212 SslError::Py(e)
1213 }
1214 });
1215 }
1216
1217 // Parse the TLS record length from the header.
1218 let record_body_len = u16::from_be_bytes([peeked_bytes[3], peeked_bytes[4]]) as usize;
1219 let total_record_size = TLS_RECORD_HEADER_SIZE + record_body_len;
1220
1221 let recv_size = if peeked_bytes.len() >= total_record_size {
1222 // Complete record available — consume exactly one record.
1223 total_record_size
1224 } else {
1225 // Incomplete record — consume everything so the kernel buffer is
1226 // drained and select() will block until more data arrives.
1227 peeked_bytes.len()
1228 };
1229
1230 // Must drop the borrow before calling sock_recv (which re-enters Python).
1231 drop(peeked_bytes);
1232 drop(peeked);
1233
1234 socket.sock_recv(recv_size, vm).map_err(|e| {
1235 if is_blocking_io_error(&e, vm) {
1236 SslError::WantRead
1237 } else {

Callers 2

handshake_read_dataFunction · 0.85

Calls 9

is_blocking_io_errorFunction · 0.85
PyClass · 0.85
sock_peekMethod · 0.80
to_stringMethod · 0.80
ErrClass · 0.50
borrow_bufMethod · 0.45
is_emptyMethod · 0.45
lenMethod · 0.45
sock_recvMethod · 0.45

Tested by

no test coverage detected