@@ -2724,16 +2724,12 @@ mod _ssl {
27242724 recv_method. call ( ( self . sock . clone ( ) , vm. ctx . new_int ( size) ) , vm)
27252725 }
27262726
2727- pub ( crate ) fn sock_send (
2728- & self ,
2729- data : Vec < u8 > ,
2730- vm : & VirtualMachine ,
2731- ) -> PyResult < PyObjectRef > {
2727+ pub ( crate ) fn sock_send ( & self , data : & [ u8 ] , vm : & VirtualMachine ) -> PyResult < PyObjectRef > {
27322728 // In BIO mode, write to outgoing BIO
27332729 if let Some ( ref bio) = self . outgoing_bio {
27342730 let bio_obj: PyObjectRef = bio. clone ( ) . into ( ) ;
27352731 let write_method = bio_obj. get_attr ( "write" , vm) ?;
2736- return write_method. call ( ( vm. ctx . new_bytes ( data) , ) , vm) ;
2732+ return write_method. call ( ( vm. ctx . new_bytes ( data. to_vec ( ) ) , ) , vm) ;
27372733 }
27382734
27392735 // Normal socket mode
@@ -2742,7 +2738,7 @@ mod _ssl {
27422738
27432739 // Call socket.socket.send(self.sock, data)
27442740 let send_method = socket_class. get_attr ( "send" , vm) ?;
2745- send_method. call ( ( self . sock . clone ( ) , vm. ctx . new_bytes ( data) ) , vm)
2741+ send_method. call ( ( self . sock . clone ( ) , vm. ctx . new_bytes ( data. to_vec ( ) ) ) , vm)
27462742 }
27472743
27482744 /// Flush any pending TLS output data to the socket
@@ -2767,8 +2763,7 @@ mod _ssl {
27672763 ) ;
27682764 }
27692765
2770- let to_send = pending[ sent_total..] . to_vec ( ) ;
2771- match self . sock_send ( to_send, vm) {
2766+ match self . sock_send ( & pending[ sent_total..] , vm) {
27722767 Ok ( result) => {
27732768 let sent: usize = result. try_to_value :: < isize > ( vm) ?. try_into ( ) . unwrap_or ( 0 ) ;
27742769 if sent == 0 {
@@ -2826,8 +2821,7 @@ mod _ssl {
28262821 ) ;
28272822 }
28282823
2829- let to_send = buf[ sent_total..] . to_vec ( ) ;
2830- match self . sock_send ( to_send, vm) {
2824+ match self . sock_send ( & buf[ sent_total..] , vm) {
28312825 Ok ( result) => {
28322826 let sent: usize = result. try_to_value :: < isize > ( vm) ?. try_into ( ) . unwrap_or ( 0 ) ;
28332827 if sent == 0 {
@@ -2865,9 +2859,12 @@ mod _ssl {
28652859 Ok ( ( ) )
28662860 }
28672861
2868- /// Blocking flush of all pending TLS output data
2869- /// Used during shutdown() to ensure all data is sent before closing
2862+ /// Flush all pending TLS output data, respecting socket timeout
2863+ /// Used during handshake completion and shutdown() to ensure all data is sent
28702864 pub ( crate ) fn blocking_flush_all_pending ( & self , vm : & VirtualMachine ) -> PyResult < ( ) > {
2865+ // Get socket timeout to respect during flush
2866+ let timeout = self . get_socket_timeout ( vm) ?;
2867+
28712868 loop {
28722869 let pending_data = {
28732870 let pending = self . pending_tls_output . lock ( ) ;
@@ -2877,21 +2874,30 @@ mod _ssl {
28772874 pending. clone ( )
28782875 } ;
28792876
2880- // Wait for socket to be writable (blocking, no timeout)
2877+ // Wait for socket to be writable, respecting socket timeout
28812878 let py_socket: PyRef < PySocket > = self . sock . clone ( ) . try_into_value ( vm) ?;
28822879 let socket = py_socket
28832880 . sock ( )
28842881 . map_err ( |e| vm. new_os_error ( format ! ( "Failed to get socket: {e}" ) ) ) ?;
2885- let _ = sock_select ( & socket, SelectKind :: Write , None ) ;
2882+ let timed_out = sock_select ( & socket, SelectKind :: Write , timeout)
2883+ . map_err ( |e| vm. new_os_error ( format ! ( "select failed: {e}" ) ) ) ?;
2884+
2885+ if timed_out {
2886+ return Err (
2887+ timeout_error_msg ( vm, "The write operation timed out" . to_string ( ) ) . upcast ( ) ,
2888+ ) ;
2889+ }
28862890
28872891 // Try to send pending data
2888- match self . sock_send ( pending_data. clone ( ) , vm) {
2892+ match self . sock_send ( & pending_data, vm) {
28892893 Ok ( result) => {
28902894 let sent: usize = result. try_to_value :: < isize > ( vm) ?. try_into ( ) . unwrap_or ( 0 ) ;
28912895 if sent > 0 {
28922896 let mut pending = self . pending_tls_output . lock ( ) ;
28932897 pending. drain ( ..sent) ;
28942898 }
2899+ // If sent == 0, socket wasn't ready despite select() saying so
2900+ // Continue loop to retry - this avoids infinite loops
28952901 }
28962902 Err ( e) => {
28972903 if is_blocking_io_error ( & e, vm) {
@@ -3555,6 +3561,13 @@ mod _ssl {
35553561 let is_bio = self . is_bio_mode ( ) ;
35563562 let data: & [ u8 ] = data_bytes. as_ref ( ) ;
35573563
3564+ // CRITICAL: Flush any pending TLS data before writing new data
3565+ // This ensures TLS 1.3 Finished message reaches server before application data
3566+ // Without this, server may not be ready to process our data
3567+ if !is_bio {
3568+ self . flush_pending_tls_output ( vm) ?;
3569+ }
3570+
35583571 // Write data in chunks to avoid filling the internal TLS buffer
35593572 // rustls has a limited internal buffer, so we need to flush periodically
35603573 const CHUNK_SIZE : usize = 16384 ; // 16KB chunks (typical TLS record size)
0 commit comments