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
#![forbid(elided_lifetimes_in_paths, unsafe_code)]
use either::Either;
use gotham::{
handler::HandlerError,
helpers::http::response::{create_empty_response, create_response},
hyper::{
header::{
HeaderMap, HeaderValue, CACHE_CONTROL, CONTENT_SECURITY_POLICY, ETAG, IF_NONE_MATCH,
REFERRER_POLICY, X_CONTENT_TYPE_OPTIONS
},
Body, Response, StatusCode
},
mime::TEXT_HTML_UTF_8,
state::State
};
use sha2::{Digest, Sha256};
use std::{io::Write, iter};
#[doc(hidden)]
pub fn handler(state: &State, spec: String) -> Result<Response<Body>, HandlerError> {
let encoded_spec = spec
.chars()
.flat_map(|c| match c {
'&' => Either::Left("&".chars()),
'<' => Either::Left("<".chars()),
'>' => Either::Left(">".chars()),
c => Either::Right(iter::once(c))
})
.collect::<String>();
let script = include_str!("script.min.js");
let mut script_hash = Sha256::new();
script_hash.update(&script);
let script_hash = base64::encode(script_hash.finalize());
let mut buf = Vec::<u8>::new();
write!(
buf,
r#"<!DOCTYPE HTML><html><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/></head>"#
)?;
write!(
buf,
r#"<body style="margin:0"><div id="spec" style="display:none">{}</div><div id="redoc"></div><script>{}</script></body></html>"#,
encoded_spec, script
)?;
let mut etag = Sha256::new();
etag.update(&buf);
let etag = format!("\"{}\"", base64::encode(etag.finalize()));
if state
.borrow::<HeaderMap>()
.get(IF_NONE_MATCH)
.map_or(false, |header| header.as_bytes() == etag.as_bytes())
{
let res = create_empty_response(state, StatusCode::NOT_MODIFIED);
return Ok(res);
}
let mut res = create_response(state, StatusCode::OK, TEXT_HTML_UTF_8, buf);
let headers = res.headers_mut();
headers.insert(
CACHE_CONTROL,
HeaderValue::from_static("public,max-age=2592000")
);
headers.insert(
CONTENT_SECURITY_POLICY,
format!(
"default-src 'none';base-uri 'none';script-src 'unsafe-inline' https://cdn.jsdelivr.net 'sha256-{script_hash}' 'strict-dynamic';style-src 'unsafe-inline' https://fonts.googleapis.com;font-src https://fonts.gstatic.com;connect-src 'self';img-src blob: data:",
).parse().unwrap()
);
headers.insert(ETAG, etag.parse().unwrap());
headers.insert(REFERRER_POLICY, HeaderValue::from_static("no-referrer"));
headers.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static("nosniff"));
Ok(res)
}