1use rama_core::error::BoxErrorExt as _;
36use std::any::TypeId;
37use std::fmt;
38use std::io;
39use std::pin::Pin;
40use std::sync::Arc;
41use std::task::{Context, Poll};
42
43use parking_lot::Mutex;
44use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
45use tokio::sync::oneshot;
46
47use rama_core::bytes::Bytes;
48use rama_core::error::BoxError;
49use rama_core::extensions::Extension;
50use rama_core::extensions::Extensions;
51use rama_core::extensions::ExtensionsRef;
52use rama_core::io::Io;
53use rama_core::io::rewind::Rewind;
54use rama_core::telemetry::tracing::trace;
55use rama_net::extensions::StreamTransformed;
56use rama_utils::macros::generate_set_and_with;
57
58pub struct Upgraded {
67 io: Rewind<Box<dyn UpgradeIo>>,
68 extensions: Extensions,
69}
70
71#[derive(Clone, Extension)]
75#[extension(tags(http))]
76pub struct OnUpgrade {
77 rx: Arc<Mutex<oneshot::Receiver<Result<Upgraded, BoxError>>>>,
78}
79
80#[derive(Debug)]
85#[non_exhaustive]
86pub struct Parts<T> {
87 pub io: T,
89 pub read_buf: Bytes,
98 pub extensions: Extensions,
100}
101
102pub fn handle_upgrade<T: ExtensionsRef>(
112 msg: T,
113) -> impl Future<Output = Result<Upgraded, BoxError>> + 'static {
114 let msg_ext = msg.extensions().clone();
115 let on_upgrade = match msg_ext.get_ref::<OnUpgrade>().cloned() {
116 Some(on_upgrade) => {
117 trace!("upgrading this: {:?}", on_upgrade);
118 if on_upgrade.has_handled_upgrade() {
119 Err(BoxError::from_static_str(
120 "upgraded has already been handled",
121 ))
122 } else {
123 Ok(on_upgrade)
124 }
125 }
126 None => Err(BoxError::from_static_str("no pending update found")),
127 };
128
129 async move {
130 let upgraded = match on_upgrade {
131 Ok(on_upgrade) => on_upgrade.await?,
132 Err(err) => return Err(err),
133 };
134 Ok(upgraded)
135 }
136}
137
138pub struct Pending {
140 tx: oneshot::Sender<Result<Upgraded, BoxError>>,
141}
142
143#[must_use]
145pub fn pending() -> (Pending, OnUpgrade) {
146 let (tx, rx) = oneshot::channel();
147
148 (
149 Pending { tx },
150 OnUpgrade {
151 rx: Arc::new(Mutex::new(rx)),
152 },
153 )
154}
155
156impl Upgraded {
159 pub fn new<T>(io: T, read_buf: Bytes) -> Self
167 where
168 T: Io + Unpin + ExtensionsRef,
169 {
170 let extensions = io.extensions().clone();
171 extensions.insert(StreamTransformed {
172 by: "rama-http::Upgraded",
173 });
174 Self {
175 extensions,
176 io: Rewind::new_buffered(Box::new(io), read_buf),
177 }
178 }
179
180 generate_set_and_with! {
181 pub fn extensions(mut self, extensions: Extensions) -> Self {
182 self.extensions = extensions;
183 self
184 }
185 }
186
187 pub fn downcast<T: Io + Unpin>(self) -> Result<Parts<T>, Self> {
192 let (io, buf) = self.io.into_inner();
193 match io.__downcast() {
194 Ok(t) => Ok(Parts {
195 io: *t,
196 read_buf: buf,
197 extensions: self.extensions,
198 }),
199 Err(io) => Err(Self {
200 io: Rewind::new_buffered(io, buf),
201 extensions: self.extensions,
202 }),
203 }
204 }
205}
206
207trait UpgradeIo: Io + Unpin {
208 fn __type_id(&self) -> TypeId {
209 TypeId::of::<Self>()
210 }
211}
212
213impl<T: Io + Unpin> UpgradeIo for T {}
214
215impl dyn UpgradeIo {
216 fn __is<T: UpgradeIo>(&self) -> bool {
217 let t = TypeId::of::<T>();
218 self.__type_id() == t
219 }
220
221 fn __downcast<T: UpgradeIo>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
224 if self.__is::<T>() {
225 unsafe {
236 let raw: *mut dyn UpgradeIo = Box::into_raw(self);
237 Ok(Box::from_raw(raw.cast()))
238 }
239 } else {
240 Err(self)
241 }
242 }
243}
244
245impl ExtensionsRef for Upgraded {
246 fn extensions(&self) -> &Extensions {
247 &self.extensions
248 }
249}
250
251#[warn(clippy::missing_trait_methods)]
252impl AsyncRead for Upgraded {
253 fn poll_read(
254 mut self: Pin<&mut Self>,
255 cx: &mut Context<'_>,
256 buf: &mut ReadBuf<'_>,
257 ) -> Poll<io::Result<()>> {
258 Pin::new(&mut self.io).poll_read(cx, buf)
259 }
260}
261
262#[warn(clippy::missing_trait_methods)]
263impl AsyncWrite for Upgraded {
264 fn poll_write(
265 mut self: Pin<&mut Self>,
266 cx: &mut Context<'_>,
267 buf: &[u8],
268 ) -> Poll<io::Result<usize>> {
269 Pin::new(&mut self.io).poll_write(cx, buf)
270 }
271
272 fn poll_write_vectored(
273 mut self: Pin<&mut Self>,
274 cx: &mut Context<'_>,
275 bufs: &[io::IoSlice<'_>],
276 ) -> Poll<io::Result<usize>> {
277 Pin::new(&mut self.io).poll_write_vectored(cx, bufs)
278 }
279
280 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
281 Pin::new(&mut self.io).poll_flush(cx)
282 }
283
284 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
285 Pin::new(&mut self.io).poll_shutdown(cx)
286 }
287
288 fn is_write_vectored(&self) -> bool {
289 self.io.is_write_vectored()
290 }
291}
292
293impl fmt::Debug for Upgraded {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 f.debug_struct("Upgraded").finish()
296 }
297}
298
299impl fmt::Debug for Pending {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 f.debug_struct("Pending").finish()
302 }
303}
304
305impl OnUpgrade {
308 #[must_use]
310 pub fn has_handled_upgrade(&self) -> bool {
311 self.rx.lock().is_terminated()
312 }
313}
314
315impl Future for OnUpgrade {
316 type Output = Result<Upgraded, BoxError>;
317
318 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
319 Pin::new(&mut *self.rx.lock())
320 .poll(cx)
321 .map(|res| match res {
322 Ok(Ok(upgraded)) => Ok(upgraded),
323 Ok(Err(err)) => Err(err),
324 Err(_oneshot_canceled) => Err(BoxError::from_static_str(
325 "OnUpgrade: cancelled while expecting upgrade",
326 )),
327 })
328 }
329}
330
331impl fmt::Debug for OnUpgrade {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 f.debug_struct("OnUpgrade").field("rx", &self.rx).finish()
334 }
335}
336
337impl Pending {
340 pub fn fulfill(self, upgraded: Upgraded) {
342 trace!("pending upgrade fulfill");
343 _ = self.tx.send(Ok(upgraded));
344 }
345
346 pub fn manual(self) {
349 trace!("pending upgrade handled manually");
350 _ = self.tx.send(Err(BoxError::from_static_str(
351 "OnUpgrade: manual upgrade failed",
352 )));
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use rama_core::ServiceInput;
359 use tokio_test::io::{Builder, Mock};
360
361 use super::*;
362
363 #[test]
364 fn upgraded_downcast() {
365 let io = Builder::default().build();
366 let io = ServiceInput::new(io);
367 let upgraded = Upgraded::new(io, Bytes::new());
368 let upgraded = upgraded.downcast::<std::io::Cursor<Vec<u8>>>().unwrap_err();
369 upgraded.downcast::<ServiceInput<Mock>>().unwrap();
370 }
371
372 #[test]
373 fn upgraded_carries_stream_transformed_marker() {
374 let io = ServiceInput::new(Builder::default().build());
375 let upgraded = Upgraded::new(io, Bytes::new());
376 let marker = upgraded
377 .extensions()
378 .get_ref::<StreamTransformed>()
379 .expect("Upgraded::new must insert the StreamTransformed marker");
380 assert_eq!(marker.by, "rama-http::Upgraded");
381 }
382}