|
|
@ -33,7 +33,8 @@ const GET_EXPIRY: &str = "expire"; |
|
|
|
/// GET param to pass secret token (as a substitute for header)
|
|
|
|
/// GET param to pass secret token (as a substitute for header)
|
|
|
|
const GET_SECRET: &str = "secret"; |
|
|
|
const GET_SECRET: &str = "secret"; |
|
|
|
|
|
|
|
|
|
|
|
const FAVICON: &[u8] = include_bytes!("favicon.ico"); |
|
|
|
const FAVICON: &[u8] = include_bytes!("embed/favicon.ico"); |
|
|
|
|
|
|
|
const ROBOTS: &[u8] = include_bytes!("embed/robots.txt"); |
|
|
|
|
|
|
|
|
|
|
|
/// Post ID (represented as a 16-digit hex string)
|
|
|
|
/// Post ID (represented as a 16-digit hex string)
|
|
|
|
type PostId = u64; |
|
|
|
type PostId = u64; |
|
|
@ -95,15 +96,23 @@ fn main() -> anyhow::Result<()> { |
|
|
|
|
|
|
|
|
|
|
|
info!("{} {}", method, req.raw_url()); |
|
|
|
info!("{} {}", method, req.raw_url()); |
|
|
|
|
|
|
|
|
|
|
|
if req.url() == "/favicon.ico" { |
|
|
|
if method == "GET" || method == "HEAD" { |
|
|
|
return decorate_response(Response::from_data("image/vnd.microsoft.icon", FAVICON)); |
|
|
|
if req.url() == "/favicon.ico" { |
|
|
|
|
|
|
|
return decorate_response(Response::from_data("image/vnd.microsoft.icon", FAVICON) |
|
|
|
|
|
|
|
.with_public_cache(86400 * 7)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if req.url() == "/robots.txt" { |
|
|
|
|
|
|
|
return decorate_response(Response::from_data("text/plain", ROBOTS) |
|
|
|
|
|
|
|
.with_public_cache(86400)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
store_w.gc_expired_posts_if_needed(); |
|
|
|
store_w.gc_expired_posts_if_needed(); |
|
|
|
|
|
|
|
|
|
|
|
let resp = match method { |
|
|
|
let resp = match method { |
|
|
|
"POST" | "PUT" => store_w.serve_post_put(req), |
|
|
|
"POST" | "PUT" => store_w.serve_post_put(req), |
|
|
|
"GET" | "HEAD" => store_w.serve_get_head(req), |
|
|
|
"GET" | "HEAD" => store_w.serve_get(req), |
|
|
|
"DELETE" => store_w.serve_delete(req), |
|
|
|
"DELETE" => store_w.serve_delete(req), |
|
|
|
_ => rouille::Response::empty_400(), |
|
|
|
_ => rouille::Response::empty_400(), |
|
|
|
}; |
|
|
|
}; |
|
|
@ -330,8 +339,8 @@ impl Repository { |
|
|
|
resp.with_additional_header(HDR_EXPIRY, post.time_remains().as_secs().to_string()) |
|
|
|
resp.with_additional_header(HDR_EXPIRY, post.time_remains().as_secs().to_string()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Serve a GET or HEAD request
|
|
|
|
/// Serve a GET request
|
|
|
|
fn serve_get_head(&mut self, req: &Request) -> Response { |
|
|
|
fn serve_get(&mut self, req: &Request) -> Response { |
|
|
|
let post_id = match self.request_to_post_id(req, false) { |
|
|
|
let post_id = match self.request_to_post_id(req, false) { |
|
|
|
Ok(Some(pid)) => pid, |
|
|
|
Ok(Some(pid)) => pid, |
|
|
|
Ok(None) => return error_with_text(400, "File ID required."), |
|
|
|
Ok(None) => return error_with_text(400, "File ID required."), |
|
|
@ -343,36 +352,36 @@ impl Repository { |
|
|
|
warn!("GET of expired post!"); |
|
|
|
warn!("GET of expired post!"); |
|
|
|
Response::empty_404() |
|
|
|
Response::empty_404() |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
let data = self.data.get(&post.hash); |
|
|
|
let data = match self.data.get(&post.hash) { |
|
|
|
if data.is_none() { |
|
|
|
Some((_uses, data)) => data, |
|
|
|
error!("No matching data!"); |
|
|
|
None => { |
|
|
|
return error_with_text(500, "File data lost."); |
|
|
|
error!("No matching data!"); |
|
|
|
} |
|
|
|
return error_with_text(500, "File data lost."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
let seconds_remains = post.expires.signed_duration_since(Utc::now()) |
|
|
|
let seconds_remains = post.expires.signed_duration_since(Utc::now()) |
|
|
|
.num_seconds(); |
|
|
|
.num_seconds(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let headers = vec![ |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
"Content-Type".into(), |
|
|
|
|
|
|
|
format!("{}; charset=utf8", post.mime).into(), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
"Cache-Control".into(), |
|
|
|
|
|
|
|
format!("public, max-age={}", seconds_remains).into() |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
HDR_EXPIRY.into(), |
|
|
|
|
|
|
|
seconds_remains.to_string().into() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
Response { |
|
|
|
Response { |
|
|
|
status_code: 200, |
|
|
|
status_code: 200, |
|
|
|
headers: vec![ |
|
|
|
headers, |
|
|
|
( |
|
|
|
data: ResponseBody::from_data(data.clone()), |
|
|
|
"Content-Type".into(), |
|
|
|
|
|
|
|
format!("{}; charset=utf8", post.mime).into(), |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
"Cache-Control".into(), |
|
|
|
|
|
|
|
format!("public, max-age={}", seconds_remains).into() |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
( |
|
|
|
|
|
|
|
HDR_EXPIRY.into(), |
|
|
|
|
|
|
|
seconds_remains.to_string().into() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
data: if req.method() == "HEAD" { |
|
|
|
|
|
|
|
ResponseBody::empty() |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
ResponseBody::from_data(data.unwrap().1.clone()) |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
upgrade: None, |
|
|
|
upgrade: None, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|