1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::collections::BTreeMap;
use std::fmt::Display;
use std::io::{self, Write};
/// A response from a FastCGI server.
#[derive(Debug)]
pub struct Response {
headers: BTreeMap<String, String>,
body: Vec<u8>,
error: Option<Box<dyn std::error::Error>>,
}
impl Default for Response {
/// Creates an empty 200 OK response
fn default() -> Self {
let default_headers = BTreeMap::from_iter([
("Status".into(), "200".into()),
("Content-Type".into(), "text/plain".into()),
]);
Self {
headers: default_headers,
body: vec![],
error: None,
}
}
}
impl Response {
/// Create a new FastCGI response.
///
/// Defaults to an empty 200 OK
pub fn new() -> Self {
Self::default()
}
/// Sets the `Content-Type` header
pub fn set_content_type<S: Display>(mut self, content_type: S) -> Self {
self.headers
.insert("Content-Type".into(), content_type.to_string());
self
}
/// Sets the `Location` header
pub fn set_location<S: Display>(mut self, location: S) -> Self {
self.headers.insert("Location".into(), location.to_string());
self
}
/// Sets the status of the response
pub fn set_status(mut self, code: u16) -> Self {
self.headers.insert("Status".into(), code.to_string());
self
}
/// Sets the content of the body
pub fn set_body<S: Display>(mut self, body: S) -> Self {
self.body = body.to_string().into_bytes();
self
}
/// Sets the content of the body using raw bytes
pub fn set_body_raw(mut self, body: &[u8]) -> Self {
self.body = body.to_vec();
self
}
pub(crate) fn write_stdout_bytes<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
for (key, value) in self.headers.iter() {
writeln!(writer, "{key}: {value}")?;
}
writeln!(writer)?;
writer.write_all(&self.body)
}
}
// Shortcuts for creating responses
impl Response {
pub fn text<S: std::fmt::Display>(str: S) -> Response {
Response::default()
.set_body(str.to_string())
.set_content_type("text/plain")
}
/// Creates an HTML response
pub fn html<S: Display>(str: S) -> Response {
Response::default()
.set_body(str.to_string())
.set_content_type("text/html")
}
/// Creates a JSON response
pub fn json<S: Display>(str: S) -> Response {
Response::default()
.set_body(str.to_string())
.set_content_type("application/json")
}
/// Creates a temporary redirection response.
///
/// The browser receiving the request will to re-make the request with `path` as the new target
/// with method and body unchanged.
///
/// Search engines receiving this response will not attribute links to the original URL to the
/// new resource, meaning no SEO value is transferred to the new URL.
pub fn redirect(path: &str) -> Response {
Response::new().set_location(path).set_status(307)
}
/// Creates a permanent redirection response.
///
/// The browser receiving the request will to re-make the request with `path` as the new target
/// with method and body unchanged.
///
/// Search engines receiving this response will attribute links to the original URL to the
/// redirected resource, passing the SEO ranking to the new URL.
pub fn permanent_redirect(path: &str) -> Response {
Response::new().set_location(path).set_status(308)
}
}