trillium_http/
synthetic.rs

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