Skip to main content

cratestack_axum/compact_layer/
service.rs

1//! [`CompactService`]: the per-request flow.
2
3use std::convert::Infallible;
4use std::future::Future;
5use std::pin::Pin;
6use std::task::{Context, Poll};
7
8use axum::extract::Request;
9use axum::response::Response;
10use cratestack_core::compact::OpKeys;
11use tower::Service;
12
13use super::layer::CompactLayer;
14use super::{negotiate, request, resolve, response};
15
16/// The service [`CompactLayer`] wraps a router in.
17#[derive(Clone)]
18pub struct CompactService<S> {
19    pub(super) inner: S,
20    pub(super) layer: CompactLayer,
21}
22
23impl<S> Service<Request> for CompactService<S>
24where
25    S: Service<Request, Response = Response, Error = Infallible> + Clone + Send + 'static,
26    S::Future: Send + 'static,
27{
28    type Response = Response;
29    type Error = Infallible;
30    type Future = Pin<Box<dyn Future<Output = Result<Response, Infallible>> + Send>>;
31
32    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Infallible>> {
33        self.inner.poll_ready(cx)
34    }
35
36    fn call(&mut self, req: Request) -> Self::Future {
37        // The clone is the not-yet-ready one; the ready service stays here.
38        let ready = self.inner.clone();
39        let mut inner = std::mem::replace(&mut self.inner, ready);
40        let layer = self.layer.clone();
41        Box::pin(async move {
42            let wanted = negotiate::accepted_compact(req.headers());
43            let sent = negotiate::request_compact(req.headers());
44            if wanted.is_empty() && sent.is_none() {
45                return inner.call(req).await;
46            }
47            let keys: Option<OpKeys> = negotiate::selector(req.headers())
48                .and_then(|selector| resolve::resolve(&layer.tables, &req, &selector));
49            let mut req = req;
50            if let Some(compact) = sent {
51                match request::open(&layer, keys.as_ref(), compact, req).await {
52                    Ok(opened) => req = opened,
53                    Err(refusal) => return Ok(*refusal),
54                }
55            }
56            let (path, headers) = (req.uri().path().to_owned(), req.headers().clone());
57            if !wanted.is_empty() {
58                negotiate::rewrite_accept(req.headers_mut());
59            }
60            let answer = inner.call(req).await?;
61            Ok(match keys {
62                Some(keys) if !wanted.is_empty() => {
63                    response::compact(&layer, &keys, &wanted, &headers, &path, answer).await
64                }
65                _ => answer,
66            })
67        })
68    }
69}