diff --git a/Cargo.lock b/Cargo.lock index 1f1e3ee..02c9fca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -556,6 +556,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" + [[package]] name = "block-buffer" version = "0.10.4" @@ -571,6 +577,12 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.4.0" @@ -639,7 +651,7 @@ checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" dependencies = [ "anstream", "anstyle", - "bitflags", + "bitflags 1.3.2", "clap_lex", "strsim", ] @@ -997,6 +1009,16 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "hdrhistogram" +version = "7.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" +dependencies = [ + "byteorder", + "num-traits", +] + [[package]] name = "heck" version = "0.4.1" @@ -1065,6 +1087,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" version = "1.8.0" @@ -1489,6 +1517,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.63" @@ -1517,13 +1551,43 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1585,7 +1649,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1616,7 +1680,7 @@ version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -1694,6 +1758,8 @@ dependencies = [ "time", "tokio", "tokio-util", + "tower", + "tower-http", "tracing", "tracing-error", "tracing-subscriber", @@ -1792,7 +1858,7 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -2110,14 +2176,37 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "hdrhistogram", + "indexmap", "pin-project", "pin-project-lite", + "rand", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower-http" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c" +dependencies = [ + "bitflags 2.3.2", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 31d5df5..062d413 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,8 @@ thiserror = "1.0.40" time = "0.3.21" tokio = { version = "1.28.2", features = ["fs", "io-util"] } tokio-util = { version = "0.7.8", features = ["io"] } +tower = { version = "0.4.13", features = ["full"] } +tower-http = { version = "0.4.1", features = ["cors"] } tracing = "0.1.37" tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter", "time"] } diff --git a/README.md b/README.md index f95f2f0..0c35d64 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This concept is backed by benchmarks from SQLite showing that it can be [faster Each bucket is saved to a separate `.sqlite3` database named after the bucket name. The [smithy](https://github.com/awslabs/smithy) generated bindings for `s3` are then mapped to the correct SQL calls against a very simple schema that is designed to be human accessible. -### data +### Data The main table, `data`, is a simple key/value with metadata store. @@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS data ( ) STRICT, WITHOUT ROWID; ``` -### multipart +### Multipart Uploads For `multipart` uploads two temporary tables are used: diff --git a/src/main.rs b/src/main.rs index 35ae3fa..6335683 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,22 +6,31 @@ use s3ite::Sqlite; use s3s::auth::SimpleAuth; use s3s::service::S3ServiceBuilder; +use tower::make::Shared; +use tower::ServiceBuilder; +use tower_http::cors::CorsLayer; +use std::net::IpAddr; +use std::net::SocketAddr; use std::net::TcpListener; use std::path::PathBuf; use clap::Parser; use hyper::server::Server; use tracing::info; +use tracing_subscriber::EnvFilter; #[derive(Debug, Parser)] struct Opt { #[clap(long, default_value = "localhost")] - host: String, + host: IpAddr, #[clap(long, default_value = "8014")] port: u16, + #[clap(long, default_value_t = true)] + permissive_cors: bool, + #[clap(long, requires("secret-key"))] access_key: Option, @@ -34,53 +43,54 @@ struct Opt { root: PathBuf, } -fn setup_tracing() { - use tracing_subscriber::EnvFilter; - - let env_filter = EnvFilter::from_default_env(); - // let enable_color = std::io::stdout().is_terminal(); // TODO - let enable_color = false; - - tracing_subscriber::fmt() - .pretty() - .with_env_filter(env_filter) - .with_ansi(enable_color) - .init(); -} - #[tokio::main] async fn main() -> Result { - setup_tracing(); + let env_filter = EnvFilter::from_default_env(); + tracing_subscriber::fmt().with_env_filter(env_filter).init(); + let opt = Opt::parse(); + // Parse addr + let addr = SocketAddr::new(opt.host, opt.port); + let listener = TcpListener::bind(addr)?; + // Setup S3 provider let sqlite = Sqlite::new(opt.root).await?; // Setup S3 service - let service = { - let mut b = S3ServiceBuilder::new(sqlite); + let s3_service = { + let mut s3 = S3ServiceBuilder::new(sqlite); // Enable authentication if let (Some(ak), Some(sk)) = (opt.access_key, opt.secret_key) { - b.set_auth(SimpleAuth::from_single(ak, sk)); + s3.set_auth(SimpleAuth::from_single(ak, sk)); } // Enable parsing virtual-hosted-style requests if let Some(domain_name) = opt.domain_name { - b.set_base_domain(domain_name); + s3.set_base_domain(domain_name); } - b.build() + s3.build().into_shared() }; // Run server - let listener = TcpListener::bind((opt.host.as_str(), opt.port))?; - let local_addr = listener.local_addr()?; - - let server = Server::from_tcp(listener)?.serve(service.into_shared().into_make_service()); - - info!("server is running at http://{local_addr}"); - server.with_graceful_shutdown(shutdown_signal()).await?; + // Add CorsLayer if defined + if opt.permissive_cors { + let service = Shared::new( + ServiceBuilder::new() + .layer(CorsLayer::very_permissive()) + .service(s3_service), + ); + let server = Server::from_tcp(listener)?.serve(service); + info!("server is running at http://{addr}"); + server.with_graceful_shutdown(shutdown_signal()).await?; + } else { + let service = Shared::new(ServiceBuilder::new().service(s3_service)); + let server = Server::from_tcp(listener)?.serve(service); + info!("server is running at http://{addr}"); + server.with_graceful_shutdown(shutdown_signal()).await?; + }; info!("server is stopped"); Ok(())