Skip to content

Commit 0b14d4f

Browse files
committed
properly test compression with streaming responses
1 parent 90339d3 commit 0b14d4f

File tree

2 files changed

+88
-8
lines changed

2 files changed

+88
-8
lines changed

dropshot/tests/integration-tests/gzip.rs

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
//! Test cases for gzip response compression.
44
5+
use bytes::Bytes;
56
use dropshot::endpoint;
67
use dropshot::ApiDescription;
78
use dropshot::HttpError;
89
use dropshot::HttpResponseOk;
910
use dropshot::RequestContext;
11+
use futures::stream;
1012
use http::{header, Method, StatusCode};
13+
use http_body_util::StreamBody;
14+
use hyper::body::Frame;
1115
use hyper::{Request, Response};
1216
use serde::{Deserialize, Serialize};
1317

@@ -100,6 +104,51 @@ struct TinyData {
100104
x: u8,
101105
}
102106

107+
const STREAMING_TEXT_CHUNK: &str = "{\"message\":\"streaming chunk\"}\n";
108+
const STREAMING_TEXT_CHUNK_COUNT: usize = 32;
109+
110+
fn streaming_payload() -> Vec<u8> {
111+
STREAMING_TEXT_CHUNK.repeat(STREAMING_TEXT_CHUNK_COUNT).into_bytes()
112+
}
113+
114+
fn streaming_body_stream(
115+
) -> impl futures::Stream<Item = Result<Frame<Bytes>, std::io::Error>> + Send {
116+
stream::iter((0..STREAMING_TEXT_CHUNK_COUNT).map(|_| {
117+
Result::<Frame<Bytes>, std::io::Error>::Ok(Frame::data(
118+
Bytes::from_static(STREAMING_TEXT_CHUNK.as_bytes()),
119+
))
120+
}))
121+
}
122+
123+
#[endpoint {
124+
method = GET,
125+
path = "/streaming-missing-content-type",
126+
}]
127+
async fn streaming_without_content_type(
128+
_rqctx: RequestContext<usize>,
129+
) -> Result<Response<dropshot::Body>, HttpError> {
130+
let body = dropshot::Body::wrap(StreamBody::new(streaming_body_stream()));
131+
Response::builder()
132+
.status(StatusCode::OK)
133+
.body(body)
134+
.map_err(|e| HttpError::for_internal_error(e.to_string()))
135+
}
136+
137+
#[endpoint {
138+
method = GET,
139+
path = "/streaming-with-content-type",
140+
}]
141+
async fn streaming_with_content_type(
142+
_rqctx: RequestContext<usize>,
143+
) -> Result<Response<dropshot::Body>, HttpError> {
144+
let body = dropshot::Body::wrap(StreamBody::new(streaming_body_stream()));
145+
Response::builder()
146+
.status(StatusCode::OK)
147+
.header(header::CONTENT_TYPE, "text/plain")
148+
.body(body)
149+
.map_err(|e| HttpError::for_internal_error(e.to_string()))
150+
}
151+
103152
fn api() -> ApiDescription<usize> {
104153
let mut api = ApiDescription::new();
105154
api.register(api_large_response).unwrap();
@@ -110,6 +159,8 @@ fn api() -> ApiDescription<usize> {
110159
api.register(api_xml_suffix_response).unwrap();
111160
api.register(api_no_content_response).unwrap();
112161
api.register(api_not_modified_response).unwrap();
162+
api.register(streaming_without_content_type).unwrap();
163+
api.register(streaming_with_content_type).unwrap();
113164
api
114165
}
115166

@@ -351,14 +402,12 @@ async fn test_no_gzip_without_accept_encoding() {
351402
}
352403

353404
#[tokio::test]
354-
async fn test_no_compression_for_streaming_responses() {
355-
// Test that streaming responses are not compressed even when client accepts gzip
356-
let testctx =
357-
test_setup("no_compression_streaming", crate::streaming::api());
405+
async fn test_streaming_without_content_type_skips_compression() {
406+
let testctx = test_setup("streaming_missing_content_type", api());
358407
let client = &testctx.client_testctx;
359408

360409
// Make request with Accept-Encoding: gzip header
361-
let uri = client.url("/streaming");
410+
let uri = client.url("/streaming-missing-content-type");
362411
let request = hyper::Request::builder()
363412
.method(http::Method::GET)
364413
.uri(&uri)
@@ -380,9 +429,40 @@ async fn test_no_compression_for_streaming_responses() {
380429

381430
assert_eq!(response.headers().get(header::CONTENT_ENCODING), None);
382431

383-
// Consume the body to verify it works (and to allow teardown to proceed)
432+
// Consume stream and confirm body is the uncompressed payload
384433
let body_bytes = get_response_bytes(&mut response).await;
385-
assert!(!body_bytes.is_empty(), "Streaming response should have content");
434+
assert_eq!(body_bytes, streaming_payload());
435+
436+
testctx.teardown().await;
437+
}
438+
439+
#[tokio::test]
440+
async fn test_streaming_with_content_type_is_compressed() {
441+
let testctx = test_setup("streaming_with_content_type_compressed", api());
442+
let client = &testctx.client_testctx;
443+
444+
let uri = client.url("/streaming-with-content-type");
445+
let request = hyper::Request::builder()
446+
.method(http::Method::GET)
447+
.uri(&uri)
448+
.header(http::header::ACCEPT_ENCODING, "gzip")
449+
.body(dropshot::Body::empty())
450+
.expect("Failed to construct request");
451+
452+
let mut response = client
453+
.make_request_with_request(request, http::StatusCode::OK)
454+
.await
455+
.expect("Streaming request with content type should succeed");
456+
457+
assert_gzip_encoded(&response);
458+
assert_eq!(
459+
response.headers().get(header::CONTENT_TYPE),
460+
Some(&header::HeaderValue::from_static("text/plain"))
461+
);
462+
463+
let compressed_body = get_response_bytes(&mut response).await;
464+
let decompressed = decompress_gzip(&compressed_body);
465+
assert_eq!(decompressed, streaming_payload(),);
386466

387467
testctx.teardown().await;
388468
}

dropshot/tests/integration-tests/streaming.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::common;
1212

1313
extern crate slog;
1414

15-
pub fn api() -> ApiDescription<usize> {
15+
fn api() -> ApiDescription<usize> {
1616
let mut api = ApiDescription::new();
1717
api.register(api_streaming).unwrap();
1818
api.register(api_not_streaming).unwrap();

0 commit comments

Comments
 (0)