Coverage Report

Created: 2026-09-04 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustls-0.23.43/src/stream.rs
Line
Count
Source
1
use core::ops::{Deref, DerefMut};
2
use std::io::{BufRead, IoSlice, Read, Result, Write};
3
4
use crate::conn::{ConnectionCommon, SideData};
5
6
/// This type implements `io::Read` and `io::Write`, encapsulating
7
/// a Connection `C` and an underlying transport `T`, such as a socket.
8
///
9
/// Relies on [`ConnectionCommon::complete_io()`] to perform the necessary I/O.
10
///
11
/// This allows you to use a rustls Connection like a normal stream.
12
#[derive(Debug)]
13
pub struct Stream<'a, C: 'a + ?Sized, T: 'a + Read + Write + ?Sized> {
14
    /// Our TLS connection
15
    pub conn: &'a mut C,
16
17
    /// The underlying transport, like a socket
18
    pub sock: &'a mut T,
19
}
20
21
impl<'a, C, T, S> Stream<'a, C, T>
22
where
23
    C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
24
    T: 'a + Read + Write,
25
    S: SideData,
26
{
27
    /// Make a new Stream using the Connection `conn` and socket-like object
28
    /// `sock`.  This does not fail and does no IO.
29
0
    pub fn new(conn: &'a mut C, sock: &'a mut T) -> Self {
30
0
        Self { conn, sock }
31
0
    }
32
33
    /// If we're handshaking, complete all the IO for that.
34
    /// If we have data to write, write it all.
35
0
    fn complete_prior_io(&mut self) -> Result<()> {
36
0
        if self.conn.is_handshaking() {
37
0
            self.conn.complete_io(self.sock)?;
38
0
        }
39
40
0
        if self.conn.wants_write() {
41
0
            self.conn.complete_io(self.sock)?;
42
0
        }
43
44
0
        Ok(())
45
0
    }
Unexecuted instantiation: <rustls::stream::Stream<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>>>::complete_prior_io
Unexecuted instantiation: <rustls::stream::Stream<_, _>>::complete_prior_io
46
47
0
    fn prepare_read(&mut self) -> Result<()> {
48
0
        self.complete_prior_io()?;
49
50
        // We call complete_io() in a loop since a single call may read only
51
        // a partial packet from the underlying transport. A full packet is
52
        // needed to get more plaintext, which we must do if EOF has not been
53
        // hit.
54
0
        while self.conn.wants_read() {
55
0
            if self.conn.complete_io(self.sock)?.0 == 0 {
56
0
                break;
57
0
            }
58
        }
59
60
0
        Ok(())
61
0
    }
Unexecuted instantiation: <rustls::stream::Stream<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>>>::prepare_read
Unexecuted instantiation: <rustls::stream::Stream<_, _>>::prepare_read
62
63
    // Implements `BufRead::fill_buf` but with more flexible lifetimes, so StreamOwned can reuse it
64
0
    fn fill_buf(mut self) -> Result<&'a [u8]>
65
0
    where
66
0
        S: 'a,
67
    {
68
0
        self.prepare_read()?;
69
0
        self.conn.reader().into_first_chunk()
70
0
    }
71
}
72
73
impl<'a, C, T, S> Read for Stream<'a, C, T>
74
where
75
    C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
76
    T: 'a + Read + Write,
77
    S: SideData,
78
{
79
0
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
80
0
        self.prepare_read()?;
81
0
        self.conn.reader().read(buf)
82
0
    }
Unexecuted instantiation: <rustls::stream::Stream<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>> as std::io::Read>::read
Unexecuted instantiation: <rustls::stream::Stream<_, _> as std::io::Read>::read
83
84
    #[cfg(read_buf)]
85
    fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_, u8>) -> Result<()> {
86
        self.prepare_read()?;
87
        self.conn.reader().read_buf(cursor)
88
    }
89
}
90
91
impl<'a, C, T, S> BufRead for Stream<'a, C, T>
92
where
93
    C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
94
    T: 'a + Read + Write,
95
    S: 'a + SideData,
96
{
97
0
    fn fill_buf(&mut self) -> Result<&[u8]> {
98
        // reborrow to get an owned `Stream`
99
0
        Stream {
100
0
            conn: self.conn,
101
0
            sock: self.sock,
102
0
        }
103
0
        .fill_buf()
104
0
    }
105
106
0
    fn consume(&mut self, amt: usize) {
107
0
        self.conn.reader().consume(amt)
108
0
    }
109
}
110
111
impl<'a, C, T, S> Write for Stream<'a, C, T>
112
where
113
    C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
114
    T: 'a + Read + Write,
115
    S: SideData,
116
{
117
0
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
118
0
        self.complete_prior_io()?;
119
120
0
        let len = self.conn.writer().write(buf)?;
121
122
        // Try to write the underlying transport here, but don't let
123
        // any errors mask the fact we've consumed `len` bytes.
124
        // Callers will learn of permanent errors on the next call.
125
0
        let _ = self.conn.complete_io(self.sock);
126
127
0
        Ok(len)
128
0
    }
Unexecuted instantiation: <rustls::stream::Stream<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>> as std::io::Write>::write
Unexecuted instantiation: <rustls::stream::Stream<_, _> as std::io::Write>::write
129
130
0
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
131
0
        self.complete_prior_io()?;
132
133
0
        let len = self
134
0
            .conn
135
0
            .writer()
136
0
            .write_vectored(bufs)?;
137
138
        // Try to write the underlying transport here, but don't let
139
        // any errors mask the fact we've consumed `len` bytes.
140
        // Callers will learn of permanent errors on the next call.
141
0
        let _ = self.conn.complete_io(self.sock);
142
143
0
        Ok(len)
144
0
    }
145
146
0
    fn flush(&mut self) -> Result<()> {
147
0
        self.complete_prior_io()?;
148
149
0
        self.conn.writer().flush()?;
150
0
        if self.conn.wants_write() {
151
0
            self.conn.complete_io(self.sock)?;
152
0
        }
153
0
        Ok(())
154
0
    }
155
}
156
157
/// This type implements `io::Read` and `io::Write`, encapsulating
158
/// and owning a Connection `C` and an underlying transport `T`, such as a socket.
159
///
160
/// Relies on [`ConnectionCommon::complete_io()`] to perform the necessary I/O.
161
///
162
/// This allows you to use a rustls Connection like a normal stream.
163
#[derive(Debug)]
164
pub struct StreamOwned<C: Sized, T: Read + Write + Sized> {
165
    /// Our connection
166
    pub conn: C,
167
168
    /// The underlying transport, like a socket
169
    pub sock: T,
170
}
171
172
impl<C, T, S> StreamOwned<C, T>
173
where
174
    C: DerefMut + Deref<Target = ConnectionCommon<S>>,
175
    T: Read + Write,
176
    S: SideData,
177
{
178
    /// Make a new StreamOwned taking the Connection `conn` and socket-like
179
    /// object `sock`.  This does not fail and does no IO.
180
    ///
181
    /// This is the same as `Stream::new` except `conn` and `sock` are
182
    /// moved into the StreamOwned.
183
0
    pub fn new(conn: C, sock: T) -> Self {
184
0
        Self { conn, sock }
185
0
    }
186
187
    /// Get a reference to the underlying socket
188
0
    pub fn get_ref(&self) -> &T {
189
0
        &self.sock
190
0
    }
191
192
    /// Get a mutable reference to the underlying socket
193
0
    pub fn get_mut(&mut self) -> &mut T {
194
0
        &mut self.sock
195
0
    }
Unexecuted instantiation: <rustls::stream::StreamOwned<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>>>::get_mut
Unexecuted instantiation: <rustls::stream::StreamOwned<_, _>>::get_mut
196
197
    /// Extract the `conn` and `sock` parts from the `StreamOwned`
198
0
    pub fn into_parts(self) -> (C, T) {
199
0
        (self.conn, self.sock)
200
0
    }
201
}
202
203
impl<'a, C, T, S> StreamOwned<C, T>
204
where
205
    C: DerefMut + Deref<Target = ConnectionCommon<S>>,
206
    T: Read + Write,
207
    S: SideData,
208
{
209
0
    fn as_stream(&'a mut self) -> Stream<'a, C, T> {
210
0
        Stream {
211
0
            conn: &mut self.conn,
212
0
            sock: &mut self.sock,
213
0
        }
214
0
    }
Unexecuted instantiation: <rustls::stream::StreamOwned<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>>>::as_stream
Unexecuted instantiation: <rustls::stream::StreamOwned<_, _>>::as_stream
215
}
216
217
impl<C, T, S> Read for StreamOwned<C, T>
218
where
219
    C: DerefMut + Deref<Target = ConnectionCommon<S>>,
220
    T: Read + Write,
221
    S: SideData,
222
{
223
0
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
224
0
        self.as_stream().read(buf)
225
0
    }
Unexecuted instantiation: <rustls::stream::StreamOwned<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>> as std::io::Read>::read
Unexecuted instantiation: <rustls::stream::StreamOwned<_, _> as std::io::Read>::read
226
227
    #[cfg(read_buf)]
228
    fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_, u8>) -> Result<()> {
229
        self.as_stream().read_buf(cursor)
230
    }
231
}
232
233
impl<C, T, S> BufRead for StreamOwned<C, T>
234
where
235
    C: DerefMut + Deref<Target = ConnectionCommon<S>>,
236
    T: Read + Write,
237
    S: 'static + SideData,
238
{
239
0
    fn fill_buf(&mut self) -> Result<&[u8]> {
240
0
        self.as_stream().fill_buf()
241
0
    }
242
243
0
    fn consume(&mut self, amt: usize) {
244
0
        self.as_stream().consume(amt)
245
0
    }
246
}
247
248
impl<C, T, S> Write for StreamOwned<C, T>
249
where
250
    C: DerefMut + Deref<Target = ConnectionCommon<S>>,
251
    T: Read + Write,
252
    S: SideData,
253
{
254
0
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
255
0
        self.as_stream().write(buf)
256
0
    }
Unexecuted instantiation: <rustls::stream::StreamOwned<rustls::client::client_conn::connection::ClientConnection, ureq::unversioned::transport::io::TransportAdapter<alloc::boxed::Box<dyn ureq::unversioned::transport::Transport>>> as std::io::Write>::write
Unexecuted instantiation: <rustls::stream::StreamOwned<_, _> as std::io::Write>::write
257
258
0
    fn flush(&mut self) -> Result<()> {
259
0
        self.as_stream().flush()
260
0
    }
261
}
262
263
#[cfg(test)]
264
mod tests {
265
    use std::net::TcpStream;
266
267
    use super::{Stream, StreamOwned};
268
    use crate::client::ClientConnection;
269
    use crate::server::ServerConnection;
270
271
    #[test]
272
    fn stream_can_be_created_for_connection_and_tcpstream() {
273
        type _Test<'a> = Stream<'a, ClientConnection, TcpStream>;
274
    }
275
276
    #[test]
277
    fn streamowned_can_be_created_for_client_and_tcpstream() {
278
        type _Test = StreamOwned<ClientConnection, TcpStream>;
279
    }
280
281
    #[test]
282
    fn streamowned_can_be_created_for_server_and_tcpstream() {
283
        type _Test = StreamOwned<ServerConnection, TcpStream>;
284
    }
285
}