Skip to main content

rama_http/layer/upgrade/
service.rs

1//! upgrade service to handle branching into http upgrade services
2//!
3//! See [`UpgradeService`] for more details.
4
5use super::Upgraded;
6use crate::opentelemetry::version_as_protocol_version;
7use rama_core::Layer;
8use rama_core::error::{BoxError, ErrorExt as _};
9use rama_core::error_sink::ErrorSink;
10use rama_core::extensions::{Extension, ExtensionsRef};
11use rama_core::layer::{ConsumeErrLayer, MapOutputLayer};
12use rama_core::rt::Executor;
13use rama_core::telemetry::tracing::{self, Instrument};
14use rama_core::{Service, extensions::Extensions, matcher::Matcher, service::BoxService};
15use rama_http_types::Request;
16use rama_utils::macros::define_inner_service_accessors;
17use std::{convert::Infallible, fmt, future::Future, pin::Pin, sync::Arc};
18
19/// Upgrade service can be used to handle the possibility of upgrading a request,
20/// after which it will pass down the transport RW to the attached upgrade service.
21pub struct UpgradeService<S, O> {
22    handlers: Vec<Arc<UpgradeHandler<O>>>,
23    inner: S,
24    exec: Executor,
25    error_sink: Arc<dyn ErrorSink>,
26}
27
28/// Handshake response produced by the responder in a
29/// [`UpgradeLayer::new_with_services`](super::UpgradeLayer::new_with_services)
30/// configuration.
31///
32/// Call [`Self::with_handler`] to attach a response-local continuation and turn
33/// it into the [`UpgradeOutput`] expected by [`UpgradeLayer::new`](super::UpgradeLayer::new).
34#[derive(Clone, Debug)]
35pub struct UpgradeResponse<I, O> {
36    /// Response that should be returned
37    pub response: O,
38    /// Request that caused this upgrade
39    pub request: I,
40    /// Extensions which will be applied to the [`Upgraded`] io
41    /// if the upgrade was successful
42    pub extensions: Extensions,
43}
44
45impl<I, O> UpgradeResponse<I, O> {
46    /// Create an upgrade response without extra extensions.
47    #[must_use]
48    pub fn new(request: I, response: O) -> Self {
49        Self {
50            response,
51            request,
52            extensions: Extensions::new(),
53        }
54    }
55
56    /// Add an extension which will be applied to the [`Upgraded`] I/O.
57    #[must_use]
58    pub fn with_extension<T: Extension>(self, extension: T) -> Self {
59        self.extensions.insert(extension);
60        self
61    }
62
63    /// Attach a response-local, one-shot handler for the upgraded connection.
64    ///
65    /// The returned [`UpgradeOutput`] is accepted by [`UpgradeLayer::new`](super::UpgradeLayer::new).
66    /// The handler can own resources established while producing this response,
67    /// such as an egress connection, without storing them in [`Extensions`].
68    pub fn with_handler<F, Fut, T, E>(self, handler: F) -> UpgradeOutput<I, O>
69    where
70        F: FnOnce(Upgraded) -> Fut + Send + 'static,
71        Fut: Future<Output = Result<T, E>> + Send + 'static,
72        E: Into<BoxError>,
73    {
74        UpgradeOutput {
75            response: self.response,
76            request: self.request,
77            extensions: self.extensions,
78            handler: Box::new(move |upgraded| {
79                Box::pin(async move { handler(upgraded).await.map(drop).map_err(Into::into) })
80            }),
81        }
82    }
83}
84
85pub(crate) type UpgradeHandlerFuture =
86    Pin<Box<dyn Future<Output = Result<(), BoxError>> + Send + 'static>>;
87pub(crate) type ResponseUpgradeHandler =
88    Box<dyn FnOnce(Upgraded) -> UpgradeHandlerFuture + Send + 'static>;
89
90/// Complete output of a service registered with [`UpgradeLayer::new`](super::UpgradeLayer::new).
91///
92/// Besides the handshake response, it contains a response-local handler which
93/// owns everything needed to serve the upgraded connection.
94#[must_use]
95pub struct UpgradeOutput<I, O> {
96    /// Response that should be returned.
97    pub response: O,
98    /// Request that caused this upgrade.
99    pub request: I,
100    /// Extensions which will be applied to the [`Upgraded`] I/O.
101    pub extensions: Extensions,
102    pub(crate) handler: ResponseUpgradeHandler,
103}
104
105impl<I, O> fmt::Debug for UpgradeOutput<I, O>
106where
107    I: fmt::Debug,
108    O: fmt::Debug,
109{
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        f.debug_struct("UpgradeOutput")
112            .field("response", &self.response)
113            .field("request", &self.request)
114            .field("extensions", &self.extensions)
115            .finish_non_exhaustive()
116    }
117}
118
119/// UpgradeHandler is a helper struct used internally to create an upgrade service.
120pub struct UpgradeHandler<O> {
121    matcher: Box<dyn Matcher<Request>>,
122    kind: UpgradeHandlerKind<O>,
123    _phantom: std::marker::PhantomData<fn(O) -> ()>,
124}
125
126enum UpgradeHandlerKind<O> {
127    ResponseLocal {
128        responder: BoxService<Request, UpgradeOutput<Request, O>, O>,
129        handler_error_sink: Arc<dyn ErrorSink>,
130    },
131    SeparateServices {
132        responder: BoxService<Request, UpgradeResponse<Request, O>, O>,
133        handler: BoxService<Upgraded, (), Infallible>,
134    },
135}
136
137enum UpgradeContinuation {
138    ResponseLocal {
139        handler: ResponseUpgradeHandler,
140        error_sink: Arc<dyn ErrorSink>,
141    },
142    Service(BoxService<Upgraded, (), Infallible>),
143}
144
145struct PreparedUpgrade<O> {
146    response: O,
147    request: Request,
148    extensions: Extensions,
149    continuation: UpgradeContinuation,
150}
151
152impl<O: Send + 'static> UpgradeHandler<O> {
153    /// Register one service which returns its response-local upgrade handler.
154    pub(crate) fn new<M, R, Sink>(matcher: M, responder: R, sink: Sink) -> Self
155    where
156        M: Matcher<Request>,
157        R: Service<Request, Output = UpgradeOutput<Request, O>, Error = O> + Clone,
158        Sink: ErrorSink,
159    {
160        Self {
161            matcher: Box::new(matcher),
162            kind: UpgradeHandlerKind::ResponseLocal {
163                responder: responder.boxed(),
164                handler_error_sink: Arc::new(sink),
165            },
166            _phantom: std::marker::PhantomData,
167        }
168    }
169
170    /// Register separate responder and upgraded-connection services.
171    pub(crate) fn new_with_services<M, R, H, Sink>(
172        matcher: M,
173        responder: R,
174        handler: H,
175        sink: Sink,
176    ) -> Self
177    where
178        M: Matcher<Request>,
179        R: Service<Request, Output = UpgradeResponse<Request, O>, Error = O> + Clone,
180        H: Service<Upgraded> + Clone,
181        Sink: ErrorSink<H::Error>,
182    {
183        let sink = Arc::new(sink);
184        let handler = (
185            ConsumeErrLayer::new(move |err| sink.sink_error(err)),
186            MapOutputLayer::new(drop),
187        )
188            .into_layer(handler)
189            .boxed();
190
191        Self {
192            matcher: Box::new(matcher),
193            kind: UpgradeHandlerKind::SeparateServices {
194                responder: responder.boxed(),
195                handler,
196            },
197            _phantom: std::marker::PhantomData,
198        }
199    }
200
201    async fn prepare(&self, request: Request) -> Result<PreparedUpgrade<O>, O> {
202        match &self.kind {
203            UpgradeHandlerKind::ResponseLocal {
204                responder,
205                handler_error_sink,
206            } => responder.serve(request).await.map(
207                |UpgradeOutput {
208                     response,
209                     request,
210                     extensions,
211                     handler,
212                 }| PreparedUpgrade {
213                    response,
214                    request,
215                    extensions,
216                    continuation: UpgradeContinuation::ResponseLocal {
217                        handler,
218                        error_sink: handler_error_sink.clone(),
219                    },
220                },
221            ),
222            UpgradeHandlerKind::SeparateServices { responder, handler } => {
223                responder.serve(request).await.map(
224                    |UpgradeResponse {
225                         response,
226                         request,
227                         extensions,
228                     }| PreparedUpgrade {
229                        response,
230                        request,
231                        extensions,
232                        continuation: UpgradeContinuation::Service(handler.clone()),
233                    },
234                )
235            }
236        }
237    }
238}
239
240impl<S, O> UpgradeService<S, O> {
241    /// Create a new [`UpgradeService`].
242    pub fn new(
243        handlers: Vec<Arc<UpgradeHandler<O>>>,
244        inner: S,
245        exec: Executor,
246        error_sink: Arc<dyn ErrorSink>,
247    ) -> Self {
248        Self {
249            handlers,
250            inner,
251            exec,
252            error_sink,
253        }
254    }
255
256    define_inner_service_accessors!();
257}
258
259impl<S, O> fmt::Debug for UpgradeService<S, O>
260where
261    S: fmt::Debug,
262{
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        f.debug_struct("UpgradeService")
265            .field("handlers", &self.handlers)
266            .field("inner", &self.inner)
267            .field("exec", &self.exec)
268            .finish()
269    }
270}
271
272impl<S, O> Clone for UpgradeService<S, O>
273where
274    S: Clone,
275{
276    fn clone(&self) -> Self {
277        Self {
278            handlers: self.handlers.clone(),
279            inner: self.inner.clone(),
280            exec: self.exec.clone(),
281            error_sink: self.error_sink.clone(),
282        }
283    }
284}
285
286impl<S, O> Service<Request> for UpgradeService<S, O>
287where
288    S: Service<Request, Output = O>,
289    O: Send + 'static,
290{
291    type Output = O;
292    type Error = S::Error;
293
294    async fn serve(&self, req: Request) -> Result<Self::Output, Self::Error> {
295        for handler in &self.handlers {
296            let ext = Extensions::new();
297            if !handler.matcher.matches(Some(&ext), &req) {
298                continue;
299            }
300            req.extensions().extend(&ext);
301
302            return match handler.prepare(req).await {
303                Ok(PreparedUpgrade {
304                    response,
305                    request,
306                    extensions,
307                    continuation,
308                }) => {
309                    let upgrade_error_sink = self.error_sink.clone();
310
311                    let span = tracing::trace_root_span!(
312                        "upgrade::serve",
313                        otel.kind = "server",
314                        http.request.method = %request.method().as_str(),
315                        url.full = %request.request_uri(),
316                        url.path = %request.uri().path_or_root().as_ref(),
317                        url.query = %request.uri().query_or_empty().as_ref(),
318                        url.scheme = %request.uri().scheme_str().unwrap_or_default(),
319                        network.protocol.name = "http",
320                        network.protocol.version = version_as_protocol_version(request.version()),
321                    );
322
323                    self.exec.spawn_task(
324                        async move {
325                            match crate::io::upgrade::handle_upgrade(request).await {
326                                Ok(upgraded) => {
327                                    upgraded.extensions().extend(&extensions);
328                                    match continuation {
329                                        UpgradeContinuation::ResponseLocal {
330                                            handler,
331                                            error_sink,
332                                        } => {
333                                            if let Err(err) = handler(upgraded).await {
334                                                error_sink.sink_error(err);
335                                            }
336                                        }
337                                        UpgradeContinuation::Service(handler) => {
338                                            _ = handler.serve(upgraded).await;
339                                        }
340                                    }
341                                }
342                                Err(err) => {
343                                    // The HTTP upgrade itself failed (before the handler
344                                    // ran): route it to the layer's upgrade error sink.
345                                    upgrade_error_sink.sink_error(
346                                        err.context("http upgrade failed before handler"),
347                                    );
348                                }
349                            }
350                        }
351                        .instrument(span),
352                    );
353                    Ok(response)
354                }
355                Err(e) => Ok(e),
356            };
357        }
358
359        self.inner.serve(req).await
360    }
361}
362
363impl<O> fmt::Debug for UpgradeHandler<O> {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        f.debug_struct("UpgradeHandler").finish()
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use super::*;
372    use crate::io::upgrade::{Upgraded, pending};
373    use crate::layer::upgrade::UpgradeLayer;
374    use rama_core::Layer;
375    use rama_core::ServiceInput;
376    use rama_core::bytes::Bytes;
377    use rama_core::error::{BoxError, BoxErrorExt as _};
378    use rama_core::service::service_fn;
379    use rama_http_types::{Body, Response};
380    use std::time::Duration;
381    use std::{cell::Cell, convert::Infallible};
382    use tokio::sync::mpsc;
383    use tokio_test::io::Builder;
384
385    #[derive(Debug)]
386    struct ResponseLocalMarker;
387
388    impl Extension for ResponseLocalMarker {}
389
390    #[derive(Clone)]
391    struct SendOnlyOutputResponder;
392
393    impl Service<Request> for SendOnlyOutputResponder {
394        type Output = UpgradeOutput<Request, Cell<u8>>;
395        type Error = Cell<u8>;
396
397        async fn serve(&self, req: Request) -> Result<Self::Output, Self::Error> {
398            Ok(UpgradeResponse::new(req, Cell::new(1))
399                .with_handler(|_upgraded| async { Ok::<_, BoxError>(()) }))
400        }
401    }
402
403    #[derive(Clone)]
404    struct SendOnlyInner;
405
406    impl Service<Request> for SendOnlyInner {
407        type Output = Cell<u8>;
408        type Error = Cell<u8>;
409
410        async fn serve(&self, _req: Request) -> Result<Self::Output, Self::Error> {
411            Err(Cell::new(2))
412        }
413    }
414
415    #[tokio::test]
416    async fn output_and_error_need_not_be_sync() {
417        let service = UpgradeLayer::new(Executor::default(), false, SendOnlyOutputResponder)
418            .into_layer(SendOnlyInner);
419
420        let error = service
421            .serve(Request::new(Body::empty()))
422            .await
423            .unwrap_err();
424
425        assert_eq!(error.get(), 2);
426    }
427
428    #[tokio::test]
429    async fn response_local_handler_runs_after_upgrade() {
430        let (handled_tx, mut handled_rx) = mpsc::unbounded_channel();
431        let (pending_upgrade, on_upgrade) = pending();
432        let req = Request::new(Body::empty());
433        req.extensions().insert(on_upgrade);
434
435        let service = service_fn(move |req: Request| {
436            let handled_tx = handled_tx.clone();
437            async move {
438                Ok::<_, Response>(
439                    UpgradeResponse::new(req, Response::new(Body::empty()))
440                        .with_extension(ResponseLocalMarker)
441                        .with_handler(move |upgraded| async move {
442                            assert!(upgraded.extensions().contains::<ResponseLocalMarker>());
443                            _ = handled_tx.send(());
444                            Ok::<_, BoxError>(())
445                        }),
446                )
447            }
448        });
449        let inner =
450            service_fn(
451                |_req: Request| async move { Ok::<_, Infallible>(Response::new(Body::empty())) },
452            );
453        let svc = UpgradeLayer::new(Executor::default(), true, service).into_layer(inner);
454
455        let _response = svc.serve(req).await.expect("upgrade response");
456        assert!(handled_rx.try_recv().is_err());
457
458        pending_upgrade.fulfill(Upgraded::new(
459            ServiceInput::new(Builder::default().build()),
460            Bytes::new(),
461        ));
462        tokio::time::timeout(Duration::from_secs(5), handled_rx.recv())
463            .await
464            .expect("response-local handler should run")
465            .expect("handler notification");
466    }
467
468    #[tokio::test]
469    async fn response_local_handler_error_is_routed_to_sink() {
470        let (error_tx, mut error_rx) = mpsc::unbounded_channel();
471        let (pending_upgrade, on_upgrade) = pending();
472        let req = Request::new(Body::empty());
473        req.extensions().insert(on_upgrade);
474
475        let service = service_fn(|req: Request| async move {
476            Ok::<_, Response>(
477                UpgradeResponse::new(req, Response::new(Body::empty())).with_handler(
478                    |_upgraded| async move {
479                        Err::<(), _>(BoxError::from_static_str("response-local handler boom"))
480                    },
481                ),
482            )
483        });
484        let inner =
485            service_fn(
486                |_req: Request| async move { Ok::<_, Infallible>(Response::new(Body::empty())) },
487            );
488        let svc = UpgradeLayer::new_with_error_sink(
489            Executor::default(),
490            true,
491            service,
492            move |err: BoxError| {
493                _ = error_tx.send(format!("{err:?}"));
494            },
495        )
496        .into_layer(inner);
497
498        let _response = svc.serve(req).await.expect("upgrade response");
499        pending_upgrade.fulfill(Upgraded::new(
500            ServiceInput::new(Builder::default().build()),
501            Bytes::new(),
502        ));
503
504        let reported = tokio::time::timeout(Duration::from_secs(5), error_rx.recv())
505            .await
506            .expect("handler error should reach sink")
507            .expect("handler error notification");
508        assert!(reported.contains("response-local handler boom"));
509    }
510
511    // Regression for #1014: a failing upgrade handler must hand its error to its
512    // per-handler [`ErrorSink`] instead of being silently swallowed.
513    #[tokio::test]
514    async fn upgrade_handler_error_is_routed_to_sink() {
515        // mpsc so the (sync) sink can report out of the detached upgrade task.
516        let (tx, mut rx) = mpsc::unbounded_channel::<String>();
517
518        // The request carries an `OnUpgrade` extension, as the http server sets.
519        let (pending_upgrade, on_upgrade) = pending();
520        let req = Request::new(Body::empty());
521        req.extensions().insert(on_upgrade);
522
523        // Responder echoes the request back (so `handle_upgrade` can find the
524        // `OnUpgrade`) and yields a response.
525        let responder = service_fn(|req: Request| async move {
526            Ok::<_, Response>(UpgradeResponse {
527                response: Response::new(Body::empty()),
528                request: req,
529                extensions: Extensions::new(),
530            })
531        });
532
533        // Handler that always fails — previously this had to be `Infallible`.
534        let handler = service_fn(|_upgraded: Upgraded| async move {
535            Err::<(), BoxError>(BoxError::from_static_str("handler boom"))
536        });
537
538        // Fallthrough inner service (not reached: matcher is `true`).
539        let inner =
540            service_fn(
541                |_req: Request| async move { Ok::<_, Infallible>(Response::new(Body::empty())) },
542            );
543
544        // The handler keeps its own error type; its (raw) error is routed to
545        // the per-handler sink given here.
546        let svc = UpgradeLayer::new_with_services_and_error_sink(
547            Executor::default(),
548            true,
549            responder,
550            handler,
551            move |err: BoxError| {
552                _ = tx.send(format!("{err:?}"));
553            },
554        )
555        .into_layer(inner);
556
557        // Serving spawns the detached upgrade task (which awaits the upgrade).
558        let _resp = svc.serve(req).await.expect("upgrade match -> Ok(response)");
559
560        // Fulfill the pending upgrade so the handler runs and then fails.
561        let upgraded = Upgraded::new(ServiceInput::new(Builder::default().build()), Bytes::new());
562        pending_upgrade.fulfill(upgraded);
563
564        // The handler error must reach the sink (not be swallowed).
565        let reported = tokio::time::timeout(Duration::from_secs(5), rx.recv())
566            .await
567            .expect("sink should be called within timeout")
568            .expect("sink channel should yield the error");
569        assert!(
570            reported.contains("handler boom"),
571            "unexpected sink message: {reported}"
572        );
573    }
574}