Skip to main content

leyline/core/
service.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use http::{Request as HttpRequest, Response as HttpResponse};
6
7use crate::core::body::Body;
8use crate::core::session::decompress::{Decoder, content_codings};
9use crate::core::{HttpVersion, Kind, Response, Result, Session};
10
11#[derive(Clone, Debug)]
12pub struct LeylineService {
13    session: Session,
14}
15
16impl LeylineService {
17    pub fn new(session: Session) -> Self {
18        Self { session }
19    }
20}
21
22fn http_version(version: HttpVersion) -> http::Version {
23    match version {
24        HttpVersion::Http1_1 => http::Version::HTTP_11,
25        HttpVersion::Http2 => http::Version::HTTP_2,
26        HttpVersion::Http3 => http::Version::HTTP_3,
27    }
28}
29
30fn adapt(resp: Response) -> Result<HttpResponse<Body>> {
31    let encoding = content_codings(resp.headers().get_all(http::header::CONTENT_ENCODING));
32    let decoded = Decoder::new(encoding.as_deref(), &resp.compression)?.is_some();
33    let mut builder = HttpResponse::builder()
34        .status(resp.status())
35        .version(http_version(resp.version()))
36        .extension(resp.url().clone())
37        .extension(resp.version())
38        .extension(*resp.timing());
39    for (name, value) in resp.headers() {
40        if decoded
41            && (name == http::header::CONTENT_ENCODING || name == http::header::CONTENT_LENGTH)
42        {
43            continue;
44        }
45        builder = builder.header(name, value);
46    }
47    let body = Body::stream(resp.into_decoded_stream(None)?, None);
48    builder
49        .body(body)
50        .map_err(|e| crate::core::Error::new(Kind::Request).with_source(e))
51}
52
53impl tower_service::Service<HttpRequest<Body>> for LeylineService {
54    type Response = HttpResponse<Body>;
55    type Error = crate::core::Error;
56    type Future = Pin<Box<dyn Future<Output = Result<HttpResponse<Body>>> + Send>>;
57
58    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<()>> {
59        Poll::Ready(Ok(()))
60    }
61
62    fn call(&mut self, req: HttpRequest<Body>) -> Self::Future {
63        let session = self.session.clone();
64        Box::pin(async move { adapt(session.http_request(req).stream().send().await?) })
65    }
66}