foxhole/
action.rs

1use http::{Response, Version};
2
3use crate::http_utils::IntoRawBytes;
4
5pub type RawResponse = Response<Vec<u8>>;
6
7/// This is a helper trait to remove the unnecessary `Option` many response types don't need as
8/// they do not affect control flow.
9pub trait IntoResponse {
10    fn response(self) -> RawResponse;
11}
12
13/// All `System`s must return a type implementing `Action`. This trait decides the
14/// behaviour of the underlying router. If this method returns `None` the router will
15/// continue. If it receives `Some` value, it will respond to the connection and stop routing.
16pub trait Action {
17    fn action(self) -> Option<RawResponse>;
18}
19
20impl<T> Action for T
21where
22    T: IntoResponse,
23{
24    fn action(self) -> Option<RawResponse> {
25        Some(self.response())
26    }
27}
28
29impl<T> Action for Response<T>
30where
31    T: IntoRawBytes,
32{
33    fn action(self) -> Option<RawResponse> {
34        Some(self.map(IntoRawBytes::into_raw_bytes))
35    }
36}
37
38impl Action for () {
39    fn action(self) -> Option<RawResponse> {
40        None
41    }
42}
43
44impl<T> Action for Option<T>
45where
46    T: Action,
47{
48    fn action(self) -> Option<RawResponse> {
49        self.and_then(Action::action)
50    }
51}
52
53impl<T, E> Action for Result<T, E>
54where
55    T: Action,
56    E: Action,
57{
58    fn action(self) -> Option<RawResponse> {
59        match self {
60            Ok(v) => v.action(),
61            Err(e) => e.action(),
62        }
63    }
64}
65
66impl IntoResponse for u16 {
67    fn response(self) -> RawResponse {
68        Response::builder()
69            .version(Version::HTTP_11)
70            .status(self)
71            .header("Content-Type", "text/plain; charset=UTF-8")
72            .header("Content-Length", "0")
73            .body(Vec::new())
74            .expect("Failed to build request")
75    }
76}
77
78/// Creates a response with the content-type `application/x-binary` there may be a better MIME type
79/// to use for this.
80pub struct Raw(Vec<u8>);
81
82impl IntoResponse for Raw {
83    fn response(self) -> RawResponse {
84        Response::builder()
85            .version(Version::HTTP_11)
86            .status(200)
87            .header("Content-Type", "application/x-binary")
88            .header("Content-Length", format!("{}", self.0.len()))
89            .body(self.0)
90            .unwrap()
91    }
92}
93
94/// Creates a response with the content-type `text/plain`
95pub struct Plain(pub String);
96
97impl IntoResponse for Plain {
98    fn response(self) -> RawResponse {
99        let bytes = self.0.into_bytes();
100
101        Response::builder()
102            .version(Version::HTTP_11)
103            .status(200)
104            .header("Content-Type", "text/plain; charset=utf-8")
105            .header("Content-Length", format!("{}", bytes.len()))
106            .body(bytes)
107            .unwrap()
108    }
109}
110
111/// Creates a response with the content-type `text/html`
112pub struct Html(pub String);
113
114impl IntoResponse for Html {
115    fn response(self) -> RawResponse {
116        let bytes = self.0.into_bytes();
117
118        Response::builder()
119            .version(Version::HTTP_11)
120            .status(200)
121            .header("Content-Type", "text/html; charset=utf-8")
122            .header("Content-Length", format!("{}", bytes.len()))
123            .body(bytes)
124            .unwrap()
125    }
126}
127
128/// Creates a response with the content-type `text/css`
129pub struct Css(pub String);
130
131impl IntoResponse for Css {
132    fn response(self) -> RawResponse {
133        let bytes = self.0.into_bytes();
134
135        Response::builder()
136            .version(Version::HTTP_11)
137            .status(200)
138            .header("Content-Type", "text/css; charset=utf-8")
139            .header("Content-Length", format!("{}", bytes.len()))
140            .body(bytes)
141            .unwrap()
142    }
143}
144
145/// Creates a response with the content-type `text/javascript`
146pub struct Js(pub String);
147
148impl IntoResponse for Js {
149    fn response(self) -> RawResponse {
150        let bytes = self.0.into_bytes();
151
152        Response::builder()
153            .version(Version::HTTP_11)
154            .status(200)
155            .header("Content-Type", "text/javascript; charset=utf-8")
156            .header("Content-Length", format!("{}", bytes.len()))
157            .body(bytes)
158            .unwrap()
159    }
160}