Skip to content

Commit ed1dcbc

Browse files
committed
Suppress EOF errors caused by decompressing empty body
1 parent 7cfdf76 commit ed1dcbc

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

tower-http/src/compression_utils.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,24 @@ where
211211
return Poll::Ready(Some(Ok(Frame::data(chunk))));
212212
}
213213
Err(err) => {
214-
let body_error: Option<B::Error> = M::get_pin_mut(this.read)
214+
let body_error: Option<B::Error> = M::get_pin_mut(this.read.as_mut())
215215
.get_pin_mut()
216216
.project()
217217
.error
218218
.take();
219219

220+
let read_some_data = M::get_pin_mut(this.read.as_mut())
221+
.get_pin_mut()
222+
.project()
223+
.read_some_data;
224+
220225
if let Some(body_error) = body_error {
221226
return Poll::Ready(Some(Err(body_error.into())));
222227
} else if err.raw_os_error() == Some(SENTINEL_ERROR_CODE) {
223228
// SENTINEL_ERROR_CODE only gets used when storing
224229
// an underlying body error
225230
unreachable!()
226-
} else {
231+
} else if *read_some_data {
227232
return Poll::Ready(Some(Err(err.into())));
228233
}
229234
}
@@ -359,12 +364,17 @@ pin_project! {
359364
#[pin]
360365
inner: S,
361366
error: Option<E>,
367+
read_some_data: bool
362368
}
363369
}
364370

365371
impl<S, E> StreamErrorIntoIoError<S, E> {
366372
pub(crate) fn new(inner: S) -> Self {
367-
Self { inner, error: None }
373+
Self {
374+
inner,
375+
error: None,
376+
read_some_data: false,
377+
}
368378
}
369379

370380
/// Get a reference to the inner body
@@ -398,7 +408,10 @@ where
398408
let this = self.project();
399409
match ready!(this.inner.poll_next(cx)) {
400410
None => Poll::Ready(None),
401-
Some(Ok(value)) => Poll::Ready(Some(Ok(value))),
411+
Some(Ok(value)) => {
412+
*this.read_some_data = true;
413+
Poll::Ready(Some(Ok(value)))
414+
}
402415
Some(Err(err)) => {
403416
*this.error = Some(err);
404417
Poll::Ready(Some(Err(io::Error::from_raw_os_error(SENTINEL_ERROR_CODE))))

tower-http/src/decompression/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,28 @@ mod tests {
230230
let _: Response<DecompressionBody<_>> =
231231
client.ready().await.unwrap().call(req).await.unwrap();
232232
}
233+
234+
#[tokio::test]
235+
async fn decompress_empty() {
236+
let mut client = Decompression::new(Compression::new(service_fn(handle_empty)));
237+
238+
let req = Request::builder()
239+
.header("accept-encoding", "gzip")
240+
.body(Body::empty())
241+
.unwrap();
242+
let res = client.ready().await.unwrap().call(req).await.unwrap();
243+
244+
let body = res.into_body();
245+
let decompressed_data =
246+
String::from_utf8(body.collect().await.unwrap().to_bytes().to_vec()).unwrap();
247+
248+
assert_eq!(decompressed_data, "");
249+
}
250+
251+
async fn handle_empty(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
252+
let mut res = Response::new(Body::empty());
253+
res.headers_mut()
254+
.insert("content-encoding", "gzip".parse().unwrap());
255+
Ok(res)
256+
}
233257
}

0 commit comments

Comments
 (0)