-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathserver.rs
More file actions
108 lines (91 loc) · 3.27 KB
/
server.rs
File metadata and controls
108 lines (91 loc) · 3.27 KB
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
//! Core server utilities: error types, HTML rendering, error responses.
use axum::http::{header, HeaderMap, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use std::io;
/// The HTML template, rendered with `format!`.
static HTML_TEMPLATE: &str = include_str!("template.html");
/// Render an HTML page with a title and body.
pub fn render_html(title: &str, body: &str) -> String {
HTML_TEMPLATE
.replace("{title}", title)
.replace("{body}", body)
}
/// Render an error page from an HTTP status code.
pub fn error_response(status: StatusCode) -> Response {
error_response_with_headers(status, HeaderMap::new())
}
/// Render an error page with additional headers.
pub fn error_response_with_headers(status: StatusCode, extra_headers: HeaderMap) -> Response {
let html = render_html(&status.to_string(), "");
let mut response = (status, [(header::CONTENT_TYPE, "text/html")], html).into_response();
response.headers_mut().extend(extra_headers);
response
}
/// The server error type.
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error")]
Io(#[from] io::Error),
#[error("HTTP error")]
Http(#[from] http::Error),
#[error("requested URI is not UTF-8")]
UriNotUtf8,
#[error("requested URI is not an absolute path")]
UriNotAbsolute,
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
match &self {
Error::Io(e) if e.kind() == io::ErrorKind::NotFound => {
tracing::debug!("{}", e);
error_response(StatusCode::NOT_FOUND)
}
Error::Io(e) => {
tracing::error!("I/O error: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR)
}
e => {
tracing::error!("internal error: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
}
/// Map a request URI to a local filesystem path.
pub fn local_path_for_request(
uri_path: &str,
root_dir: &std::path::Path,
) -> Result<std::path::PathBuf, Error> {
use percent_encoding::percent_decode_str;
tracing::debug!("raw URI path: {}", uri_path);
// Trim off query parameters.
let end = uri_path.find('?').unwrap_or(uri_path.len());
let request_path = &uri_path[..end];
// Decode percent-encoding.
let decoded = percent_decode_str(request_path)
.decode_utf8()
.map_err(|_| {
tracing::error!("non-UTF-8 URL: {}", request_path);
Error::UriNotUtf8
})?;
// Build the local path.
let mut path = root_dir.to_owned();
if let Some(rest) = decoded.strip_prefix('/') {
path.push(rest);
} else {
tracing::warn!("non-absolute path: {}", decoded);
return Err(Error::UriNotAbsolute);
}
tracing::debug!("resolved path: {}", path.display());
Ok(path)
}
/// Make an HTTP response from an HTML string.
pub fn html_response(body: String, status: StatusCode) -> Response {
let mut headers = HeaderMap::new();
headers.insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/html; charset=utf-8"),
);
headers.insert(header::CONTENT_LENGTH, HeaderValue::from(body.len()));
(status, headers, body).into_response()
}