unit_rs/
http.rs

1//! This module contains an adapter to the Request and Response types from the
2//! [`http`](https://docs.rs/http) crate.
3//!
4//! # Example
5//!
6//! ```no_run
7//! use http::{Request, Response};
8//! use unit_rs::{http::HttpHandler, Unit};
9//!
10//! fn main() {
11//!     let mut unit = Unit::new().unwrap();
12//!
13//!     unit.set_request_handler(HttpHandler::new(|req: Request<Vec<u8>>| {
14//!         let body = format!("Hello world!\n\nBody length: {}\n", req.body().len());
15//!
16//!         let response = Response::builder()
17//!             .header("Content-Type", "text/plain")
18//!             .body(body.into_bytes())?;
19//!
20//!         Ok(response)
21//!     }));
22//!
23//!     unit.run();
24//! }
25//! ```
26
27use std::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
28
29use http::{uri::PathAndQuery, Uri};
30
31use crate::{request::LogLevel, unit::UnitService, UnitError, UnitResult};
32
33pub use http::{Request, Response};
34
35/// A trait for request handlers that uses types from the [`http`] crate.
36///
37/// [`http`]: https://docs.rs/http
38///
39/// Handlers that implement this trait can be used with the [`HttpHandler`]
40/// adapter to convert them to a [`UnitService`].
41pub trait HttpService: UnwindSafe {
42    fn handle_request(
43        &self,
44        _req: Request<Vec<u8>>,
45    ) -> Result<Response<Vec<u8>>, Box<dyn std::error::Error>>;
46}
47
48/// Adapter to use types from the [`http`] crate.
49///
50/// [`http`]: https://docs.rs/http
51///
52/// The inner request handler must implement the [`HttpService`] trait.
53pub struct HttpHandler<H: HttpService>(H);
54
55impl<H: HttpService> HttpHandler<H> {
56    pub fn new(unit_service: H) -> Self {
57        Self(unit_service)
58    }
59}
60
61impl<H: HttpService + RefUnwindSafe> UnitService for HttpHandler<H> {
62    fn handle_request(&mut self, mut req: crate::request::Request<'_>) -> UnitResult<()> {
63        self.handle_request_with_http(&mut req).map_err(|err| {
64            req.log(LogLevel::Error, err.to_string());
65            UnitError::error()
66        })
67    }
68}
69
70impl<H: HttpService + RefUnwindSafe> HttpHandler<H> {
71    fn handle_request_with_http(
72        &self,
73        req: &mut crate::request::Request<'_>,
74    ) -> Result<(), Box<dyn std::error::Error>> {
75        let path_and_query: PathAndQuery = req.target().parse()?;
76        let uri = Uri::builder()
77            .scheme(if req.tls() { "https" } else { "http" })
78            .authority(req.server_name())
79            .path_and_query(path_and_query)
80            .build()?;
81        let mut http_request_builder = Request::builder();
82
83        for (name, value) in req.fields() {
84            http_request_builder = http_request_builder.header(name, value);
85        }
86
87        let http_request = http_request_builder
88            .uri(uri)
89            .method(req.method())
90            .body(req.body().read_to_vec()?)?;
91
92        // SAFETY:
93        // The only !UnwindSafe part of http::Request is its Extensions, and
94        // this library does not add any extensions to it.
95        let http_request = AssertUnwindSafe(http_request);
96        let handler = &self.0;
97
98        let http_response = std::panic::catch_unwind(move || {
99            let http_request = http_request;
100            handler.handle_request(http_request.0)
101        });
102
103        match http_response {
104            Ok(Ok(http_response)) => {
105                let header_count = http_response.headers().len();
106                let headers_size: usize = http_response
107                    .headers()
108                    .iter()
109                    .map(|(name, value)| name.as_str().len() + value.as_bytes().len())
110                    .sum();
111                let body_size = http_response.body().len();
112
113                let response = req.create_response(
114                    http_response.status().as_u16(),
115                    header_count,
116                    headers_size + body_size,
117                )?;
118
119                for (name, value) in http_response.headers() {
120                    response.add_field(name, value)?;
121                }
122                response.add_content(http_response.body())?;
123                response.send()?;
124            }
125            Ok(Err(err)) => {
126                let content_type = ("Content-Type", "text/plain");
127                let response_body = format!("The server experienced an internal error: {}", err);
128                let response = req.create_response(
129                    501,
130                    1,
131                    content_type.0.len() + content_type.1.len() + response_body.len(),
132                )?;
133                response.add_field(content_type.0, content_type.1)?;
134                response.add_content(response_body)?;
135                response.send()?;
136            }
137            Err(panic_payload) => {
138                req.log(LogLevel::Error, "Panicked during http request handling.");
139
140                // If a request has not been created yet, Unit will generate a
141                // 503 error itself, which is what we want.
142                std::panic::resume_unwind(panic_payload);
143            }
144        }
145        Ok(())
146    }
147}
148
149impl<F> HttpService for F
150where
151    F: Fn(Request<Vec<u8>>) -> Result<Response<Vec<u8>>, Box<dyn std::error::Error>>,
152    F: UnwindSafe + 'static,
153{
154    fn handle_request(
155        &self,
156        req: Request<Vec<u8>>,
157    ) -> Result<Response<Vec<u8>>, Box<dyn std::error::Error>> {
158        self(req)
159    }
160}