diff --git a/avaje-jex/src/main/java/io/avaje/jex/compression/CompressedOutputStream.java b/avaje-jex/src/main/java/io/avaje/jex/compression/CompressedOutputStream.java index 6f80934a..f323036f 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/compression/CompressedOutputStream.java +++ b/avaje-jex/src/main/java/io/avaje/jex/compression/CompressedOutputStream.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -39,7 +40,7 @@ private void decideCompression(int length) throws IOException { if (compressionAllowed && length >= minSizeForCompression) { Optional compressor; - compressor = findMatchingCompressor(ctx.header(Constants.ACCEPT_ENCODING)); + compressor = findMatchingCompressor(ctx.headerValues(Constants.ACCEPT_ENCODING)); if (compressor.isPresent()) { this.compressedStream = compressor.get().compress(originStream); ctx.header(Constants.CONTENT_ENCODING, compressor.get().encoding()); @@ -69,9 +70,15 @@ public void close() throws IOException { originStream.close(); } - private Optional findMatchingCompressor(String acceptedEncoding) { + private Optional findMatchingCompressor(List acceptedEncoding) { if (acceptedEncoding != null) { - return Arrays.stream(acceptedEncoding.split(",")) + // it seems jetty may handle multi-value headers differently + var stream = + acceptedEncoding.size() > 1 + ? acceptedEncoding.stream() + : Arrays.stream(acceptedEncoding.getFirst().split(",")); + + return stream .map(e -> e.trim().split(";")[0]) .map(e -> "*".equals(e) ? "gzip" : e.toLowerCase()) .map(compression::forType) diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/JdkContext.java b/avaje-jex/src/main/java/io/avaje/jex/core/JdkContext.java index 70fd667a..cf02ceb0 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/core/JdkContext.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/JdkContext.java @@ -221,8 +221,7 @@ public Map> formParamMap() { } private String header(Headers headers, String name) { - final List values = headers.get(name); - return values == null || values.isEmpty() ? null : values.getFirst(); + return headers.getFirst(name); } @Override @@ -230,6 +229,11 @@ public String header(String key) { return header(exchange.getRequestHeaders(), key); } + @Override + public List headerValues(String key) { + return exchange.getRequestHeaders().get(key); + } + @Override public Context header(String key, List value) { exchange.getResponseHeaders().put(key, value); @@ -261,10 +265,20 @@ public Context headerMap(Map> map) { } @Override - public Headers headers() { + public Headers requestHeaders() { return exchange.getRequestHeaders(); } + @Override + public Headers responseHeaders() { + return exchange.getResponseHeaders(); + } + + @Override + public List responseHeaderValues(String key) { + return exchange.getResponseHeaders().get(key); + } + @Override public String host() { return header(Constants.HOST); diff --git a/avaje-jex/src/main/java/io/avaje/jex/http/Context.java b/avaje-jex/src/main/java/io/avaje/jex/http/Context.java index 45b8aa5f..0b01619d 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/http/Context.java +++ b/avaje-jex/src/main/java/io/avaje/jex/http/Context.java @@ -90,7 +90,7 @@ default T bodyAsClass(Class beanType) { * @param beanType The bean type */ T bodyAsType(Type beanType); - + /** * Return the request body as bean using {@link #bodyAsInputStream()}. * @@ -99,7 +99,7 @@ default T bodyAsClass(Class beanType) { default T bodyStreamAsClass(Class beanType) { return bodyAsType(beanType); } - + /** * Return the request body as bean of the given type using {@link #bodyAsInputStream()}. * @@ -192,12 +192,19 @@ default String fullUrl() { } /** - * Return the request header. + * Return the request header value by name. * - * @param key The header key + * @param key The first value of the header */ String header(String key); + /** + * Return the request headers. + * + * @param key all values of the header key + */ + List headerValues(String key); + /** * Set the response header. * @@ -235,7 +242,14 @@ default String fullUrl() { * * @return the request headers */ - Headers headers(); + Headers requestHeaders(); + + /** + * Return underlying response headers. + * + * @return the response headers + */ + Headers responseHeaders(); /** Add the response headers using the provided map. */ default Context headers(Map headers) { @@ -439,6 +453,14 @@ default Context render(String name) { */ String responseHeader(String key); + /** + * Returns the value of the specified response header. + * + * @param key The name of the header. + * @return The value of the header, or null if not found. + */ + List responseHeaderValues(String key); + /** Return true if the response has been sent. */ boolean responseSent(); diff --git a/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java b/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java index 1f241201..2ee5612a 100644 --- a/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java +++ b/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java @@ -33,10 +33,7 @@ static TestPair init() { "/sus", ctx -> ctx.write( - CompressionTest.class.getResourceAsStream("/public/sus.txt"))) - .get( - "/forced", - ctx -> ctx.header(Constants.CONTENT_ENCODING, "gzip").text("hi"))); + CompressionTest.class.getResourceAsStream("/public/sus.txt")))); return TestPair.create(app); } @@ -72,12 +69,4 @@ void testNoCompression() { assertThat(res.statusCode()).isEqualTo(200); assertThat(res.headers().firstValue(Constants.CONTENT_ENCODING)).isEmpty(); } - - @Test - void testForcedCompression() { - HttpResponse res = - pair.request().header(Constants.ACCEPT_ENCODING, "gzip").path("forced").GET().asString(); - assertThat(res.statusCode()).isEqualTo(200); - assertThat(res.headers().firstValue(Constants.CONTENT_ENCODING)).contains("gzip"); - } } diff --git a/avaje-jex/src/test/java/io/avaje/jex/core/TestPair.java b/avaje-jex/src/test/java/io/avaje/jex/core/TestPair.java index f9429ed2..2bab7988 100644 --- a/avaje-jex/src/test/java/io/avaje/jex/core/TestPair.java +++ b/avaje-jex/src/test/java/io/avaje/jex/core/TestPair.java @@ -1,6 +1,7 @@ package io.avaje.jex.core; import java.net.http.HttpClient.Version; +import java.time.Duration; import io.avaje.http.client.HttpClient; import io.avaje.http.client.HttpClientRequest; @@ -43,7 +44,12 @@ public static TestPair create(Jex app) { var jexServer = app.port(0).start(); var port = jexServer.port(); var url = "http://localhost:" + port; - var client = HttpClient.builder().version(Version.HTTP_1_1).baseUrl(url).build(); + var client = + HttpClient.builder() + .version(Version.HTTP_1_1) + .requestTimeout(Duration.ofDays(1)) + .baseUrl(url) + .build(); return new TestPair(port, jexServer, client); }