Skip to main content

trillium_http/
synthetic.rs

1use crate::{
2    Conn, Headers, HttpConfig, HttpContext, KnownHeaderName, Method, TypeSet, Version,
3    after_send::AfterSend, received_body::ReceivedBodyState,
4};
5use futures_lite::io::{AsyncRead, AsyncWrite, Cursor, Result};
6use std::{
7    borrow::Cow,
8    pin::Pin,
9    sync::Arc,
10    task::{Context, Poll},
11    time::Instant,
12};
13
14/// Synthetic represents a simple transport that contains fixed content. This is exclusively useful
15/// for testing or for server implementations that are not read from an io connection, such as a
16/// faas function, in which the entire body may be available immediately on invocation.
17#[doc(hidden)]
18#[derive(Debug)]
19pub struct Synthetic {
20    data: Cursor<Vec<u8>>,
21    closed: bool,
22}
23
24impl AsyncRead for Synthetic {
25    fn poll_read(
26        mut self: Pin<&mut Self>,
27        cx: &mut Context<'_>,
28        buf: &mut [u8],
29    ) -> Poll<Result<usize>> {
30        let Synthetic { data, closed } = &mut *self;
31        if *closed {
32            Poll::Ready(Ok(0))
33        } else {
34            match Pin::new(data).poll_read(cx, buf) {
35                Poll::Ready(Ok(0)) => Poll::Pending,
36                other => other,
37            }
38        }
39    }
40}
41
42impl Synthetic {
43    /// the length of this synthetic transport's body
44    #[doc(hidden)]
45    pub fn len(&self) -> usize {
46        self.data.get_ref().len()
47    }
48
49    /// predicate to determine if this synthetic contains no content
50    #[doc(hidden)]
51    pub fn is_empty(&self) -> bool {
52        self.data.get_ref().is_empty()
53    }
54
55    /// close this connection
56    #[doc(hidden)]
57    pub fn close(&mut self) {
58        self.closed = true;
59    }
60}
61
62impl AsyncWrite for Synthetic {
63    fn poll_write(self: Pin<&mut Self>, _cx: &mut Context<'_>, _buf: &[u8]) -> Poll<Result<usize>> {
64        Poll::Ready(Ok(0))
65    }
66
67    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
68        Poll::Ready(Ok(()))
69    }
70
71    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
72        Poll::Ready(Ok(()))
73    }
74}
75
76impl From<Cursor<Vec<u8>>> for Synthetic {
77    fn from(data: Cursor<Vec<u8>>) -> Self {
78        Self {
79            data,
80            closed: false,
81        }
82    }
83}
84
85impl From<Vec<u8>> for Synthetic {
86    fn from(v: Vec<u8>) -> Self {
87        Cursor::new(v).into()
88    }
89}
90
91impl From<&[u8]> for Synthetic {
92    fn from(v: &[u8]) -> Self {
93        v.to_owned().into()
94    }
95}
96
97impl From<String> for Synthetic {
98    fn from(v: String) -> Self {
99        v.into_bytes().into()
100    }
101}
102
103impl From<&str> for Synthetic {
104    fn from(v: &str) -> Self {
105        v.as_bytes().into()
106    }
107}
108
109impl From<()> for Synthetic {
110    fn from((): ()) -> Self {
111        Vec::new().into()
112    }
113}
114
115impl From<Option<Vec<u8>>> for Synthetic {
116    fn from(v: Option<Vec<u8>>) -> Self {
117        v.unwrap_or_default().into()
118    }
119}
120
121impl Conn<Synthetic> {
122    /// Construct a new synthetic conn with provided method, path, and body.
123    /// ```rust
124    /// # use trillium_http::{Method, Conn};
125    /// let conn = Conn::new_synthetic(Method::Get, "/", "hello");
126    /// assert_eq!(conn.method(), Method::Get);
127    /// assert_eq!(conn.path(), "/");
128    /// ```
129    #[doc(hidden)]
130    pub fn new_synthetic(
131        method: Method,
132        path: impl Into<String>,
133        body: impl Into<Synthetic>,
134    ) -> Self {
135        let transport = body.into();
136        let mut request_headers = Headers::new();
137        request_headers.insert(KnownHeaderName::ContentLength, transport.len().to_string());
138
139        Self {
140            context: Arc::default(),
141            transport,
142            request_headers,
143            response_headers: Headers::new(),
144            path: Cow::Owned(path.into()),
145            method,
146            status: None,
147            version: Version::Http1_1,
148            state: TypeSet::new(),
149            response_body: None,
150            buffer: Vec::with_capacity(HttpConfig::DEFAULT.request_buffer_initial_len).into(),
151            request_body_state: ReceivedBodyState::Start,
152            secure: false,
153            after_send: AfterSend::default(),
154            start_time: Instant::now(),
155            peer_ip: None,
156            authority: None,
157            scheme: None,
158            h3_connection: None,
159            protocol: None,
160            request_trailers: None,
161        }
162    }
163
164    /// use a particular shared server config for this synthetic conn
165    #[doc(hidden)]
166    pub fn set_context(&mut self, context: Arc<HttpContext>) {
167        self.context = context;
168    }
169
170    /// chainable setter for server config
171    #[doc(hidden)]
172    #[must_use]
173    pub fn with_context(mut self, context: Arc<HttpContext>) -> Self {
174        self.set_context(context);
175        self
176    }
177
178    /// simulate closing the transport
179    #[doc(hidden)]
180    pub fn close(&mut self) {
181        self.transport.close();
182    }
183
184    /// Replaces the synthetic body. This is intended for testing use.
185    #[doc(hidden)]
186    pub fn replace_body(&mut self, body: impl Into<Synthetic>) {
187        let transport = body.into();
188        self.request_headers_mut()
189            .insert(KnownHeaderName::ContentLength, transport.len().to_string());
190        self.transport = transport;
191        self.request_body_state = ReceivedBodyState::default();
192    }
193}