Add cache-busting and server headers with a wrapper.
In order to support custom headers for various response types, this commit adds a wrapper type, ResponseWrapper, which can service all types of response in `bin`. For paste objects, the preferred `Last-Modified` is used, so that caches can compare their exact timings with the HEAD response when revalidating. For static objects, an `ETag` is used instead, based on the Cargo version and git hash of the codebase at compilation time; a `build.rs` is used for this.
This commit is contained in:
parent
55ed495b83
commit
2ab7ddb9c8
12 changed files with 291 additions and 152 deletions
|
|
@ -1,6 +1,5 @@
|
|||
pub mod index;
|
||||
pub mod pretty_retrieve;
|
||||
pub mod pretty_retrieve_ext;
|
||||
pub mod retrieve;
|
||||
pub mod static_files;
|
||||
pub mod submit;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,62 @@
|
|||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::Template;
|
||||
use std::fs;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind::InvalidData;
|
||||
use std::io::ErrorKind::{InvalidData, NotFound};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::get_upload_dir;
|
||||
use crate::models::maybe_redirect::MaybeRedirect;
|
||||
use crate::models::paste_id::PasteId;
|
||||
use crate::models::pretty::get_pretty_body;
|
||||
use crate::models::pretty_syntax::PasteIdSyntax;
|
||||
use crate::models::response_wrapper::ResponseWrapper;
|
||||
|
||||
#[get("/p/<id>", rank = 2)]
|
||||
pub async fn pretty_retrieve(id: PasteId<'_>) -> Option<MaybeRedirect> {
|
||||
let filepath = Path::new(&get_upload_dir()).join(format!("{id}", id = id));
|
||||
pub async fn pretty_retrieve(id: PasteId<'_>) -> ResponseWrapper<Template> {
|
||||
pretter_retrieve_inner(&id.to_string(), "txt").await
|
||||
}
|
||||
|
||||
let contents = match get_pretty_body(&filepath, &String::from("txt")) {
|
||||
#[get("/p/<id_ext>", rank = 1)]
|
||||
pub async fn pretty_retrieve_ext(
|
||||
id_ext: PasteIdSyntax<'_>,
|
||||
) -> ResponseWrapper<Template> {
|
||||
let id = id_ext.get_fname();
|
||||
let ext = id_ext.get_ext();
|
||||
|
||||
pretter_retrieve_inner(id, ext).await
|
||||
}
|
||||
|
||||
pub async fn pretter_retrieve_inner(
|
||||
id: &str,
|
||||
ext: &str,
|
||||
) -> ResponseWrapper<Template> {
|
||||
let filepath = Path::new(&get_upload_dir()).join(id.to_string());
|
||||
|
||||
let modified_date =
|
||||
match fs::metadata(&filepath).and_then(|m| m.modified()) {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.kind() == NotFound => {
|
||||
return ResponseWrapper::not_found(id);
|
||||
}
|
||||
Err(e) => {
|
||||
return ResponseWrapper::server_error(e.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
let contents = match get_pretty_body(&filepath, ext) {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.kind() == InvalidData => {
|
||||
return Some(Redirect::to(format!("/{}", id)).into());
|
||||
return ResponseWrapper::redirect(Redirect::permanent(format!(
|
||||
"/{}",
|
||||
id
|
||||
)));
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
Err(e) if e.kind() == NotFound => {
|
||||
return ResponseWrapper::not_found(id)
|
||||
}
|
||||
Err(e) => {
|
||||
return ResponseWrapper::server_error(e.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -30,7 +66,7 @@ pub async fn pretty_retrieve(id: PasteId<'_>) -> Option<MaybeRedirect> {
|
|||
let rendered = Template::render("pretty.html", &map);
|
||||
|
||||
match tree_magic::match_filepath("text/plain", &filepath) {
|
||||
true => Some(rendered.into()),
|
||||
false => None,
|
||||
true => ResponseWrapper::paste_response(rendered, modified_date),
|
||||
false => ResponseWrapper::server_error("media type unacceptable"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
use rocket::response::Redirect;
|
||||
use rocket_dyn_templates::Template;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind::InvalidData;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::get_upload_dir;
|
||||
use crate::models::maybe_redirect::MaybeRedirect;
|
||||
use crate::models::pretty::get_pretty_body;
|
||||
use crate::models::pretty_syntax::PasteIdSyntax;
|
||||
|
||||
#[get("/p/<id_ext>", rank = 1)]
|
||||
pub async fn pretty_retrieve_ext(
|
||||
id_ext: PasteIdSyntax<'_>,
|
||||
) -> Option<MaybeRedirect> {
|
||||
let id = id_ext.get_fname();
|
||||
let ext = id_ext.get_ext();
|
||||
|
||||
let filepath = Path::new(&get_upload_dir()).join(id.to_string());
|
||||
|
||||
let contents = match get_pretty_body(&filepath, &ext.to_string()) {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.kind() == InvalidData => {
|
||||
return Some(Redirect::to(format!("/{}", id)).into());
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert("title", id.to_string());
|
||||
map.insert("body", contents);
|
||||
let rendered = Template::render("pretty.html", &map);
|
||||
|
||||
match tree_magic::match_filepath("text/plain", &filepath) {
|
||||
true => Some(rendered.into()),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,46 @@
|
|||
use std::fs::File;
|
||||
use std::io::ErrorKind::NotFound;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::get_upload_dir;
|
||||
use crate::models::paste_id::PasteId;
|
||||
use crate::models::pretty_syntax::PasteIdSyntax;
|
||||
use crate::models::response_wrapper::ResponseWrapper;
|
||||
|
||||
#[get("/<id>", rank = 2)]
|
||||
pub async fn retrieve(id: PasteId<'_>) -> Option<File> {
|
||||
// let filename = format!("upload/{id}", id = id);
|
||||
|
||||
File::open(get_upload_dir().join(format!("{id}", id = id))).ok()
|
||||
#[get("/r/<id>", rank = 2)]
|
||||
pub async fn retrieve(id: PasteId<'_>) -> ResponseWrapper<File> {
|
||||
retrieve_inner(&id.to_string()).await
|
||||
}
|
||||
|
||||
// rank 1 here because this would be more oftenly used
|
||||
#[get("/<id_ext>", rank = 1)]
|
||||
pub async fn retrieve_ext(id_ext: PasteIdSyntax<'_>) -> Option<File> {
|
||||
// let filename = format!("upload/{id}", id = id_ext.get_fname());
|
||||
#[get("/r/<id_ext>", rank = 1)]
|
||||
pub async fn retrieve_ext(id_ext: PasteIdSyntax<'_>) -> ResponseWrapper<File> {
|
||||
retrieve_inner(&id_ext.get_fname().to_string()).await
|
||||
}
|
||||
|
||||
File::open(get_upload_dir().join(id_ext.get_fname().to_string())).ok()
|
||||
pub async fn retrieve_inner(id: &str) -> ResponseWrapper<File> {
|
||||
let filepath = Path::new(&get_upload_dir()).join(id.to_string());
|
||||
|
||||
let modified_date =
|
||||
match std::fs::metadata(&filepath).and_then(|m| m.modified()) {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.kind() == NotFound => {
|
||||
return ResponseWrapper::not_found(id);
|
||||
}
|
||||
Err(e) => {
|
||||
return ResponseWrapper::server_error(e.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
let file = match File::open(&filepath) {
|
||||
Ok(v) => v,
|
||||
Err(e) if e.kind() == NotFound => {
|
||||
return ResponseWrapper::not_found(id)
|
||||
}
|
||||
Err(e) => {
|
||||
return ResponseWrapper::server_error(e.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
ResponseWrapper::paste_response(file, modified_date)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::models::response_wrapper::ResponseWrapper;
|
||||
use rocket::http::ContentType;
|
||||
use rust_embed::RustEmbed;
|
||||
use std::{borrow::Cow, ffi::OsStr, path::PathBuf};
|
||||
|
|
@ -9,9 +10,12 @@ struct Static;
|
|||
#[get("/static/<file..>")]
|
||||
pub fn static_files(
|
||||
file: PathBuf,
|
||||
) -> Option<(ContentType, Cow<'static, [u8]>)> {
|
||||
) -> ResponseWrapper<(ContentType, Cow<'static, [u8]>)> {
|
||||
let filename = file.display().to_string();
|
||||
let asset = Static::get(&filename)?;
|
||||
let asset = match Static::get(&filename) {
|
||||
Some(v) => v,
|
||||
None => return ResponseWrapper::not_found(&file.to_string_lossy()),
|
||||
};
|
||||
|
||||
let content_type = file
|
||||
.extension()
|
||||
|
|
@ -19,5 +23,5 @@ pub fn static_files(
|
|||
.and_then(ContentType::from_extension)
|
||||
.unwrap_or(ContentType::Bytes);
|
||||
|
||||
Some((content_type, asset.data))
|
||||
ResponseWrapper::meta_response((content_type, asset.data))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue