From e1dbc943e677113beee289ade4b6463ac6aa5149 Mon Sep 17 00:00:00 2001 From: arloor Date: Sat, 28 Sep 2024 17:34:36 +0800 Subject: [PATCH] change to nginx like ETAG for cache --- rust_http_proxy/src/web_func.rs | 71 +++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/rust_http_proxy/src/web_func.rs b/rust_http_proxy/src/web_func.rs index 06e5c6a..6ac97fa 100644 --- a/rust_http_proxy/src/web_func.rs +++ b/rust_http_proxy/src/web_func.rs @@ -13,7 +13,6 @@ use futures_util::TryStreamExt; use http::{Error, HeaderValue}; use http_body_util::combinators::BoxBody; use http_body_util::{BodyExt, StreamBody}; -use httpdate::fmt_http_date; use hyper::body::{Body, Bytes, Frame}; use hyper::header::{CONTENT_ENCODING, REFERER}; use hyper::{http, Method, Request, Response, StatusCode}; @@ -250,15 +249,12 @@ async fn serve_path( Ok(time) => time, Err(_) => return not_found(), }; - let mime_type = from_path(&path).first_or_octet_stream(); - if let Some(request_if_modified_since) = req.headers().get(http::header::IF_MODIFIED_SINCE) { - if let Ok(request_if_modified_since) = request_if_modified_since.to_str() { - if request_if_modified_since == fmt_http_date(last_modified).as_str() { - return not_modified(last_modified); - } - } + let file_len = meta.len(); + let file_etag = cal_file_etag(last_modified, file_len); + if let Some(value) = handle_etag_match(req, &file_etag) { + return value; } - + let mime_type = from_path(&path).first_or_octet_stream(); let content_type = mime_type.as_ref(); let content_type = if !content_type.to_ascii_lowercase().contains("charset") { format!("{}{}", &content_type, "; charset=utf-8") @@ -267,7 +263,7 @@ async fn serve_path( }; let mut builder = Response::builder() .header(http::header::CONTENT_TYPE, content_type.as_str()) - .header(http::header::LAST_MODIFIED, fmt_http_date(last_modified)) + .header(http::header::ETAG, file_etag) .header(http::header::ACCEPT_RANGES, "bytes") .header(http::header::SERVER, SERVER_NAME); @@ -294,9 +290,8 @@ async fn serve_path( Err(_) => return not_found(), }; - let file_size = meta.len(); let (start, end, builder) = - match parse_range(req.headers().get(http::header::RANGE), file_size, builder) { + match parse_range(req.headers().get(http::header::RANGE), file_len, builder) { Ok((start, end, builder)) => (start, end, builder), Err(e) => { return Response::builder() @@ -312,13 +307,39 @@ async fn serve_path( return Ok(build_500_resp()); }; } - if end != file_size - 1 { + if end != file_len - 1 { final_build(need_gzip, file.take(end - start + 1), builder) } else { final_build(need_gzip, file, builder) } } +fn handle_etag_match( + req: &Request, + file_etag: &str, +) -> Option>, Error>> { + let etag_header = req.headers().get(http::header::IF_NONE_MATCH)?; + let etag_of_req = etag_header.to_str().ok()?; + if etag_of_req == file_etag { + return Some( + Response::builder() + .status(StatusCode::NOT_MODIFIED) + .header(http::header::ETAG, file_etag) + .header(http::header::SERVER, SERVER_NAME) + .body(empty_body()), + ); + } + None +} + +fn cal_file_etag(last_modified: SystemTime, file_len: u64) -> String { + let last_modified_secs = last_modified + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_millis(); + format!("\"{:x}-{:x}\"", last_modified_secs, file_len) +} + fn final_build( need_gzip: bool, async_read: T, @@ -423,20 +444,16 @@ fn serve_favico( req: &Request, need_body: bool, ) -> Result>, Error> { - if let Some(request_if_modified_since) = req.headers().get(http::header::IF_MODIFIED_SINCE) { - if let Ok(request_if_modified_since) = request_if_modified_since.to_str() { - if request_if_modified_since == fmt_http_date(BOOTUP_TIME.to_owned()).as_str() { - return not_modified(BOOTUP_TIME.to_owned()); - } - } + let last_modified = BOOTUP_TIME.to_owned(); + let file_len = FAV_ICO.len() as u64; + let file_etag = cal_file_etag(last_modified, file_len); + if let Some(value) = handle_etag_match(req, &file_etag) { + return value; } Response::builder() .status(StatusCode::OK) .header(http::header::SERVER, SERVER_NAME) - .header( - http::header::LAST_MODIFIED, - fmt_http_date(BOOTUP_TIME.to_owned()), - ) + .header(http::header::ETAG, file_etag) .header(http::header::CONTENT_TYPE, "image/x-icon") .body(if need_body { full_body(FAV_ICO.to_vec()) @@ -461,14 +478,6 @@ fn not_found() -> Result>, Error> { .header(http::header::CONTENT_TYPE, "text/html; charset=utf-8") .body(full_body(H404)) } - -fn not_modified(last_modified: SystemTime) -> Result>, Error> { - Response::builder() - .status(StatusCode::NOT_MODIFIED) - .header(http::header::LAST_MODIFIED, fmt_http_date(last_modified)) - .header(http::header::SERVER, SERVER_NAME) - .body(empty_body()) -} const H404: &str = include_str!("../html/404.html"); const FAV_ICO: &[u8] = include_bytes!("../html/favicon.ico"); static BOOTUP_TIME: LazyLock = LazyLock::new(SystemTime::now);