main
parent
3389292457
commit
ff73f501a4
|
@ -90,6 +90,7 @@ pub enum StatusCode {
|
|||
NoContent, // 204
|
||||
PartialContent, // 206
|
||||
|
||||
NotModified, // 304
|
||||
TemporaryRedirect, // 307
|
||||
|
||||
BadRequest, // 400
|
||||
|
@ -114,6 +115,7 @@ impl From <StatusCode> for hyper::StatusCode {
|
|||
StatusCode::NoContent => Self::NO_CONTENT,
|
||||
StatusCode::PartialContent => Self::PARTIAL_CONTENT,
|
||||
|
||||
StatusCode::NotModified => Self::NOT_MODIFIED,
|
||||
StatusCode::TemporaryRedirect => Self::TEMPORARY_REDIRECT,
|
||||
|
||||
StatusCode::BadRequest => Self::BAD_REQUEST,
|
||||
|
|
|
@ -121,13 +121,27 @@ async fn serve_dir_json (
|
|||
#[instrument (level = "debug", skip (f))]
|
||||
async fn serve_file (
|
||||
mut f: File,
|
||||
should_send_body: bool,
|
||||
range: range::ValidParsed
|
||||
client_wants_body: bool,
|
||||
range: range::ValidParsed,
|
||||
if_none_match: Option <&Vec <u8>>,
|
||||
)
|
||||
-> Result <Response, FileServerError>
|
||||
{
|
||||
// Tripping the etag through UTF-8 isn't the best way to encourage it to
|
||||
// be valid ASCII, but if I make it binary I might accidentally pass the
|
||||
// hash binary as a header, which is not valid.
|
||||
|
||||
let etag = get_file_etag (&f).await.map (String::into_bytes);
|
||||
let client_cache_hit = match &etag {
|
||||
None => false,
|
||||
Some (actual) => match &if_none_match {
|
||||
None => false,
|
||||
Some (if_none_match) => &actual == if_none_match,
|
||||
}
|
||||
};
|
||||
|
||||
let (tx, rx) = channel (1);
|
||||
let body = if should_send_body {
|
||||
let body = if client_wants_body && ! client_cache_hit {
|
||||
Some (rx)
|
||||
}
|
||||
else {
|
||||
|
@ -143,7 +157,7 @@ async fn serve_file (
|
|||
let seek = SeekFrom::Start (range.start);
|
||||
f.seek (seek).await?;
|
||||
|
||||
if should_send_body {
|
||||
if body.is_some () {
|
||||
tokio::spawn (async move {
|
||||
let mut bytes_sent = 0;
|
||||
let mut bytes_left = content_length;
|
||||
|
@ -194,7 +208,7 @@ async fn serve_file (
|
|||
// The intended semantics I'm using are:
|
||||
// - etag - Some random hashed value that changes whenever the metadata
|
||||
// (name, inode number, length, mtime) of a file changes. Also changes
|
||||
// on new server instance.
|
||||
// on new server instance. Maybe.
|
||||
// - no-cache - Clients and the relay can store this, but should revalidate
|
||||
// with the origin server (us) because only we can check if the file
|
||||
// changed on disk.
|
||||
|
@ -203,7 +217,9 @@ async fn serve_file (
|
|||
// consider it stale.
|
||||
|
||||
response.header ("cache-control".to_string (), b"no-cache,max-age=0".to_vec ());
|
||||
response.header ("etag".to_string (), rusty_ulid::generate_ulid_string ().into_bytes ());
|
||||
etag.map (|etag| {
|
||||
response.header ("etag".to_string (), etag);
|
||||
});
|
||||
response.header (String::from ("accept-ranges"), b"bytes".to_vec ());
|
||||
|
||||
if range_requested {
|
||||
|
@ -215,11 +231,14 @@ async fn serve_file (
|
|||
response.header (String::from ("content-length"), range.end.to_string ().into_bytes ());
|
||||
}
|
||||
|
||||
if should_send_body {
|
||||
response.content_length = Some (content_length);
|
||||
if client_cache_hit {
|
||||
response.status_code (StatusCode::NotModified);
|
||||
}
|
||||
else if ! client_wants_body {
|
||||
response.status_code (StatusCode::NoContent);
|
||||
}
|
||||
else {
|
||||
response.status_code (StatusCode::NoContent);
|
||||
response.content_length = Some (content_length);
|
||||
}
|
||||
|
||||
if let Some (body) = body {
|
||||
|
@ -229,6 +248,12 @@ async fn serve_file (
|
|||
Ok (response)
|
||||
}
|
||||
|
||||
async fn get_file_etag (f: &File) -> Option <String>
|
||||
{
|
||||
let md = f.metadata ().await;
|
||||
None
|
||||
}
|
||||
|
||||
// Pass a request to the internal decision-making logic.
|
||||
// When it returns, prettify it as HTML or JSON based on what the client
|
||||
// asked for.
|
||||
|
@ -293,7 +318,7 @@ pub async fn serve_all (
|
|||
file,
|
||||
send_body,
|
||||
range,
|
||||
}) => serve_file (file.into_inner (), send_body, range).await?,
|
||||
}) => serve_file (file.into_inner (), send_body, range, headers.get ("if-none-match")).await?,
|
||||
MarkdownErr (e) => {
|
||||
#[cfg (feature = "markdown")]
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue