diff --git a/Cargo.lock b/Cargo.lock index 0bac311..62f5330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -139,6 +154,22 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "async-compression" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd", + "zstd-safe", +] + [[package]] name = "async-trait" version = "0.1.81" @@ -321,6 +352,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -356,6 +408,10 @@ name = "cc" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +dependencies = [ + "jobserver", + "libc", +] [[package]] name = "cfg-if" @@ -1337,6 +1393,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -2773,8 +2838,10 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ + "async-compression", "bitflags 2.6.0", "bytes", + "futures-core", "futures-util", "http 1.1.0", "http-body", @@ -3403,3 +3470,31 @@ dependencies = [ "num_enum", "thiserror", ] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.12+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/contrib/docker/quickstart/geoprox.toml b/contrib/docker/quickstart/geoprox.toml index 6609c9d..d6fe724 100644 --- a/contrib/docker/quickstart/geoprox.toml +++ b/contrib/docker/quickstart/geoprox.toml @@ -4,6 +4,8 @@ http_addr = '0.0.0.0' # The port the server will listen on http_port = 5000 +# Timeout duration in seconds +timeout = 10 [shard] # Determines the default geohash length for inserts diff --git a/geoprox-server/CHANGELOG.md b/geoprox-server/CHANGELOG.md index 6e9902e..a5a45ad 100644 --- a/geoprox-server/CHANGELOG.md +++ b/geoprox-server/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Added support for `Accept-Encoding`(via [`compression-full`](https://docs.rs/crate/tower-http/0.5.2/features#compression-full) feature in `tower-http`) and `Content-Encoding`(via [`decompression-full`](https://docs.rs/crate/tower-http/0.5.2/features#decompression-full) feature in `tower-http`) headers, [timeouts](https://docs.rs/crate/tower-http/0.5.2/features#timeout) and [tracing](https://docs.rs/crate/tower-http/0.5.2/features#trace) in responses. - Added support for path normalization (i.e trailing slash or no trailing slash) ([#4](https://github.com/ezrasingh/geoprox/issues/4)) ## 0.4.1 diff --git a/geoprox-server/Cargo.toml b/geoprox-server/Cargo.toml index 12696b9..54e6880 100644 --- a/geoprox-server/Cargo.toml +++ b/geoprox-server/Cargo.toml @@ -20,7 +20,14 @@ serde = { version = "1.0.199", features = ["derive", "rc"] } serde_json = "1.0.116" tokio = { version = "1.37.0", features = ["full"] } tower = "0.4.13" -tower-http = { version = "0.5.2", features = ["fs", "trace", "normalize-path"] } +tower-http = { version = "0.5.2", features = [ + "fs", + "trace", + "timeout", + "compression-full", + "decompression-full", + "normalize-path", +] } tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } utoipa = { version = "4.2.3", features = ["axum_extras"] } diff --git a/geoprox-server/src/config.rs b/geoprox-server/src/config.rs index e12927b..4dc4605 100644 --- a/geoprox-server/src/config.rs +++ b/geoprox-server/src/config.rs @@ -3,13 +3,16 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; pub const DEFAULT_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); pub const DEFAULT_PORT: u16 = 5000; +pub const DEFAULT_TIMEOUT: u64 = 10; #[derive(Clone, Debug, Deserialize)] pub struct ServerConfig { - /// The address the server will bind to + /// The address the server will bind to (default 0.0.0.0) pub http_addr: Option, - /// The port the server will listen on + /// The port the server will listen on (default 5000) pub http_port: Option, + /// Timeout duration in seconds (default 10) + pub timout: Option, } impl Default for ServerConfig { @@ -17,6 +20,7 @@ impl Default for ServerConfig { Self { http_addr: Some(DEFAULT_ADDR), http_port: Some(DEFAULT_PORT), + timout: Some(DEFAULT_TIMEOUT), } } } diff --git a/geoprox-server/src/lib.rs b/geoprox-server/src/lib.rs index 342ba65..bf5049b 100644 --- a/geoprox-server/src/lib.rs +++ b/geoprox-server/src/lib.rs @@ -9,10 +9,15 @@ use app::AppState; use config::ServerConfig; use geoprox_core::models::GeoShardConfig; -use axum::extract::Request; use axum::routing::Router; +use axum::{body::Bytes, extract::Request}; +use std::time::Duration; use tower::Layer; -use tower_http::normalize_path::NormalizePathLayer; +use tower_http::compression::CompressionLayer; +use tower_http::decompression::DecompressionLayer; +use tower_http::timeout::TimeoutLayer; +use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse}; +use tower_http::{normalize_path::NormalizePathLayer, trace::TraceLayer}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; /// Start http server @@ -29,7 +34,30 @@ pub async fn run(server_config: ServerConfig, shard_config: GeoShardConfig) { let state = AppState::new(server_config.clone(), shard_config); let router = Router::new() .nest("/api/v1/", api::routes(state)) - .merge(api::docs::router()); + .merge(api::docs::router()) + .layer( + tower::ServiceBuilder::new() + .layer( + // Add high level tracing/logging to all requests + TraceLayer::new_for_http() + .on_body_chunk(|chunk: &Bytes, latency: Duration, _: &tracing::Span| { + tracing::trace!(size_bytes = chunk.len(), latency = ?latency, "sending body chunk") + }) + .make_span_with(DefaultMakeSpan::new().include_headers(true)) + .on_response(DefaultOnResponse::new() + .include_headers(true). + latency_unit(tower_http::LatencyUnit::Micros) + ), + ) + // Set a timeout + .layer(TimeoutLayer::new( + Duration::from_secs(server_config.timout.unwrap_or(config::DEFAULT_TIMEOUT))) + ) + // Compress responses + .layer(CompressionLayer::new()) + // Decompress requests + .layer(DecompressionLayer::new()) + ); // normalize paths on all routes let router = NormalizePathLayer::trim_trailing_slash().layer(router); diff --git a/geoprox/README.md b/geoprox/README.md index fd706dd..6422a75 100644 --- a/geoprox/README.md +++ b/geoprox/README.md @@ -78,6 +78,8 @@ Here's an example configuration file in `TOML` format: http_addr = '0.0.0.0' # The port the server will listen on http_port = 5000 +# Timeout duration in seconds +timeout = 10 [shard] # Determines the default geohash length for inserts diff --git a/geoprox/src/main.rs b/geoprox/src/main.rs index 7508ad2..c28cede 100644 --- a/geoprox/src/main.rs +++ b/geoprox/src/main.rs @@ -19,6 +19,7 @@ fn main() { ServerConfig { http_addr: Some(socket.ip()), http_port: Some(socket.port()), + ..server_conf }, shard_conf, )