fix caching (#2216)

This commit is contained in:
Aiden McClelland
2023-03-13 17:25:10 -06:00
committed by GitHub
parent caf47943c3
commit de519edf78

View File

@@ -10,6 +10,7 @@ use digest::Digest;
use futures::FutureExt; use futures::FutureExt;
use http::header::ACCEPT_ENCODING; use http::header::ACCEPT_ENCODING;
use http::header::CONTENT_ENCODING; use http::header::CONTENT_ENCODING;
use http::request::Parts as RequestParts;
use http::response::Builder; use http::response::Builder;
use hyper::{Body, Method, Request, Response, StatusCode}; use hyper::{Body, Method, Request, Response, StatusCode};
use openssl::hash::MessageDigest; use openssl::hash::MessageDigest;
@@ -252,6 +253,7 @@ async fn alt_ui(req: Request<Body>, ui_mode: UiMode) -> Result<Response<Body>, E
let full_path = Path::new(selected_root_dir).join(uri_path); let full_path = Path::new(selected_root_dir).join(uri_path);
file_send( file_send(
&request_parts,
if tokio::fs::metadata(&full_path) if tokio::fs::metadata(&full_path)
.await .await
.ok() .ok()
@@ -298,6 +300,7 @@ async fn main_embassy_ui(req: Request<Body>, ctx: RpcContext) -> Result<Response
let sub_path = Path::new(path); let sub_path = Path::new(path);
if let Ok(rest) = sub_path.strip_prefix("package-data") { if let Ok(rest) = sub_path.strip_prefix("package-data") {
file_send( file_send(
&request_parts,
ctx.datadir.join(PKG_PUBLIC_DIR).join(rest), ctx.datadir.join(PKG_PUBLIC_DIR).join(rest),
&accept_encoding, &accept_encoding,
) )
@@ -330,6 +333,7 @@ async fn main_embassy_ui(req: Request<Body>, ctx: RpcContext) -> Result<Response
let full_path = Path::new(selected_root_dir).join(uri_path); let full_path = Path::new(selected_root_dir).join(uri_path);
file_send( file_send(
&request_parts,
if tokio::fs::metadata(&full_path) if tokio::fs::metadata(&full_path)
.await .await
.ok() .ok()
@@ -406,6 +410,7 @@ fn cert_send(cert: &X509) -> Result<Response<Body>, Error> {
} }
async fn file_send( async fn file_send(
req: &RequestParts,
path: impl AsRef<Path>, path: impl AsRef<Path>,
accept_encoding: &[&str], accept_encoding: &[&str],
) -> Result<Response<Body>, Error> { ) -> Result<Response<Body>, Error> {
@@ -421,40 +426,52 @@ async fn file_send(
.await .await
.with_ctx(|_| (ErrorKind::Filesystem, path.display().to_string()))?; .with_ctx(|_| (ErrorKind::Filesystem, path.display().to_string()))?;
match IsNonEmptyFile::new(&metadata, path) { let e_tag = e_tag(path, &metadata)?;
Some(a) => a,
None => return Ok(not_found()),
};
let mut builder = Response::builder().status(StatusCode::OK); let mut builder = Response::builder();
builder = with_e_tag(path, &metadata, builder)?;
builder = with_content_type(path, builder); builder = with_content_type(path, builder);
let body = if false && accept_encoding.contains(&"br") && metadata.len() > u16::MAX as u64 { builder = builder.header(http::header::ETAG, &e_tag);
builder = builder.header(CONTENT_ENCODING, "br"); builder = builder.header(
Body::wrap_stream(ReaderStream::new(BrotliEncoder::new(BufReader::new(file)))) http::header::CACHE_CONTROL,
} else if accept_encoding.contains(&"gzip") && metadata.len() > u16::MAX as u64 { "public, max-age=21000000, immutable",
builder = builder.header(CONTENT_ENCODING, "gzip"); );
Body::wrap_stream(ReaderStream::new(GzipEncoder::new(BufReader::new(file))))
} else {
builder = with_content_length(&metadata, builder);
Body::wrap_stream(ReaderStream::new(file))
};
builder.body(body).with_kind(ErrorKind::Network)
}
struct IsNonEmptyFile(()); if req
impl IsNonEmptyFile { .headers
fn new(metadata: &Metadata, path: &Path) -> Option<Self> { .get_all(http::header::CONNECTION)
let length = metadata.len(); .iter()
if !metadata.is_file() || length == 0 { .flat_map(|s| s.to_str().ok())
tracing::debug!("File is empty: {:?}", path); .flat_map(|s| s.split(","))
return None; .any(|s| s.trim() == "keep-alive")
} {
Some(Self(())) builder = builder.header(http::header::CONNECTION, "keep-alive");
} }
if req
.headers
.get("if-none-match")
.and_then(|h| h.to_str().ok())
== Some(e_tag.as_str())
{
builder = builder.status(StatusCode::NOT_MODIFIED);
builder.body(Body::empty())
} else {
let body = if false && accept_encoding.contains(&"br") && metadata.len() > u16::MAX as u64 {
builder = builder.header(CONTENT_ENCODING, "br");
Body::wrap_stream(ReaderStream::new(BrotliEncoder::new(BufReader::new(file))))
} else if accept_encoding.contains(&"gzip") && metadata.len() > u16::MAX as u64 {
builder = builder.header(CONTENT_ENCODING, "gzip");
Body::wrap_stream(ReaderStream::new(GzipEncoder::new(BufReader::new(file))))
} else {
builder = with_content_length(&metadata, builder);
Body::wrap_stream(ReaderStream::new(file))
};
builder.body(body)
}
.with_kind(ErrorKind::Network)
} }
fn with_e_tag(path: &Path, metadata: &Metadata, builder: Builder) -> Result<Builder, Error> { fn e_tag(path: &Path, metadata: &Metadata) -> Result<String, Error> {
let modified = metadata.modified().with_kind(ErrorKind::Filesystem)?; let modified = metadata.modified().with_kind(ErrorKind::Filesystem)?;
let mut hasher = sha2::Sha256::new(); let mut hasher = sha2::Sha256::new();
hasher.update(format!("{:?}", path).as_bytes()); hasher.update(format!("{:?}", path).as_bytes());
@@ -469,11 +486,12 @@ fn with_e_tag(path: &Path, metadata: &Metadata, builder: Builder) -> Result<Buil
.as_bytes(), .as_bytes(),
); );
let res = hasher.finalize(); let res = hasher.finalize();
Ok(builder.header( Ok(format!(
http::header::ETAG, "\"{}\"",
base32::encode(base32::Alphabet::RFC4648 { padding: false }, res.as_slice()).to_lowercase(), base32::encode(base32::Alphabet::RFC4648 { padding: false }, res.as_slice()).to_lowercase()
)) ))
} }
///https://en.wikipedia.org/wiki/Media_type ///https://en.wikipedia.org/wiki/Media_type
fn with_content_type(path: &Path, builder: Builder) -> Builder { fn with_content_type(path: &Path, builder: Builder) -> Builder {
let content_type = match path.extension() { let content_type = match path.extension() {