Skip to main content

trillium_http/
h1.rs

1//! Serving HTTP/1.x on a transport.
2//!
3//! [`H1Connection`] runs a handler for each request on one HTTP/1.x connection, and reports how the
4//! connection ended as an [`H1ConnectionResult`].
5
6use crate::{
7    Conn, Error, HttpContext, Upgrade,
8    conn::{ConnInner, H1Status, HeadError},
9    graceful_close::GracefulClose,
10};
11use futures_lite::io::{AsyncRead, AsyncWrite};
12use std::{
13    fmt::{self, Debug, Formatter},
14    future::Future,
15    pin::Pin,
16    sync::Arc,
17    task::{Context, Poll},
18};
19
20/// An HTTP/1.x connection on one transport.
21///
22/// Configure it with the `with_*` methods, then call [`run`][Self::run] with a handler to serve
23/// requests on it.
24pub struct H1Connection<Transport> {
25    context: Arc<HttpContext>,
26    transport: Transport,
27    initial_bytes: Option<Vec<u8>>,
28}
29
30impl<Transport> Debug for H1Connection<Transport> {
31    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
32        f.debug_struct("H1Connection")
33            .field("initial_bytes", &self.initial_bytes.as_ref().map(Vec::len))
34            .finish_non_exhaustive()
35    }
36}
37
38impl<Transport> H1Connection<Transport> {
39    /// An HTTP/1.x connection on `transport`, sharing `context` with the rest of the server.
40    pub fn new(context: Arc<HttpContext>, transport: Transport) -> Self {
41        Self {
42            context,
43            transport,
44            initial_bytes: None,
45        }
46    }
47
48    /// Bytes already read from the transport, such as those read while checking for an HTTP/2
49    /// connection preface. They're parsed as the start of the first request, before anything else
50    /// is read.
51    #[must_use]
52    pub fn with_initial_bytes(mut self, initial_bytes: Vec<u8>) -> Self {
53        self.initial_bytes = Some(initial_bytes);
54        self
55    }
56}
57
58impl<Transport> H1Connection<Transport>
59where
60    Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
61{
62    /// Serve requests on this connection, calling `handler` once for each, until the connection
63    /// closes, upgrades, or fails.
64    pub async fn run<Handler, Fut>(self, mut handler: Handler) -> H1ConnectionResult<Transport>
65    where
66        Handler: FnMut(Conn<Transport>) -> Fut,
67        Fut: Future<Output = Conn<Transport>>,
68    {
69        let Self {
70            context,
71            transport,
72            initial_bytes,
73        } = self;
74        let initial_bytes = initial_bytes
75            .unwrap_or_else(|| Vec::with_capacity(context.config.request_buffer_initial_len));
76        let _guard = context.swansong.guard();
77
78        let mut status = match Conn::new_h1(context, transport, initial_bytes).await {
79            Ok(conn) => Ok(H1Status::Conn(conn)),
80            Err(HeadError::BadRequest(bad)) => bad.send().await,
81            Err(HeadError::Fatal(error)) => Err(error),
82        };
83
84        loop {
85            status = match status {
86                Ok(H1Status::Conn(conn)) => handler(conn).await.send().await,
87                Ok(H1Status::Upgrade(upgrade)) => return H1ConnectionResult::Upgrade(upgrade),
88                Ok(H1Status::Close(conn)) => return H1ConnectionResult::Close(H1Close::new(conn)),
89                Ok(H1Status::Ended(conn)) => {
90                    return H1ConnectionResult::Close(H1Close::finished(conn));
91                }
92                Err(error) => return H1ConnectionResult::Error(error),
93            };
94        }
95    }
96}
97
98/// How an HTTP/1.x connection ended.
99#[derive(Debug)]
100#[must_use]
101#[non_exhaustive]
102pub enum H1ConnectionResult<Transport> {
103    /// The connection negotiated an upgrade, which now owns the transport.
104    Upgrade(Upgrade<Transport>),
105
106    /// The connection is finished. Await the [`H1Close`] to close it gracefully, or drop it to
107    /// hang up immediately.
108    Close(H1Close<Transport>),
109
110    /// The connection ended with an error, such as a transport failure or a client that
111    /// disconnected partway through a request.
112    Error(Error),
113}
114
115impl<Transport> H1ConnectionResult<Transport> {
116    /// The `HttpContext::run` shape: an upgrade if one was negotiated, hanging up on a close.
117    pub(crate) fn into_upgrade(self) -> crate::Result<Option<Upgrade<Transport>>> {
118        match self {
119            Self::Upgrade(upgrade) => Ok(Some(upgrade)),
120            Self::Close(_) => Ok(None),
121            Self::Error(error) => Err(error),
122        }
123    }
124}
125
126/// The close of a finished HTTP/1.x connection.
127///
128/// Awaiting it shuts down our sending side of the transport, then reads and discards whatever the
129/// client still sends until it closes its side. If the transport is dropped while the client is
130/// still sending, the operating system answers with a TCP reset, which can destroy the end of a
131/// response the client hasn't read yet. A client that never closes keeps this pending indefinitely,
132/// so await it under a timeout. Dropping it closes the connection immediately.
133///
134/// When the client has already closed the connection, it completes immediately.
135pub struct H1Close<Transport>(GracefulClose<Transport>);
136
137impl<Transport> Debug for H1Close<Transport> {
138    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
139        f.debug_tuple("H1Close").finish_non_exhaustive()
140    }
141}
142
143impl<Transport> H1Close<Transport> {
144    fn new(conn: Conn<Transport>) -> Self {
145        let Conn { inner, transport } = conn;
146        let ConnInner {
147            buffer, context, ..
148        } = *inner;
149        Self(GracefulClose::new(
150            transport,
151            buffer.into(),
152            context.config.copy_loops_per_yield,
153        ))
154    }
155
156    fn finished(conn: Conn<Transport>) -> Self {
157        Self(GracefulClose::finished(conn.transport))
158    }
159}
160
161impl<Transport> Future for H1Close<Transport>
162where
163    Transport: AsyncRead + AsyncWrite + Unpin,
164{
165    type Output = ();
166
167    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
168        Pin::new(&mut self.get_mut().0).poll(cx)
169    }
170}