Equivalent to OpenSSL's SSL_write() Writes application data to TLS connection. Automatically handles TLS record I/O as needed. = SSL_write_ex()
(
conn: &mut TlsConnection,
data: &[u8],
socket: &PySSLSocket,
vm: &VirtualMachine,
)
| 1812 | /// |
| 1813 | /// = SSL_write_ex() |
| 1814 | pub(super) fn ssl_write( |
| 1815 | conn: &mut TlsConnection, |
| 1816 | data: &[u8], |
| 1817 | socket: &PySSLSocket, |
| 1818 | vm: &VirtualMachine, |
| 1819 | ) -> SslResult<usize> { |
| 1820 | if data.is_empty() { |
| 1821 | return Ok(0); |
| 1822 | } |
| 1823 | |
| 1824 | let is_bio = socket.is_bio_mode(); |
| 1825 | |
| 1826 | // Get socket timeout and calculate deadline (= _PyDeadline_Init) |
| 1827 | let deadline = if !is_bio { |
| 1828 | match socket.get_socket_timeout(vm).map_err(SslError::Py)? { |
| 1829 | Some(timeout) if !timeout.is_zero() => Some(std::time::Instant::now() + timeout), |
| 1830 | _ => None, |
| 1831 | } |
| 1832 | } else { |
| 1833 | None |
| 1834 | }; |
| 1835 | |
| 1836 | // Flush any pending TLS output before writing new data |
| 1837 | if !is_bio { |
| 1838 | socket |
| 1839 | .flush_pending_tls_output(vm, deadline) |
| 1840 | .map_err(SslError::Py)?; |
| 1841 | } |
| 1842 | |
| 1843 | // Check if we already have data buffered from a previous retry |
| 1844 | // (prevents duplicate writes when retrying after WantWrite/WantRead) |
| 1845 | let already_buffered = *socket.write_buffered_len.lock(); |
| 1846 | |
| 1847 | // Only write plaintext if not already buffered |
| 1848 | // Track how much we wrote for partial write handling |
| 1849 | let mut bytes_written_to_rustls = 0usize; |
| 1850 | |
| 1851 | if already_buffered == 0 { |
| 1852 | // Write plaintext to rustls (= SSL_write_ex internal buffer write) |
| 1853 | bytes_written_to_rustls = { |
| 1854 | let mut writer = conn.writer(); |
| 1855 | use std::io::Write; |
| 1856 | // Use write() instead of write_all() to support partial writes. |
| 1857 | // In BIO mode (asyncio), when the internal buffer is full, |
| 1858 | // we want to write as much as possible and return that count, |
| 1859 | // rather than failing completely. |
| 1860 | match writer.write(data) { |
| 1861 | Ok(0) if !data.is_empty() => { |
| 1862 | // Buffer is full and nothing could be written. |
| 1863 | // In BIO mode, return WantWrite so the caller can |
| 1864 | // drain the outgoing BIO and retry. |
| 1865 | if is_bio { |
| 1866 | return Err(SslError::WantWrite); |
| 1867 | } |
| 1868 | return Err(SslError::Syscall("Write failed: buffer full".to_string())); |
| 1869 | } |
| 1870 | Ok(n) => n, |
| 1871 | Err(e) => { |
no test coverage detected