Skip to content

Commit 15417ec

Browse files
committed
feat: add request body size configuration
Add VssServiceConfig to make the maximum request body size configurable through the server configuration file, with a hard limit of 1GB. Additionally, add test coverage for 1GB maximum supported value size and verifies that storage backends can handle the configured maximum value size.
1 parent e539410 commit 15417ec

File tree

4 files changed

+98
-18
lines changed

4 files changed

+98
-18
lines changed

rust/api/src/kv_store_tests.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ macro_rules! define_kv_store_tests {
4444
create_test!(put_should_fail_when_global_version_mismatched);
4545
create_test!(put_should_succeed_when_no_global_version_is_given);
4646
create_test!(put_and_delete_should_succeed_as_atomic_transaction);
47+
create_test!(put_should_succeed_with_maximum_supported_value_size);
4748
create_test!(delete_should_succeed_when_item_exists);
4849
create_test!(delete_should_succeed_when_item_does_not_exist);
4950
create_test!(delete_should_be_idempotent);
@@ -266,6 +267,29 @@ pub trait KvStoreTestSuite {
266267
Ok(())
267268
}
268269

270+
async fn put_should_succeed_with_maximum_supported_value_size() -> Result<(), VssError> {
271+
const MAXIMUM_SUPPORTED_VALUE_SIZE: usize = 1024 * 1024 * 1024;
272+
const PROTOCOL_OVERHEAD_MARGIN: usize = 150;
273+
let kv_store = Self::create_store().await;
274+
let ctx = TestContext::new(&kv_store);
275+
276+
// Construct entry that's for a field that's the maximum size of a non-"large_object" object
277+
let large_value = vec![0u8; MAXIMUM_SUPPORTED_VALUE_SIZE - PROTOCOL_OVERHEAD_MARGIN];
278+
let kv = KeyValue { key: "k1".into(), version: 0, value: Bytes::from(large_value) };
279+
280+
// Put succeeds
281+
ctx.put_objects(None, vec![kv]).await?;
282+
283+
// Retrieval succeeds
284+
let result = ctx.get_object("k1").await?;
285+
assert_eq!(result.value.len(), MAXIMUM_SUPPORTED_VALUE_SIZE - PROTOCOL_OVERHEAD_MARGIN);
286+
assert!(result.value.iter().all(|&b| b == 0));
287+
288+
ctx.delete_object(result).await?;
289+
290+
Ok(())
291+
}
292+
269293
async fn delete_should_succeed_when_item_exists() -> Result<(), VssError> {
270294
let kv_store = Self::create_store().await;
271295
let ctx = TestContext::new(&kv_store);

rust/server/src/main.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use api::kv_store::KvStore;
2323
use auth_impls::JWTAuthorizer;
2424
use impls::postgres_store::{Certificate, PostgresPlaintextBackend, PostgresTlsBackend};
2525
use util::config::{Config, ServerConfig};
26-
use vss_service::VssService;
26+
use vss_service::{VssService, VssServiceConfig};
2727

2828
mod util;
2929
mod vss_service;
@@ -35,14 +35,17 @@ fn main() {
3535
std::process::exit(1);
3636
}
3737

38-
let Config { server_config: ServerConfig { host, port }, jwt_auth_config, postgresql_config } =
39-
match util::config::load_config(&args[1]) {
40-
Ok(cfg) => cfg,
41-
Err(e) => {
42-
eprintln!("Failed to load configuration: {}", e);
43-
std::process::exit(1);
44-
},
45-
};
38+
let Config {
39+
server_config: ServerConfig { host, port, maximum_request_body_size },
40+
jwt_auth_config,
41+
postgresql_config,
42+
} = match util::config::load_config(&args[1]) {
43+
Ok(cfg) => cfg,
44+
Err(e) => {
45+
eprintln!("Failed to load configuration: {}", e);
46+
std::process::exit(1);
47+
},
48+
};
4649
let addr: SocketAddr = match format!("{}:{}", host, port).parse() {
4750
Ok(addr) => addr,
4851
Err(e) => {
@@ -144,7 +147,10 @@ fn main() {
144147
match res {
145148
Ok((stream, _)) => {
146149
let io_stream = TokioIo::new(stream);
147-
let vss_service = VssService::new(Arc::clone(&store), Arc::clone(&authorizer));
150+
let vss_service_config = if let Some(req_body_size) = maximum_request_body_size {
151+
VssServiceConfig::new(req_body_size)
152+
} else {VssServiceConfig::default()};
153+
let vss_service = VssService::new(Arc::clone(&store), Arc::clone(&authorizer), vss_service_config);
148154
runtime.spawn(async move {
149155
if let Err(err) = http1::Builder::new().serve_connection(io_stream, vss_service).await {
150156
eprintln!("Failed to serve connection: {}", err);

rust/server/src/util/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) struct Config {
1111
pub(crate) struct ServerConfig {
1212
pub(crate) host: String,
1313
pub(crate) port: u16,
14+
pub(crate) maximum_request_body_size: Option<usize>,
1415
}
1516

1617
#[derive(Deserialize)]

rust/server/src/vss_service.rs

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,35 @@ use std::sync::Arc;
2020

2121
const MAXIMUM_REQUEST_BODY_SIZE: usize = 1024 * 1024 * 1024;
2222

23+
#[derive(Clone)]
24+
pub(crate) struct VssServiceConfig {
25+
maximum_request_body_size: usize,
26+
}
27+
28+
impl VssServiceConfig {
29+
pub fn new(maximum_request_body_size: usize) -> Self {
30+
Self { maximum_request_body_size: maximum_request_body_size.min(MAXIMUM_REQUEST_BODY_SIZE) }
31+
}
32+
}
33+
34+
impl Default for VssServiceConfig {
35+
fn default() -> Self {
36+
Self { maximum_request_body_size: MAXIMUM_REQUEST_BODY_SIZE }
37+
}
38+
}
39+
2340
#[derive(Clone)]
2441
pub struct VssService {
2542
store: Arc<dyn KvStore>,
2643
authorizer: Arc<dyn Authorizer>,
44+
config: VssServiceConfig,
2745
}
2846

2947
impl VssService {
30-
pub(crate) fn new(store: Arc<dyn KvStore>, authorizer: Arc<dyn Authorizer>) -> Self {
31-
Self { store, authorizer }
48+
pub(crate) fn new(
49+
store: Arc<dyn KvStore>, authorizer: Arc<dyn Authorizer>, config: VssServiceConfig,
50+
) -> Self {
51+
Self { store, authorizer, config }
3252
}
3353
}
3454

@@ -43,22 +63,51 @@ impl Service<Request<Incoming>> for VssService {
4363
let store = Arc::clone(&self.store);
4464
let authorizer = Arc::clone(&self.authorizer);
4565
let path = req.uri().path().to_owned();
66+
let maximum_request_body_size = self.config.maximum_request_body_size;
4667

4768
Box::pin(async move {
4869
let prefix_stripped_path = path.strip_prefix(BASE_PATH_PREFIX).unwrap_or_default();
4970

5071
match prefix_stripped_path {
5172
"/getObject" => {
52-
handle_request(store, authorizer, req, handle_get_object_request).await
73+
handle_request(
74+
store,
75+
authorizer,
76+
req,
77+
maximum_request_body_size,
78+
handle_get_object_request,
79+
)
80+
.await
5381
},
5482
"/putObjects" => {
55-
handle_request(store, authorizer, req, handle_put_object_request).await
83+
handle_request(
84+
store,
85+
authorizer,
86+
req,
87+
maximum_request_body_size,
88+
handle_put_object_request,
89+
)
90+
.await
5691
},
5792
"/deleteObject" => {
58-
handle_request(store, authorizer, req, handle_delete_object_request).await
93+
handle_request(
94+
store,
95+
authorizer,
96+
req,
97+
maximum_request_body_size,
98+
handle_delete_object_request,
99+
)
100+
.await
59101
},
60102
"/listKeyVersions" => {
61-
handle_request(store, authorizer, req, handle_list_object_request).await
103+
handle_request(
104+
store,
105+
authorizer,
106+
req,
107+
maximum_request_body_size,
108+
handle_list_object_request,
109+
)
110+
.await
62111
},
63112
_ => {
64113
let error_msg = "Invalid request path.".as_bytes();
@@ -99,7 +148,7 @@ async fn handle_request<
99148
Fut: Future<Output = Result<R, VssError>> + Send,
100149
>(
101150
store: Arc<dyn KvStore>, authorizer: Arc<dyn Authorizer>, request: Request<Incoming>,
102-
handler: F,
151+
maximum_request_body_size: usize, handler: F,
103152
) -> Result<<VssService as Service<Request<Incoming>>>::Response, hyper::Error> {
104153
let (parts, body) = request.into_parts();
105154
let headers_map = parts
@@ -113,7 +162,7 @@ async fn handle_request<
113162
Err(e) => return Ok(build_error_response(e)),
114163
};
115164

116-
let limited_body = Limited::new(body, MAXIMUM_REQUEST_BODY_SIZE.into());
165+
let limited_body = Limited::new(body, maximum_request_body_size);
117166
let bytes = match limited_body.collect().await {
118167
Ok(body) => body.to_bytes(),
119168
Err(_) => {

0 commit comments

Comments
 (0)