|
| 1 | +use std::io::Write; |
| 2 | + |
1 | 3 | use apollo_gateway_types::communication::{GatewayClientError, MockGatewayClient}; |
2 | 4 | use apollo_gateway_types::deprecated_gateway_error::{ |
3 | 5 | KnownStarknetErrorCode, |
@@ -385,3 +387,67 @@ async fn request_body_size_limit_enforced( |
385 | 387 | let response = http_client.add_tx(tx).await; |
386 | 388 | assert_eq!(response.status(), expected_status, "Unexpected status: {}", response.status()); |
387 | 389 | } |
| 390 | + |
| 391 | +#[tokio::test] |
| 392 | +async fn zstd_compressed_request_decompression() { |
| 393 | + let mut mock_gateway_client = MockGatewayClient::new(); |
| 394 | + mock_gateway_client.expect_add_tx().times(1).return_const(Ok(default_gateway_output())); |
| 395 | + |
| 396 | + let http_client = HttpClientServerSetupBuilder::new(unique_u16!()) |
| 397 | + .with_mock_gateway_client(mock_gateway_client) |
| 398 | + .build() |
| 399 | + .await; |
| 400 | + |
| 401 | + let tx_json = serde_json::to_string(&rpc_invoke_tx()).unwrap(); |
| 402 | + let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap(); |
| 403 | + encoder.write_all(tx_json.as_bytes()).unwrap(); |
| 404 | + let compressed_body = encoder.finish().unwrap(); |
| 405 | + |
| 406 | + let response = http_client.add_rpc_tx_with_zstd(compressed_body).await; |
| 407 | + |
| 408 | + assert_eq!(response.status(), StatusCode::OK, "Request should be decompressed and handled"); |
| 409 | + let response_body = response.text().await.unwrap(); |
| 410 | + let gateway_output: GatewayOutput = |
| 411 | + serde_json::from_str(&response_body).expect("Response should be valid GatewayOutput"); |
| 412 | + assert_eq!(gateway_output.transaction_hash(), EXPECTED_TX_HASH); |
| 413 | +} |
| 414 | + |
| 415 | +#[tokio::test] |
| 416 | +async fn zstd_compressed_request_too_large() { |
| 417 | + let tx_json = serde_json::to_string(&rpc_invoke_tx()).unwrap(); |
| 418 | + let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap(); |
| 419 | + encoder.write_all(tx_json.as_bytes()).unwrap(); |
| 420 | + let compressed_body = encoder.finish().unwrap(); |
| 421 | + |
| 422 | + let http_client = HttpClientServerSetupBuilder::new(unique_u16!()) |
| 423 | + .with_max_request_body_size(compressed_body.len() - 1) |
| 424 | + .build() |
| 425 | + .await; |
| 426 | + |
| 427 | + let response = http_client.add_rpc_tx_with_zstd(compressed_body).await; |
| 428 | + |
| 429 | + assert_eq!(response.status(), StatusCode::PAYLOAD_TOO_LARGE); |
| 430 | +} |
| 431 | + |
| 432 | +#[tokio::test] |
| 433 | +async fn zstd_decompressed_request_too_large() { |
| 434 | + // 10 KB of repeated bytes — compresses to ~50 bytes with zstd. |
| 435 | + let large_body = vec![b'a'; 10 * 1024]; |
| 436 | + let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 0).unwrap(); |
| 437 | + encoder.write_all(&large_body).unwrap(); |
| 438 | + let compressed_body = encoder.finish().unwrap(); |
| 439 | + |
| 440 | + // Limit between compressed size and decompressed size. |
| 441 | + // compressed_body is ~50 bytes; decompressed is 10240 bytes. |
| 442 | + let max_request_body_size = large_body.len() - 1; |
| 443 | + assert!(compressed_body.len() < max_request_body_size); |
| 444 | + |
| 445 | + let http_client = HttpClientServerSetupBuilder::new(unique_u16!()) |
| 446 | + .with_max_request_body_size(max_request_body_size) |
| 447 | + .build() |
| 448 | + .await; |
| 449 | + |
| 450 | + let response = http_client.add_rpc_tx_with_zstd(compressed_body).await; |
| 451 | + |
| 452 | + assert_eq!(response.status(), StatusCode::PAYLOAD_TOO_LARGE); |
| 453 | +} |
0 commit comments