Skip to main content

wasmtime_wasi/p2/
tcp.rs

1use crate::MAX_READ_SIZE_ALLOC;
2use crate::p2::bindings::sockets::network::ErrorCode;
3use crate::p2::{
4    DynInputStream, DynOutputStream, InputStream, OutputStream, Pollable, SocketResult, StreamError,
5};
6use crate::runtime::poll_now;
7use crate::sockets::{
8    MaybeSpawned, TcpListenStream, TcpReceiveStream, TcpSendStream, TcpSocket as P3Socket,
9};
10use std::future::poll_fn;
11use std::mem;
12use std::net::Shutdown;
13use std::sync::Arc;
14use std::sync::Mutex;
15use std::task::{Poll, ready};
16use wasmtime::Result;
17use wasmtime_wasi_io::streams::StreamResult;
18
19/// A TCP socket + associated p2 bookkeeping.
20pub struct TcpSocket {
21    pub(crate) inner: P3Socket,
22    pub(crate) in_progress_operation: Option<AsyncOperation>,
23    pub(crate) listener: Option<TcpListenStream>,
24    reader: Option<TcpReader>,
25    writer: Option<TcpWriter>,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub(crate) enum AsyncOperation {
30    Bind,
31    Connect,
32    Listen,
33}
34
35impl TcpSocket {
36    pub(crate) fn new(inner: P3Socket) -> Self {
37        Self {
38            inner,
39            in_progress_operation: None,
40            listener: None,
41            reader: None,
42            writer: None,
43        }
44    }
45    pub(crate) fn take_streams(&mut self) -> SocketResult<(DynInputStream, DynOutputStream)> {
46        let reader = TcpReader::new(self.inner.take_receive_stream()?);
47        let writer = TcpWriter::new(self.inner.take_send_stream()?);
48        self.reader = Some(reader.clone());
49        self.writer = Some(writer.clone());
50        let input: DynInputStream = Box::new(reader);
51        let output: DynOutputStream = Box::new(writer);
52        Ok((input, output))
53    }
54    pub(crate) fn shutdown(&mut self, how: Shutdown) -> SocketResult<()> {
55        let reader = self.reader.as_mut().ok_or(ErrorCode::InvalidState)?;
56        let writer = self.writer.as_mut().ok_or(ErrorCode::InvalidState)?;
57
58        if let Shutdown::Both | Shutdown::Read = how {
59            reader.0.lock().unwrap().shutdown();
60        }
61
62        if let Shutdown::Both | Shutdown::Write = how {
63            writer.0.lock().unwrap().shutdown();
64        }
65
66        Ok(())
67    }
68}
69
70enum ReadState {
71    Open(TcpReceiveStream),
72    Closed,
73}
74impl ReadState {
75    fn read(&mut self, size: usize) -> StreamResult<bytes::Bytes> {
76        let Self::Open(stream) = self else {
77            return Err(StreamError::Closed);
78        };
79        if size == 0 {
80            return Ok(bytes::Bytes::new());
81        }
82        let mut buf = bytes::BytesMut::zeroed(size.min(crate::MAX_READ_SIZE_ALLOC));
83        let n = match poll_now(|cx| stream.poll_read(cx, &mut buf)) {
84            None => 0,
85            Some(Ok(0)) => {
86                *self = ReadState::Closed;
87                return Err(StreamError::Closed);
88            }
89            Some(Ok(n)) => n,
90            Some(Err(e)) => {
91                *self = ReadState::Closed;
92                return Err(StreamError::LastOperationFailed(e.into()));
93            }
94        };
95
96        buf.truncate(n);
97        Ok(buf.freeze())
98    }
99
100    fn shutdown(&mut self) {
101        *self = ReadState::Closed;
102    }
103
104    fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<()> {
105        match self {
106            Self::Open(stream) => stream.poll_ready(cx),
107            Self::Closed => Poll::Ready(()),
108        }
109    }
110}
111
112#[derive(Clone)]
113struct TcpReader(Arc<Mutex<ReadState>>);
114impl TcpReader {
115    fn new(stream: TcpReceiveStream) -> Self {
116        Self(Arc::new(Mutex::new(ReadState::Open(stream))))
117    }
118}
119
120#[async_trait::async_trait]
121impl InputStream for TcpReader {
122    fn read(&mut self, size: usize) -> StreamResult<bytes::Bytes> {
123        self.0.lock().unwrap().read(size)
124    }
125}
126
127#[async_trait::async_trait]
128impl Pollable for TcpReader {
129    async fn ready(&mut self) {
130        std::future::poll_fn(|cx| self.0.lock().unwrap().poll_ready(cx)).await
131    }
132}
133
134/// A cloneable subset of StreamError
135#[derive(Debug, Clone)]
136enum WriteError {
137    Closed,
138    LastOperationFailed(ErrorCode),
139}
140impl From<WriteError> for StreamError {
141    fn from(err: WriteError) -> Self {
142        match err {
143            WriteError::Closed => StreamError::Closed,
144            WriteError::LastOperationFailed(e) => StreamError::LastOperationFailed(e.into()),
145        }
146    }
147}
148
149enum WriteState {
150    Ready(TcpSendStream, usize),
151    Writing(MaybeSpawned<Result<TcpSendStream, WriteError>>),
152    Closing(MaybeSpawned<Result<(), WriteError>>),
153    Closed(WriteError),
154}
155
156impl WriteState {
157    fn take(&mut self) -> WriteState {
158        mem::replace(self, WriteState::Closed(WriteError::Closed))
159    }
160
161    fn check_write(&mut self) -> StreamResult<usize> {
162        match poll_now(|cx| self.poll_ready(cx)) {
163            None => Ok(0),
164            Some(Ok((_, permit))) => {
165                *permit = MAX_READ_SIZE_ALLOC;
166                Ok(*permit)
167            }
168            Some(Err(e)) => Err(e),
169        }
170    }
171
172    fn write(&mut self, mut bytes: bytes::Bytes) -> StreamResult<()> {
173        let mut stream = match self {
174            WriteState::Ready(_, permit) if bytes.len() <= *permit => {
175                if bytes.is_empty() {
176                    return Ok(());
177                }
178
179                let WriteState::Ready(stream, _) = self.take() else {
180                    unreachable!()
181                };
182                stream
183            }
184            WriteState::Closed(e) => {
185                return Err(e.clone().into());
186            }
187            _ => {
188                return Err(StreamError::Trap(wasmtime::format_err!(
189                    "not permitted to write {} bytes",
190                    bytes.len()
191                )));
192            }
193        };
194
195        *self = WriteState::Writing(MaybeSpawned::poll_or_spawn(async move {
196            while !bytes.is_empty() {
197                match stream.write(&bytes).await {
198                    Ok(n) => {
199                        let _ = bytes.split_to(n);
200                    }
201                    Err(crate::sockets::ErrorCode::ConnectionBroken) => {
202                        return Err(WriteError::Closed);
203                    }
204                    Err(e) => {
205                        return Err(WriteError::LastOperationFailed(e.into()));
206                    }
207                }
208            }
209
210            Ok(stream)
211        }));
212
213        // Attempt to finish the write, surfacing potential errors immediately:
214        match poll_now(|cx| self.poll_ready(cx)) {
215            None | Some(Ok(_)) => Ok(()),
216            Some(Err(e)) => Err(e),
217        }
218    }
219
220    fn flush(&mut self) -> StreamResult<()> {
221        // `flush` is a no-op here. Writes happen on background tasks and will
222        // always be delivered to the OS as soon as possible. There's nothing
223        // for `flush` to do here that will speed up that process.
224        match self {
225            WriteState::Ready(..) | WriteState::Writing(_) | WriteState::Closing(_) => Ok(()),
226            WriteState::Closed(e) => Err(e.clone().into()),
227        }
228    }
229
230    pub(crate) fn shutdown(&mut self) {
231        *self = match self.take() {
232            // No write in progress, immediately drop the inner stream:
233            WriteState::Ready(..) => WriteState::Closed(WriteError::Closed),
234
235            // Schedule the shutdown after the current write has finished:
236            WriteState::Writing(write) => {
237                WriteState::Closing(MaybeSpawned::poll_or_spawn(async move {
238                    _ = write.into_future().await?;
239                    Ok(())
240                }))
241            }
242
243            s => s,
244        };
245    }
246
247    fn poll_ready(
248        &mut self,
249        cx: &mut std::task::Context<'_>,
250    ) -> Poll<StreamResult<(&mut TcpSendStream, &mut usize)>> {
251        match self {
252            WriteState::Writing(write) => {
253                ready!(write.poll_ready(cx));
254                let WriteState::Writing(write) = self.take() else {
255                    unreachable!()
256                };
257                *self = match write.unwrap_ready() {
258                    Ok(stream) => WriteState::Ready(stream, 0),
259                    Err(err) => WriteState::Closed(err),
260                };
261            }
262            WriteState::Closing(close) => {
263                ready!(close.poll_ready(cx));
264                let WriteState::Closing(close) = self.take() else {
265                    unreachable!()
266                };
267                *self = match close.unwrap_ready() {
268                    Ok(()) => WriteState::Closed(WriteError::Closed),
269                    Err(err) => WriteState::Closed(err),
270                };
271            }
272            _ => {}
273        }
274
275        match self {
276            WriteState::Ready(stream, permit) => match stream.poll_ready(cx) {
277                Poll::Ready(()) => Poll::Ready(Ok((stream, permit))),
278                Poll::Pending => Poll::Pending,
279            },
280            WriteState::Writing(..) | WriteState::Closing(..) => Poll::Pending,
281            WriteState::Closed(e) => Poll::Ready(Err(e.clone().into())),
282        }
283    }
284}
285
286#[derive(Clone)]
287struct TcpWriter(Arc<Mutex<WriteState>>);
288impl TcpWriter {
289    fn new(stream: TcpSendStream) -> Self {
290        Self(Arc::new(Mutex::new(WriteState::Ready(stream, 0))))
291    }
292}
293
294#[async_trait::async_trait]
295impl OutputStream for TcpWriter {
296    fn write(&mut self, bytes: bytes::Bytes) -> StreamResult<()> {
297        self.0.lock().unwrap().write(bytes)
298    }
299
300    fn flush(&mut self) -> StreamResult<()> {
301        self.0.lock().unwrap().flush()
302    }
303
304    fn check_write(&mut self) -> StreamResult<usize> {
305        self.0.lock().unwrap().check_write()
306    }
307
308    async fn cancel(&mut self) {
309        // Wait for background writes to finish in order to prevent silently
310        // dropping data that (from the guest's perspective) was already written.
311        self.ready().await
312    }
313}
314
315#[async_trait::async_trait]
316impl Pollable for TcpWriter {
317    async fn ready(&mut self) {
318        poll_fn(|cx| self.0.lock().unwrap().poll_ready(cx).map(|_| ())).await;
319    }
320}