From 9b4a9932aaa5f4e8c755068594fe9cce14d18097 Mon Sep 17 00:00:00 2001 From: Enigbe Date: Sun, 14 Dec 2025 23:07:36 +0100 Subject: [PATCH 1/3] build: add postgres docker service --- rust/docker-compose.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 rust/docker-compose.yml diff --git a/rust/docker-compose.yml b/rust/docker-compose.yml new file mode 100644 index 0000000..0507974 --- /dev/null +++ b/rust/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.8' +services: + postgres: + image: postgres:15 + environment: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + volumes: + - postgres-data:/var/lib/postgresql/data + - ./impls/src/postgres/sql/v0_create_vss_db.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" + networks: + - app-network + +volumes: + postgres-data: + +networks: + app-network: + driver: bridge From a163ae796253d43110b3b83bc7aaf315ce4b7afa Mon Sep 17 00:00:00 2001 From: Enigbe Date: Sun, 14 Dec 2025 23:31:06 +0100 Subject: [PATCH 2/3] feat: bound request body size --- rust/server/src/vss_service.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/rust/server/src/vss_service.rs b/rust/server/src/vss_service.rs index 811bbbd..78f0242 100644 --- a/rust/server/src/vss_service.rs +++ b/rust/server/src/vss_service.rs @@ -1,4 +1,4 @@ -use http_body_util::{BodyExt, Full}; +use http_body_util::{BodyExt, Full, Limited}; use hyper::body::{Bytes, Incoming}; use hyper::service::Service; use hyper::{Request, Response, StatusCode}; @@ -18,6 +18,8 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; +const MAXIMUM_REQUEST_BODY_SIZE: u16 = 65_535; + #[derive(Clone)] pub struct VssService { store: Arc, @@ -110,8 +112,17 @@ async fn handle_request< Ok(auth_response) => auth_response.user_token, Err(e) => return Ok(build_error_response(e)), }; - // TODO: we should bound the amount of data we read to avoid allocating too much memory. - let bytes = body.collect().await?.to_bytes(); + + let limited_body = Limited::new(body, MAXIMUM_REQUEST_BODY_SIZE.into()); + let bytes = match limited_body.collect().await { + Ok(body) => body.to_bytes(), + Err(_) => { + return Ok(Response::builder() + .status(StatusCode::PAYLOAD_TOO_LARGE) + .body(Full::new(Bytes::from("Request body too large"))) + .unwrap()); + }, + }; match T::decode(bytes) { Ok(request) => match handler(store.clone(), user_token, request).await { Ok(response) => Ok(Response::builder() From fbdd957ce51ea89b8193f57c732b1f3e66e71861 Mon Sep 17 00:00:00 2001 From: Enigbe Date: Tue, 16 Dec 2025 09:12:03 +0100 Subject: [PATCH 3/3] feat: configure request body limit --- rust/server/src/main.rs | 7 ++- rust/server/src/util/config.rs | 1 + rust/server/src/vss_service.rs | 75 ++++++++++++++++++++++++++---- rust/server/vss-server-config.toml | 1 + 4 files changed, 73 insertions(+), 11 deletions(-) diff --git a/rust/server/src/main.rs b/rust/server/src/main.rs index 38fdccd..7175d6f 100644 --- a/rust/server/src/main.rs +++ b/rust/server/src/main.rs @@ -17,7 +17,7 @@ use tokio::signal::unix::SignalKind; use hyper::server::conn::http1; use hyper_util::rt::TokioIo; -use crate::vss_service::VssService; +use crate::vss_service::{VssService, VssServiceConfig}; use api::auth::{Authorizer, NoopAuthorizer}; use api::kv_store::KvStore; use impls::postgres_store::{Certificate, PostgresPlaintextBackend, PostgresTlsBackend}; @@ -118,7 +118,10 @@ fn main() { match res { Ok((stream, _)) => { let io_stream = TokioIo::new(stream); - let vss_service = VssService::new(Arc::clone(&store), Arc::clone(&authorizer)); + let vss_service_config = if let Some(req_body_size) = config.server_config.maximum_request_body_size { + VssServiceConfig::new(req_body_size) + } else {VssServiceConfig::default()}; + let vss_service = VssService::new(Arc::clone(&store), Arc::clone(&authorizer), vss_service_config); runtime.spawn(async move { if let Err(err) = http1::Builder::new().serve_connection(io_stream, vss_service).await { eprintln!("Failed to serve connection: {}", err); diff --git a/rust/server/src/util/config.rs b/rust/server/src/util/config.rs index 801d1bd..6e04ccd 100644 --- a/rust/server/src/util/config.rs +++ b/rust/server/src/util/config.rs @@ -10,6 +10,7 @@ pub(crate) struct Config { pub(crate) struct ServerConfig { pub(crate) host: String, pub(crate) port: u16, + pub(crate) maximum_request_body_size: Option, } #[derive(Deserialize)] diff --git a/rust/server/src/vss_service.rs b/rust/server/src/vss_service.rs index 78f0242..a07a296 100644 --- a/rust/server/src/vss_service.rs +++ b/rust/server/src/vss_service.rs @@ -18,17 +18,45 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; -const MAXIMUM_REQUEST_BODY_SIZE: u16 = 65_535; +const MAXIMUM_REQUEST_BODY_SIZE: usize = 20 * 1024 * 1024; +const DEFAULT_REQUEST_BODY_SIZE: usize = 10 * 1024 * 1024; + +#[derive(Clone)] +pub(crate) struct VssServiceConfig { + maximum_request_body_size: usize, +} + +impl VssServiceConfig { + pub fn new(maximum_request_body_size: usize) -> Self { + let capped = maximum_request_body_size.min(MAXIMUM_REQUEST_BODY_SIZE); + if capped < maximum_request_body_size { + eprintln!( + "Warning: maximum_request_body_size {} exceeds limit, capped to {}.", + maximum_request_body_size, MAXIMUM_REQUEST_BODY_SIZE + ); + } + Self { maximum_request_body_size: capped } + } +} + +impl Default for VssServiceConfig { + fn default() -> Self { + Self { maximum_request_body_size: DEFAULT_REQUEST_BODY_SIZE } + } +} #[derive(Clone)] pub struct VssService { store: Arc, authorizer: Arc, + config: VssServiceConfig, } impl VssService { - pub(crate) fn new(store: Arc, authorizer: Arc) -> Self { - Self { store, authorizer } + pub(crate) fn new( + store: Arc, authorizer: Arc, config: VssServiceConfig, + ) -> Self { + Self { store, authorizer, config } } } @@ -43,22 +71,51 @@ impl Service> for VssService { let store = Arc::clone(&self.store); let authorizer = Arc::clone(&self.authorizer); let path = req.uri().path().to_owned(); + let maximum_request_body_size = self.config.maximum_request_body_size; Box::pin(async move { let prefix_stripped_path = path.strip_prefix(BASE_PATH_PREFIX).unwrap_or_default(); match prefix_stripped_path { "/getObject" => { - handle_request(store, authorizer, req, handle_get_object_request).await + handle_request( + store, + authorizer, + req, + maximum_request_body_size, + handle_get_object_request, + ) + .await }, "/putObjects" => { - handle_request(store, authorizer, req, handle_put_object_request).await + handle_request( + store, + authorizer, + req, + maximum_request_body_size, + handle_put_object_request, + ) + .await }, "/deleteObject" => { - handle_request(store, authorizer, req, handle_delete_object_request).await + handle_request( + store, + authorizer, + req, + maximum_request_body_size, + handle_delete_object_request, + ) + .await }, "/listKeyVersions" => { - handle_request(store, authorizer, req, handle_list_object_request).await + handle_request( + store, + authorizer, + req, + maximum_request_body_size, + handle_list_object_request, + ) + .await }, _ => { let error_msg = "Invalid request path.".as_bytes(); @@ -99,7 +156,7 @@ async fn handle_request< Fut: Future> + Send, >( store: Arc, authorizer: Arc, request: Request, - handler: F, + maximum_request_body_size: usize, handler: F, ) -> Result<>>::Response, hyper::Error> { let (parts, body) = request.into_parts(); let headers_map = parts @@ -113,7 +170,7 @@ async fn handle_request< Err(e) => return Ok(build_error_response(e)), }; - let limited_body = Limited::new(body, MAXIMUM_REQUEST_BODY_SIZE.into()); + let limited_body = Limited::new(body, maximum_request_body_size); let bytes = match limited_body.collect().await { Ok(body) => body.to_bytes(), Err(_) => { diff --git a/rust/server/vss-server-config.toml b/rust/server/vss-server-config.toml index 8c3d9c0..3ee205d 100644 --- a/rust/server/vss-server-config.toml +++ b/rust/server/vss-server-config.toml @@ -1,6 +1,7 @@ [server_config] host = "127.0.0.1" port = 8080 +# maximum_request_body_size = 10485760 # Optional: maximum request body size in bytes capped at 20971520 (20 MB). [postgresql_config] username = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_USERNAME`