From 6435a0a372da32e2057123504894289012364653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 10 May 2020 19:53:47 +0200 Subject: [PATCH] add a readme, remove junk --- .gitignore | 1 + Cargo.lock | 72 +------------- Cargo.toml | 4 +- README.md | 79 +++++++++++++++ postit.db | Bin 73874 -> 0 bytes src/config.rs | 2 +- src/main.rs | 219 +++++++++++++++++++++++++++-------------- src/well_known_mime.rs | 43 +++++--- 8 files changed, 259 insertions(+), 161 deletions(-) create mode 100644 README.md delete mode 100644 postit.db diff --git a/.gitignore b/.gitignore index 765220b..495a94c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target .idea/ postit.json +postit.db diff --git a/Cargo.lock b/Cargo.lock index 78ea7d8..d63b7da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,52 +576,6 @@ dependencies = [ "memchr 1.0.2", ] -[[package]] -name = "num" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg 1.0.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg 1.0.0", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "num-integer" version = "0.1.42" @@ -632,29 +586,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" -dependencies = [ - "autocfg 1.0.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg 1.0.0", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.11" @@ -811,9 +742,8 @@ dependencies = [ "chrono", "clappconfig", "flate2", + "lazy_static", "log 0.4.8", - "num", - "num-derive", "parking_lot", "rand 0.7.3", "rouille", diff --git a/Cargo.toml b/Cargo.toml index 13667dc..804918a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "postit" version = "0.1.0" authors = ["Ondřej Hruška "] edition = "2018" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -21,5 +22,4 @@ bincode = "1.2.1" flate2 = "1.0.14" anyhow = "1.0.28" tree_magic = { version = "0.2.3", default_features = false, features = ["staticmime"] } -num = "0.2.1" -num-derive = "0.3.0" +lazy_static = "1.4.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fea6dc --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# PostIt file sharing server + +PostIt is designed to work as a temporary public storage for text (and other) +files uploaded to it by software that need a publicly reachable page without +hosting its own server or even having a public IP. + +The primary use case is to share diagnostic and contextual information +produced by Fediverse bots (think an interactive game where the game board +is rendered to an image or text file on demand). There are sure to be many +other uses I didn't think of. + +The uploaded files have a lifetime of 10 minutes, which can be shortened or +extended up to 1 hour (or more, as configured). + +## Uploading a file + +To upload a file, send a POST request to the running PostIt server. + +```none +$ curl -X POST --data-binary @RICKROLL.txt 0.0.0.0:7745 -i +HTTP/1.1 200 OK +X-Secret: 5273d775746e393b +X-Expire: 599 +Content-Length: 16 + +421d082ef85827ea +``` + +Take note of the `X-Secret` header, you will need it to update or delete the file. + +If you only want to share the file, this is all you need. Grab the file ID from the response body +and share it. The URL is `/`, e.g. + +```none +$ curl -X GET 0.0.0.0:7745/421d082ef85827ea -i +HTTP/1.1 200 OK +Content-Type: text/plain; charset=utf8 +X-Expire: 459 +Content-Length: 688 + +File content here... +``` + +### Content type + +The server attempts to auto-detect the file's `Content-Type`. The fallback is `text/plain`. +If you wish to set a custom type, use the `Content-Type` header when uploading the file, e.g. + +``` +$ curl -X POST --data-binary @RICKROLL.txt 0.0.0.0:7745 -i -H 'Content-Type: application/json' +``` + +### Expiration time + +To customize the expiration time, use the header `X-Expire: `, or a GET argument `?expire=` e.g. + +``` +$ curl -X POST --data-binary @RICKROLL.txt 0.0.0.0:7745 -i -H 'X-Expire: 60' +``` + +## Updating a file + +A file you uploaded can be deleted or modified using the **secret token** obtained in respose to its upload. +Send the token as the `X-Secret` header, or GET argument `?secret=....` + +File is updated by sending a `PUT` request to the file's URL. + +The `PUT` request can change file expiration (`X-Expire: ` or GET arg `expire`), +update its `Content-Type`, or replace its content. + +Note that sending `PUT` with empty body will *not* clear the file, in that case the file content is +not changed at all. This can be used to extend file's expiration without changing it in any other way +(by sending the `X-Expire` header). + +## Deleting a file + +The `DELETE` verb, unsurprisingly, deletes a file. As with `PUT`, the secret token is required. + +. diff --git a/postit.db b/postit.db deleted file mode 100644 index 156c8223c524247e3fbbf144a6a4aee3dc3c6336..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73874 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

c)B6|zUSs-< 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 diff --git a/src/config.rs b/src/config.rs index f03d461..cf6a0f4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -40,7 +40,7 @@ pub(crate) struct Config { pub(crate) compression: bool, /// Persistence file - pub(crate) persist_file : String, + pub(crate) persist_file: String, } impl Default for Config { diff --git a/src/main.rs b/src/main.rs index f451c87..15f47bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,36 +2,38 @@ extern crate serde_derive; #[macro_use] extern crate log; -#[macro_use] -extern crate num_derive; use crate::config::Config; +use crate::well_known_mime::Mime; +use chrono::{DateTime, Utc}; use clappconfig::{anyhow, AppConfig}; use parking_lot::Mutex; use rand::rngs::OsRng; use rand::Rng; use rouille::{Request, Response, ResponseBody}; + use std::borrow::Cow; use std::collections::HashMap; + +use std::fs::OpenOptions; use std::hash::{Hash, Hasher}; use std::io::Read; use std::time::Duration; -use chrono::{Utc, DateTime}; -use std::fs::OpenOptions; -use std::fmt::Display; -use serde::export::Formatter; -use std::fmt; -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{DeserializeOwned, Visitor}; -use crate::well_known_mime::Mime; +use siphasher::sip::SipHasher; mod config; mod well_known_mime; -const HDR_EXPIRES : &str = "X-Expires"; -const HDR_SECRET : &str = "X-Secret"; +/// Header to set expiry (seconds) +const HDR_EXPIRY: &str = "X-Expire"; +/// Header to pass secret token for update/delete +const HDR_SECRET: &str = "X-Secret"; +/// GET param to pass secret token (as a substitute for header) +const GET_EXPIRY: &str = "expire"; +/// GET param to pass secret token (as a substitute for header) +const GET_SECRET: &str = "secret"; -const FAVICON : &[u8] = include_bytes!("favicon.ico"); +const FAVICON: &[u8] = include_bytes!("favicon.ico"); /// Post ID (represented as a 16-digit hex string) type PostId = u64; @@ -41,7 +43,7 @@ type Secret = u64; type DataHash = u64; /// Post stored in the repository -#[derive(Debug,Serialize,Deserialize)] +#[derive(Debug, Serialize, Deserialize)] struct Post { /// Content-Type mime: Mime, @@ -59,6 +61,17 @@ impl Post { pub fn is_expired(&self) -> bool { self.expires < Utc::now() } + + /// Get remaining lifetime + pub fn time_remains(&self) -> Duration { + let seconds_remains = self.expires.signed_duration_since(Utc::now()) + .num_seconds(); + if seconds_remains < 0 { + Duration::from_secs(0) + } else { + Duration::from_secs(seconds_remains as u64) + } + } } fn main() -> anyhow::Result<()> { @@ -72,6 +85,7 @@ fn main() -> anyhow::Result<()> { error!("Load failed: {}", e); } } + store.gc_expired_posts(); store }); @@ -82,7 +96,7 @@ fn main() -> anyhow::Result<()> { info!("{} {}", method, req.raw_url()); if req.url() == "/favicon.ico" { - return Response::from_data("image/vnd.microsoft.icon", FAVICON); + return decorate_response(Response::from_data("image/vnd.microsoft.icon", FAVICON)); } store_w.gc_expired_posts_if_needed(); @@ -104,14 +118,21 @@ fn main() -> anyhow::Result<()> { warn!("Error resp: {}", resp.status_code); } - resp + decorate_response(resp) }); } +fn decorate_response(resp : Response) -> Response { + resp.without_header("Server") + .with_additional_header("Server", "postit.rs") + .with_additional_header("Access-Control-Allow-Origin", "*") + .with_additional_header("X-Version", env!("CARGO_PKG_VERSION")) +} + type PostsMap = HashMap; type DataMap = HashMap)>; -#[derive(Debug,Serialize,Deserialize)] +#[derive(Debug, Serialize, Deserialize)] struct Repository { #[serde(skip)] config: Config, @@ -175,7 +196,7 @@ impl Repository { .read(true) .open(&self.config.persist_file)?; - let result : Repository = if self.config.compression { + let result: Repository = if self.config.compression { let flate = flate2::read::DeflateDecoder::new(file); bincode::deserialize_from(flate)? } else { @@ -193,7 +214,7 @@ impl Repository { fn serve_delete(&mut self, req: &Request) -> Response { let post_id = match self.request_to_post_id(req, true) { Ok(Some(pid)) => pid, - Ok(None) => return error_with_text(400, "Post ID required."), + Ok(None) => return error_with_text(400, "File ID required."), Err(resp) => return resp, }; @@ -207,13 +228,16 @@ impl Repository { /// POST inserts a new record /// PUT updates a record fn serve_post_put(&mut self, req: &Request) -> Response { + let is_post = req.method() == "POST"; + let is_put = req.method() == "PUT"; + // Post ID is empty for POST, set for PUT let post_id = match self.request_to_post_id(req, true) { Ok(pid) => { - if req.method() == "PUT" && pid.is_none() { + if is_put && pid.is_none() { warn!("PUT without ID!"); return error_with_text(400, "PUT requires a file ID!"); - } else if req.method() == "POST" && pid.is_some() { + } else if is_post && pid.is_some() { warn!("POST with ID!"); return error_with_text(400, "Use PUT to update a file!"); } @@ -232,11 +256,17 @@ impl Repository { body.take(self.config.max_file_size as u64 + 1) .read_to_end(&mut data) .unwrap(); - if data.len() > self.config.max_file_size { + + if is_post && data.len() == 0 { + warn!("Empty body!"); + return error_with_text(400, "Empty body!"); + } else if data.len() > self.config.max_file_size { + warn!("Upload too large!"); return empty_error(413); } } else { - return error_with_text(400, "Empty body!"); + // Should not be possible + panic!("Req data None!"); } // Convert "application/x-www-form-urlencoded" to text/plain (CURL uses this) @@ -247,7 +277,13 @@ impl Repository { Some(v) => Some(v), }; - let expiry = match req.header(HDR_EXPIRES) { + let expiry = req.get_param(GET_EXPIRY); + let mut expiry_s = expiry.as_ref().map(|s| s.as_str()); + if expiry_s.is_none() { + expiry_s = req.header(HDR_EXPIRY); + } + + let expiry = match expiry_s { Some(text) => match text.parse() { Ok(v) => { let dur = Duration::from_secs(v); @@ -266,31 +302,38 @@ impl Repository { Err(_) => { return error_with_text( 400, - "Malformed \"X-Expires\", use relative time in seconds.", + "Malformed expiration, use relative time in seconds.", ); } }, None => None, }; - if let Some(id) = post_id { + let the_id; + + let resp = if let Some(id) = post_id { // UPDATE self.update(id, data, mime, expiry); + the_id = id; Response::text("Updated OK.") } else { // INSERT - let (id, token) = self.insert(data, mime, expiry.unwrap_or(self.config.default_expiry)); + the_id = self.insert(data, mime, expiry.unwrap_or(self.config.default_expiry)); + Response::text(format!("{:016x}", the_id)) + }; - Response::text(format!("{:016x}", id)) - .with_additional_header("X-Secret", format!("{:016x}", token)) - } + let post = self.posts.get(&the_id).unwrap(); + + resp + .with_additional_header(HDR_SECRET, format!("{:016x}", post.secret)) + .with_additional_header(HDR_EXPIRY, post.time_remains().as_secs().to_string()) } /// Serve a GET or HEAD request fn serve_get_head(&mut self, req: &Request) -> Response { let post_id = match self.request_to_post_id(req, false) { Ok(Some(pid)) => pid, - Ok(None) => return error_with_text(400, "Post ID required."), + Ok(None) => return error_with_text(400, "File ID required."), Err(resp) => return resp, }; @@ -305,12 +348,25 @@ impl Repository { return error_with_text(500, "File data lost."); } + let seconds_remains = post.expires.signed_duration_since(Utc::now()) + .num_seconds(); + Response { status_code: 200, - headers: vec![( - "Content-Type".into(), - format!("{}; charset=utf8", post.mime).into(), - )], + 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() + ) + ], data: if req.method() == "HEAD" { ResponseBody::empty() } else { @@ -361,30 +417,38 @@ impl Repository { None => { warn!("ID {} does not exist!", id); return Err(error_with_text(404, "No file with this ID!")); - }, + } Some(post) => { if post.is_expired() { warn!("Access of expired file {}!", id); return Err(error_with_text(404, "No file with this ID!")); } - let secret: u64 = match req.header(HDR_SECRET).map(|v| u64::from_str_radix(v, 16)) { - Some(Ok(bytes)) => bytes, - None => { - warn!("Missing secret token!"); - return Err(error_with_text(400, "Secret token required!")); - } - Some(Err(e)) => { - warn!("Token parse error: {:?}", e); - return Err(error_with_text(400, "Bad secret token format!")); - }, - }; + let secret = req.get_param(GET_SECRET); + let mut secret_str = secret.as_ref().map(|s| s.as_str()); + + if secret_str.is_none() { + secret_str = req.header(HDR_SECRET); + } + + let secret: u64 = + match secret_str.map(|v| u64::from_str_radix(v, 16)) { + Some(Ok(bytes)) => bytes, + None => { + warn!("Missing secret token!"); + return Err(error_with_text(400, "Secret token required!")); + } + Some(Err(e)) => { + warn!("Token parse error: {:?}", e); + return Err(error_with_text(400, "Bad secret token format!")); + } + }; if post.secret != secret { warn!("Secret token mismatch"); return Err(error_with_text(401, "Invalid secret token!")); } - }, + } } } @@ -394,7 +458,12 @@ impl Repository { /// Drop expired posts, if cleaning is due fn gc_expired_posts_if_needed(&mut self) { - if Utc::now().signed_duration_since(self.last_gc_time).to_std().unwrap_or_default() > self.config.expired_gc_interval { + if Utc::now() + .signed_duration_since(self.last_gc_time) + .to_std() + .unwrap_or_default() + > self.config.expired_gc_interval + { self.gc_expired_posts(); self.last_gc_time = Utc::now(); } @@ -425,7 +494,7 @@ impl Repository { /// Get hash of a byte vector (for deduplication) fn hash_data(data: &Vec) -> DataHash { - let mut hasher = siphasher::sip::SipHasher::new(); + let mut hasher = SipHasher::new(); data.hash(&mut hasher); hasher.finish() } @@ -462,7 +531,7 @@ impl Repository { } /// Insert a post - fn insert(&mut self, data: Vec, mime: Option<&str>, expires: Duration) -> (PostId, Secret) { + fn insert(&mut self, data: Vec, mime: Option<&str>, expires: Duration) -> PostId { info!( "Insert post with data of len {} bytes, mime {}, expiry {:?}", data.len(), @@ -473,12 +542,8 @@ impl Repository { let hash = Self::hash_data(&data); let mime = match mime { - None => { - Mime::from(tree_magic::from_u8(&data)) - }, - Some(explicit) => { - Mime::from(explicit) - }, + None => Mime::from(tree_magic::from_u8(&data)), + Some(explicit) => Mime::from(explicit), }; Self::store_data_or_increment_rc(&mut self.data, hash, data); @@ -492,7 +557,7 @@ impl Repository { let secret = OsRng.gen(); - debug!("Post ID = #{:016x}", post_id); + debug!("File ID = #{:016x} (http://{}:{}/{:016x})", post_id, self.config.host, self.config.port, post_id); debug!("Data hash = #{:016x}, mime {}", hash, mime); debug!("Secret = #{:016x}", secret); @@ -508,7 +573,7 @@ impl Repository { self.dirty = true; - (post_id, secret) + post_id } /// Update a post by ID @@ -523,18 +588,21 @@ impl Repository { .unwrap_or("unchanged".into()) ); - let hash = Self::hash_data(&data); let post = self.posts.get_mut(&id).unwrap(); // post existence was checked before - if hash != post.hash { - debug!("Data hash = #{:016x} (content changed)", hash); + if !data.is_empty() { + let hash = Self::hash_data(&data); - Self::drop_data_or_decrement_rc(&mut self.data, post.hash); - Self::store_data_or_increment_rc(&mut self.data, hash, data); - post.hash = hash; - self.dirty = true; - } else { - debug!("Data hash = #{:016x} (no change)", hash); + if hash != post.hash { + debug!("Data hash = #{:016x} (content changed)", hash); + + Self::drop_data_or_decrement_rc(&mut self.data, post.hash); + Self::store_data_or_increment_rc(&mut self.data, hash, data); + post.hash = hash; + self.dirty = true; + } else { + debug!("Data hash = #{:016x} (no change)", hash); + } } if let Some(mime) = mime { @@ -566,22 +634,25 @@ impl Repository { /// Serialize chrono unix timestamp as seconds mod serde_chrono_datetime_as_unix { + use chrono::{DateTime, NaiveDateTime, Utc}; use serde::{self, Deserialize, Deserializer, Serializer}; - use chrono::{DateTime, Utc, NaiveDateTime}; pub fn serialize(value: &DateTime, se: S) -> Result - where - S: Serializer, + where + S: Serializer, { se.serialize_i64(value.naive_utc().timestamp()) } pub fn deserialize<'de, D>(de: D) -> Result, D::Error> - where - D: Deserializer<'de>, + where + D: Deserializer<'de>, { let ts: i64 = i64::deserialize(de)?; - Ok(DateTime::from_utc(NaiveDateTime::from_timestamp(ts, 0), Utc)) + Ok(DateTime::from_utc( + NaiveDateTime::from_timestamp(ts, 0), + Utc, + )) } } diff --git a/src/well_known_mime.rs b/src/well_known_mime.rs index 09cf482..e3c5f0c 100644 --- a/src/well_known_mime.rs +++ b/src/well_known_mime.rs @@ -1,18 +1,20 @@ -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use std::fmt::{Write, Display, Formatter}; use std::fmt; -use serde::de::Visitor; +use std::fmt::{Display, Formatter}; +use lazy_static::lazy_static; -#[derive(Serialize,Deserialize,Debug,PartialEq,Eq,Hash)] +/// Mime type +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] pub enum Mime { + /// Well-known mime WellKnown(usize), + /// Custom mime Custom(String), } impl From for Mime { fn from(s: String) -> Self { - if let Ok(index) = WELL_KNOWN.binary_search(&s.as_str()) { - Mime::WellKnown(index) + if let Some(index) = MIME_LOOKUP.get(&s.as_str()) { + Mime::WellKnown(*index) } else { Mime::Custom(s) } @@ -21,8 +23,8 @@ impl From for Mime { impl<'a> From<&'a str> for Mime { fn from(s: &'a str) -> Self { - if let Ok(index) = WELL_KNOWN.binary_search(&s) { - Mime::WellKnown(index) + if let Some(index) = MIME_LOOKUP.get(&s) { + Mime::WellKnown(*index) } else { Mime::Custom(s.to_string()) } @@ -38,15 +40,16 @@ impl Display for Mime { } else { f.write_str("application/octet-stream") } - }, - Mime::Custom(s) => { - f.write_str(s) - }, + } + Mime::Custom(s) => f.write_str(s), } } } -// CAUTION!!!!! This list must be alphabetically sorted! +// The positions in this list must be kept stable - otherwise the persistence file may +// deserialize to the wrong type. +// +// If a new type needs to be added, add it at the end. const WELL_KNOWN : &[&str] = &[ "application/andrew-inset", "application/applixware", @@ -739,4 +742,18 @@ const WELL_KNOWN : &[&str] = &[ "video/x-msvideo", "video/x-sgi-movie", "x-conference/x-cooltalk", + // Extras + "application/toml", + "application/json5", ]; + +use std::collections::HashMap; +lazy_static!{ + static ref MIME_LOOKUP : HashMap<&'static str, usize> = { + let mut map = HashMap::new(); + for (n, entry) in WELL_KNOWN.iter().enumerate() { + map.insert(*entry, n); + } + map + }; +}