From 0e39128bfe2dddb3b2e458c24ad65aaf34b8930d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 21 Jun 2024 08:16:20 +0200 Subject: [PATCH] Only cap the input if content length header is given Currently RqLengthAware uses the InputStream#available to cap the input if no Content-Length header is given, but the return value does not say anything about the real content length, only how many bytes can be read without blocking (what might be 0). This now only returns a CapInputStream when a Content-Length is given in the request and adds a testcase for the given case. --- src/main/java/org/takes/rq/RqLengthAware.java | 22 +++++++------ .../java/org/takes/rq/RqLengthAwareTest.java | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/takes/rq/RqLengthAware.java b/src/main/java/org/takes/rq/RqLengthAware.java index 0436b0d74..b204da547 100644 --- a/src/main/java/org/takes/rq/RqLengthAware.java +++ b/src/main/java/org/takes/rq/RqLengthAware.java @@ -52,7 +52,9 @@ @EqualsAndHashCode(callSuper = true) public final class RqLengthAware extends RqWrap { - /** + private static final String CONTENT_LENGTH = "Content-Length"; + + /** * Ctor. * @param req Original request */ @@ -68,14 +70,16 @@ public RqLengthAware(final Request req) { */ private static InputStream cap(final Request req) throws IOException { final Iterator hdr = new RqHeaders.Base(req) - .header("Content-Length").iterator(); - InputStream body = req.body(); - long length = (long) body.available(); - if (hdr.hasNext()) { - length = Long.parseLong(hdr.next()); - } - body = new CapInputStream(body, length); - return body; + .header(CONTENT_LENGTH).iterator(); + if(hdr.hasNext()) { + String value = hdr.next(); + try { + return new CapInputStream(req.body(), Long.parseLong(value)); + } catch(NumberFormatException e) { + throw new IOException(String.format("Invalid %s header: %s", CONTENT_LENGTH, value)); + } + } + return req.body(); } } diff --git a/src/test/java/org/takes/rq/RqLengthAwareTest.java b/src/test/java/org/takes/rq/RqLengthAwareTest.java index 4fe943c8f..34c600ce3 100644 --- a/src/test/java/org/takes/rq/RqLengthAwareTest.java +++ b/src/test/java/org/takes/rq/RqLengthAwareTest.java @@ -23,9 +23,12 @@ */ package org.takes.rq; +import java.io.ByteArrayInputStream; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; + import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; @@ -95,6 +98,34 @@ void readsByte() throws IOException { Matchers.equalTo(data.length() - 1) ); } + + @Test + void noContentLength() throws IOException { + byte[] bytes = "test".getBytes(); + final InputStream data = new FilterInputStream(new ByteArrayInputStream(bytes)) { + @Override + public int available() throws IOException { + //This simulates a stream where only one byte is available at once + return 1; + } + }; + final InputStream stream = new RqLengthAware( + new RqFake( + Arrays.asList( + "GET /test1", + "Host: b.example.com" + //We have no content length header so RqLengthAware should simply do nothing + ), + data + ) + ).body(); + for(int i = 0; i < bytes.length; i++) { + MatcherAssert.assertThat( + stream.read(), + Matchers.equalTo(bytes[i] & 0xFF) + ); + } + } @Test void readsByteArray() throws IOException {