1use rama_core::error::{BoxError, BoxErrorExt as _};
2use std::{
3 io::{self, Read, Write},
4 pin::Pin,
5 task::{Context, Poll, ready},
6};
7
8use rama_core::io::Io;
9use rama_core::{
10 extensions::{Extensions, ExtensionsRef},
11 futures::{self, SinkExt, StreamExt},
12 telemetry::tracing::{debug, trace},
13};
14use rama_http::io::upgrade;
15
16use crate::{
17 Message, ProtocolError,
18 protocol::{CloseFrame, Role, WebSocket, WebSocketConfig},
19 runtime::{
20 compat::{self, AllowStd, ContextWaker},
21 handshake::without_handshake,
22 },
23};
24
25#[derive(Debug)]
33pub struct AsyncWebSocket<S = upgrade::Upgraded> {
34 inner: WebSocket<AllowStd<S>>,
35 closing: bool,
36 ended: bool,
37 ready: bool,
42}
43
44impl<S> AsyncWebSocket<S> {
45 pub async fn from_raw_socket(stream: S, role: Role, config: Option<WebSocketConfig>) -> Self
48 where
49 S: Io + Unpin + ExtensionsRef,
50 {
51 without_handshake(stream, move |allow_std| {
52 WebSocket::from_raw_socket(allow_std, role, config)
53 })
54 .await
55 }
56
57 pub async fn from_partially_read(
60 stream: S,
61 part: Vec<u8>,
62 role: Role,
63 config: Option<WebSocketConfig>,
64 ) -> Self
65 where
66 S: Io + Unpin + ExtensionsRef,
67 {
68 without_handshake(stream, move |allow_std| {
69 WebSocket::from_partially_read(allow_std, part, role, config)
70 })
71 .await
72 }
73
74 pub(crate) fn new(ws: WebSocket<AllowStd<S>>) -> Self {
75 Self {
76 inner: ws,
77 closing: false,
78 ended: false,
79 ready: true,
80 }
81 }
82
83 fn with_context<F, R>(&mut self, ctx: Option<(ContextWaker, &mut Context<'_>)>, f: F) -> R
84 where
85 S: Unpin,
86 F: FnOnce(&mut WebSocket<AllowStd<S>>) -> R,
87 AllowStd<S>: Read + Write,
88 {
89 trace!("AsyncWebSocket.with_context");
90 if let Some((kind, ctx)) = ctx {
91 self.inner.get_mut().set_waker(kind, ctx.waker());
92 }
93 f(&mut self.inner)
94 }
95
96 pub fn into_inner(self) -> S {
98 self.inner.into_inner().into_inner()
99 }
100
101 pub fn get_ref(&self) -> &S
103 where
104 S: Io + Unpin,
105 {
106 self.inner.get_ref().get_ref()
107 }
108
109 pub fn get_mut(&mut self) -> &mut S
111 where
112 S: Io + Unpin,
113 {
114 self.inner.get_mut().get_mut()
115 }
116
117 pub fn get_config(&self) -> &WebSocketConfig {
119 self.inner.get_config()
120 }
121
122 pub async fn close(&mut self, msg: Option<CloseFrame>) -> Result<(), ProtocolError>
124 where
125 S: Io + Unpin,
126 {
127 self.send(Message::Close(msg)).await
128 }
129}
130
131impl<S: ExtensionsRef> ExtensionsRef for AsyncWebSocket<S> {
132 fn extensions(&self) -> &Extensions {
133 self.inner.extensions()
134 }
135}
136
137impl<S: Io + Unpin> AsyncWebSocket<S> {
138 #[inline]
139 pub fn send_message(
141 &mut self,
142 msg: Message,
143 ) -> impl Future<Output = Result<(), ProtocolError>> + Send + '_ {
144 self.send(msg)
145 }
146
147 pub async fn recv_message(&mut self) -> Result<Message, ProtocolError> {
148 self.next().await.ok_or_else(|| {
149 ProtocolError::Io(io::Error::new(
150 io::ErrorKind::ConnectionAborted,
151 BoxError::from_static_str(
152 "Connection closed: no messages to be received any longer",
153 ),
154 ))
155 })?
156 }
157}
158
159impl<T> futures::Stream for AsyncWebSocket<T>
160where
161 T: Io + Unpin,
162{
163 type Item = Result<Message, ProtocolError>;
164
165 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
166 trace!("Stream.poll_next");
167
168 if self.ended {
172 return Poll::Ready(None);
173 }
174
175 match ready!(self.with_context(Some((ContextWaker::Read, cx)), |s| {
176 trace!("Stream.with_context poll_next -> read()");
177 compat::cvt(s.read())
178 })) {
179 Ok(v) => Poll::Ready(Some(Ok(v))),
180 Err(e) => {
181 self.ended = true;
182 if e.is_connection_error() {
183 Poll::Ready(None)
184 } else {
185 Poll::Ready(Some(Err(e)))
186 }
187 }
188 }
189 }
190}
191
192impl<T> futures::stream::FusedStream for AsyncWebSocket<T>
193where
194 T: Io + Unpin,
195{
196 fn is_terminated(&self) -> bool {
197 self.ended
198 }
199}
200
201impl<T> futures::Sink<Message> for AsyncWebSocket<T>
202where
203 T: Io + Unpin,
204{
205 type Error = ProtocolError;
206
207 fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
208 if self.ready {
209 Poll::Ready(Ok(()))
210 } else {
211 (*self)
213 .with_context(Some((ContextWaker::Write, cx)), |s| compat::cvt(s.flush()))
214 .map(|r| {
215 self.ready = true;
216 r
217 })
218 }
219 }
220
221 fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
222 match (*self).with_context(None, |s| s.write(item)) {
223 Ok(()) => {
224 self.ready = true;
225 Ok(())
226 }
227 Err(ProtocolError::Io(err)) if err.kind() == std::io::ErrorKind::WouldBlock => {
228 self.ready = false;
231 Ok(())
232 }
233 Err(e) => {
234 self.ready = true;
235 debug!("websocket start_send error: {e}");
236 Err(e)
237 }
238 }
239 }
240
241 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
242 (*self)
243 .with_context(Some((ContextWaker::Write, cx)), |s| compat::cvt(s.flush()))
244 .map(|r| {
245 self.ready = true;
246 match r {
247 Err(err) if err.is_connection_error() => {
248 Ok(())
250 }
251 other => other,
252 }
253 })
254 }
255
256 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
257 self.ready = true;
258 let res = if self.closing {
259 (*self).with_context(Some((ContextWaker::Write, cx)), |s| s.flush())
261 } else {
262 (*self).with_context(Some((ContextWaker::Write, cx)), |s| s.close(None))
263 };
264
265 match res {
266 Ok(()) => Poll::Ready(Ok(())),
267 Err(ProtocolError::Io(err)) if err.kind() == std::io::ErrorKind::WouldBlock => {
268 trace!("WouldBlock");
269 self.closing = true;
270 Poll::Pending
271 }
272 Err(err) => {
273 if err.is_connection_error() {
274 Poll::Ready(Ok(()))
275 } else {
276 debug!("websocket close error: {}", err);
277 Poll::Ready(Err(err))
278 }
279 }
280 }
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use crate::runtime::{AsyncWebSocket, compat::AllowStd};
287 use std::io::{Read, Write};
288
289 fn is_read<T: Read>() {}
290 fn is_write<T: Write>() {}
291 fn is_unpin<T: Unpin>() {}
292
293 #[test]
294 fn web_socket_stream_has_traits() {
295 is_read::<AllowStd<tokio::net::TcpStream>>();
296 is_write::<AllowStd<tokio::net::TcpStream>>();
297 is_unpin::<AsyncWebSocket<tokio::net::TcpStream>>();
298 }
299}