main
_ 2021-04-03 16:21:59 +00:00
parent 3389292457
commit ff73f501a4
2 changed files with 37 additions and 10 deletions

View File

@ -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,

View File

@ -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")]
{