rasi_ext/net/http/
writer.rs

1use std::io;
2
3use http::{header::ToStrError, Request, Response};
4use rasi::io::{copy, AsyncRead, AsyncWrite, AsyncWriteExt, Cursor};
5
6fn map_to_str_error(err: ToStrError) -> io::Error {
7    io::Error::new(io::ErrorKind::InvalidData, err)
8}
9
10/// A http response writer, that write response packet into output stream.
11pub struct RequestWriter<'a, S> {
12    output: &'a mut S,
13}
14
15impl<'a, S> RequestWriter<'a, S> {
16    /// Create new `ResponseWriter` with `output` stream.
17    pub fn new(output: &'a mut S) -> Self {
18        Self { output }
19    }
20
21    /// write request packet into output stream.
22    pub async fn write_with_stream_body<T>(mut self, mut request: Request<T>) -> io::Result<()>
23    where
24        S: AsyncWrite + Unpin,
25        T: AsyncRead + Unpin,
26    {
27        // write status line.
28        self.output
29            .write_all(
30                format!(
31                    "{} {} {:?}\r\n",
32                    request.method(),
33                    request.uri(),
34                    request.version()
35                )
36                .as_bytes(),
37            )
38            .await?;
39
40        // write headers.
41        for (name, value) in request.headers() {
42            self.output
43                .write_all(
44                    format!(
45                        "{}: {}\r\n",
46                        name,
47                        value.to_str().map_err(map_to_str_error)?
48                    )
49                    .as_bytes(),
50                )
51                .await?;
52        }
53
54        self.output.write_all(b"\r\n").await?;
55
56        // write body.
57        copy(request.body_mut(), &mut self.output).await?;
58
59        Ok(())
60    }
61
62    /// Write
63    pub async fn write<T>(self, request: Request<T>) -> io::Result<()>
64    where
65        S: AsyncWrite + Unpin,
66        T: AsRef<[u8]>,
67    {
68        let (parts, body) = request.into_parts();
69
70        let request = Request::from_parts(parts, Cursor::new(body.as_ref()));
71
72        self.write_with_stream_body(request).await
73    }
74}
75
76/// A http response writer, that write response packet into output stream.
77pub struct ResponseWriter<S> {
78    output: S,
79}
80
81impl<S> ResponseWriter<S> {
82    /// Create new `ResponseWriter` with `output` stream.
83    pub fn new(output: S) -> Self {
84        Self { output }
85    }
86
87    /// write response packet which body type is a [`AsyncRead`]
88    pub async fn write_with_stream_body<T>(mut self, mut response: Response<T>) -> io::Result<()>
89    where
90        S: AsyncWrite + Unpin,
91        T: AsyncRead + Unpin,
92    {
93        // write status line.
94        self.output
95            .write_all(format!("{:?} {}\r\n", response.version(), response.status()).as_bytes())
96            .await?;
97
98        // write headers.
99        for (name, value) in response.headers() {
100            self.output
101                .write_all(
102                    format!(
103                        "{}: {}\r\n",
104                        name,
105                        value.to_str().map_err(map_to_str_error)?
106                    )
107                    .as_bytes(),
108                )
109                .await?;
110        }
111
112        self.output.write_all(b"\r\n").await?;
113
114        // write body.
115        copy(response.body_mut(), &mut self.output).await?;
116
117        Ok(())
118    }
119
120    pub async fn write<T>(self, response: Response<T>) -> io::Result<()>
121    where
122        S: AsyncWrite + Unpin,
123        T: AsRef<[u8]>,
124    {
125        let (parts, body) = response.into_parts();
126
127        let response = Response::from_parts(parts, Cursor::new(body.as_ref()));
128
129        self.write_with_stream_body(response).await
130    }
131}
132
133#[cfg(test)]
134mod tests {
135
136    use http::{Request, Response, StatusCode};
137    use rasi::io::Cursor;
138
139    use super::*;
140
141    async fn write_request_test(request: Request<&str>, expect: &[u8]) {
142        let mut output = Cursor::new(Vec::new());
143
144        RequestWriter::new(&mut output)
145            .write(request)
146            .await
147            .unwrap();
148
149        let buf = output.into_inner();
150
151        // use std::str::from_utf8;
152        // println!("{}", from_utf8(&buf).unwrap());
153
154        assert_eq!(&buf, expect);
155    }
156
157    async fn write_response_test(response: Response<&str>, expect: &[u8]) {
158        let mut output = Cursor::new(Vec::new());
159
160        ResponseWriter::new(&mut output)
161            .write(response)
162            .await
163            .unwrap();
164
165        let buf = output.into_inner();
166
167        // use std::str::from_utf8;
168        // println!("{}", from_utf8(&buf).unwrap());
169
170        assert_eq!(&buf, expect);
171    }
172
173    #[futures_test::test]
174    async fn test_request() {
175        write_request_test(
176            Request::get("http://rasi.com").body("").unwrap(),
177            b"GET http://rasi.com/ HTTP/1.1\r\n\r\n",
178        )
179        .await;
180
181        write_request_test(
182            Request::get("http://rasi.com").body("hello world").unwrap(),
183            b"GET http://rasi.com/ HTTP/1.1\r\n\r\nhello world",
184        )
185        .await;
186    }
187
188    #[futures_test::test]
189    async fn test_response() {
190        write_response_test(
191            Response::builder().status(StatusCode::OK).body("").unwrap(),
192            b"HTTP/1.1 200 OK\r\n\r\n",
193        )
194        .await;
195
196        write_response_test(
197            Response::builder()
198                .status(StatusCode::OK)
199                .body("hello world")
200                .unwrap(),
201            b"HTTP/1.1 200 OK\r\n\r\nhello world",
202        )
203        .await;
204    }
205}