This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/master by this push:
new 0426ed03b Support Brotli compression in JDK HTTP Client
0426ed03b is described below
commit 0426ed03bb3d4b6e96a7f4502b3e05cf263a99f1
Author: Konrad Windszus <[email protected]>
AuthorDate: Mon Jan 19 09:22:11 2026 +0100
Support Brotli compression in JDK HTTP Client
Enable Brotli/ZStandard in Jetty HTTP Client
Add tests for compressed responses.
This closes #1744
---
maven-resolver-test-http/pom.xml | 16 ++
.../aether/internal/test/util/http/HttpServer.java | 243 +++++++++++++++++----
.../test/util/http/HttpTransporterTest.java | 68 +++++-
.../src/main/resources/compressible-file.xml | 42 ++++
.../transport/apache/ApacheTransporterTest.java | 9 +-
.../maven-resolver-transport-jdk/pom.xml | 4 +
.../maven-resolver-transport-jdk11/pom.xml | 4 +
.../aether/transport/jdk/JdkTransporterTest.java | 6 +
maven-resolver-transport-jdk-parent/pom.xml | 5 +
maven-resolver-transport-jetty/pom.xml | 14 +-
.../transport/jetty/JettyTransporterTest.java | 7 +
11 files changed, 357 insertions(+), 61 deletions(-)
diff --git a/maven-resolver-test-http/pom.xml b/maven-resolver-test-http/pom.xml
index 9617490fa..647a15216 100644
--- a/maven-resolver-test-http/pom.xml
+++ b/maven-resolver-test-http/pom.xml
@@ -81,6 +81,18 @@
<groupId>org.eclipse.jetty.compression</groupId>
<artifactId>jetty-compression-server</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.compression</groupId>
+ <artifactId>jetty-compression-zstandard</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.compression</groupId>
+ <artifactId>jetty-compression-brotli</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.compression</groupId>
+ <artifactId>jetty-compression-gzip</artifactId>
+ </dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
@@ -98,6 +110,10 @@
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
diff --git
a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpServer.java
b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpServer.java
index 0ca20510c..ecffdd118 100644
---
a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpServer.java
+++
b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpServer.java
@@ -19,19 +19,19 @@
package org.eclipse.aether.internal.test.util.http;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.URI;
+import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -43,11 +43,19 @@ import
org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper;
import org.eclipse.aether.spi.connector.transport.http.RFC9457.RFC9457Payload;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.compression.server.CompressionConfig;
+import org.eclipse.jetty.compression.server.CompressionHandler;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.pathmap.MatchedResource;
+import org.eclipse.jetty.http.pathmap.PathMappings;
+import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
@@ -58,8 +66,8 @@ import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.Blocker;
import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -72,12 +80,16 @@ public class HttpServer {
private final String path;
- private final Map<String, String> headers;
+ private final Map<String, String> requestHeaders;
- public LogEntry(String method, String path, Map<String, String>
headers) {
+ private Map<String, String> responseHeaders;
+
+ CountDownLatch responseHeadersAvailableSignal = new CountDownLatch(1);
+
+ public LogEntry(String method, String path, Map<String, String>
requestHeaders) {
this.method = method;
this.path = path;
- this.headers = headers;
+ this.requestHeaders = requestHeaders;
}
public String getMethod() {
@@ -88,8 +100,29 @@ public class HttpServer {
return path;
}
- public Map<String, String> getHeaders() {
- return headers;
+ public Map<String, String> getRequestHeaders() {
+ return requestHeaders;
+ }
+
+ /**
+ * This method blocks until the response headers are available.
+ * @return the response headers
+ */
+ public Map<String, String> getResponseHeaders() {
+ try {
+ if (!responseHeadersAvailableSignal.await(30,
java.util.concurrent.TimeUnit.SECONDS)) {
+ throw new IllegalStateException("Timeout waiting for
response headers to be available");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Interrupted while waiting for
response headers to be available", e);
+ }
+ return responseHeaders;
+ }
+
+ public void setResponseHeaders(Map<String, String> responseHeaders) {
+ this.responseHeaders = responseHeaders;
+ responseHeadersAvailableSignal.countDown();
}
@Override
@@ -290,15 +323,15 @@ public class HttpServer {
server = new Server();
httpConnector = new ServerConnector(server);
server.addConnector(httpConnector);
- server.setHandler(new Handler.Sequence(
+
+ server.setHandler(new LogHandler(new CompressionEnforcingHandler(new
Handler.Sequence(
new ConnectionClosingHandler(),
new ServerErrorHandler(),
- new LogHandler(),
new ProxyAuthHandler(),
new AuthHandler(),
new RedirectHandler(),
new RepoHandler(),
- new RFC9457Handler()));
+ new RFC9457Handler()))));
server.start();
return this;
@@ -313,6 +346,111 @@ public class HttpServer {
}
}
+ private class CompressionEnforcingHandler extends CompressionHandler {
+ // duplicate of CompressionHandler.pathConfigs which is private
+ private final PathMappings<CompressionConfig> pathConfigs = new
PathMappings<>();
+
+ CompressionEnforcingHandler(Handler handler) {
+ super(handler);
+ this.putConfiguration(
+ "/br/*",
+
CompressionConfig.builder().compressIncludeEncoding("br").build());
+ this.putConfiguration(
+ "/zstd/*",
+
CompressionConfig.builder().compressIncludeEncoding("zstd").build());
+ this.putConfiguration(
+ "/gzip/*",
+
CompressionConfig.builder().compressIncludeEncoding("gzip").build());
+ this.putConfiguration(
+ "/deflate/*",
+ CompressionConfig.builder()
+ .compressIncludeEncoding("deflate")
+ .build());
+ }
+
+ @Override
+ public CompressionConfig putConfiguration(PathSpec pathSpec,
CompressionConfig config) {
+ // deliberately not set it in the super class yet
+ return pathConfigs.put(pathSpec, config);
+ }
+
+ @Override
+ public boolean handle(Request request, Response response, Callback
callback) throws Exception {
+ Handler next = getHandler();
+ if (next == null) {
+ return false;
+ }
+ String pathInContext = Request.getPathInContext(request);
+ MatchedResource<CompressionConfig> matchedConfig =
this.pathConfigs.getMatched(pathInContext);
+ if (matchedConfig == null) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("skipping compression: path {} has no
matching compression config", pathInContext);
+ }
+ // No configuration, skip
+ return next.handle(request, response, callback);
+ }
+
+ // set the matched config in the super class for further
processing, but for all paths
+ // no need to reset it later as this handler is not used among
multiple requests
+ super.putConfiguration(PathSpec.from("/*"),
matchedConfig.getResource());
+ // first path segment determines the encoding, remove it from the
request path for further processing
+ return super.handle(new
StripLeadingPathSegmentsRequestWrapper(request, 1), response, callback);
+ }
+ }
+
+ private static class StripLeadingPathSegmentsRequestWrapper extends
Request.Wrapper {
+ private final HttpURI modifiedURI;
+
+ StripLeadingPathSegmentsRequestWrapper(Request wrapped, int
segmentsToStrip) {
+ super(wrapped);
+ this.modifiedURI = stripPathSegments(wrapped.getHttpURI(),
segmentsToStrip);
+ }
+
+ private static HttpURI stripPathSegments(HttpURI originalURI, int
segmentsToStrip) {
+ if (segmentsToStrip <= 0) {
+ return originalURI;
+ }
+
+ String originalPath = originalURI.getPath();
+ if (originalPath == null || originalPath.isEmpty()) {
+ return originalURI;
+ }
+
+ // Split path into segments
+ String[] segments = originalPath.split("/");
+ StringBuilder newPath = new StringBuilder();
+
+ // Skip empty first segment (from leading /) and the specified
number of segments
+ int skipCount = 0;
+ for (int i = 0; i < segments.length; i++) {
+ if (segments[i].isEmpty() && i == 0) {
+ // Skip leading empty segment from leading /
+ continue;
+ }
+ if (skipCount < segmentsToStrip) {
+ skipCount++;
+ continue;
+ }
+ newPath.append("/").append(segments[i]);
+ }
+
+ // If we stripped everything, return root path
+ if (newPath.isEmpty()) {
+ newPath.append("/");
+ }
+
+ // Build new URI with modified path
+ return org.eclipse.jetty.http.HttpURI.build(originalURI)
+ .path(newPath.toString())
+ .asImmutable();
+ }
+
+ @Override
+ public HttpURI getHttpURI() {
+ return modifiedURI;
+ }
+ }
+
private class ConnectionClosingHandler extends Handler.Abstract {
@Override
@@ -330,29 +468,48 @@ public class HttpServer {
if (serverErrorsBeforeWorks.getAndDecrement() > 0) {
response.setStatus(serverErrorStatusCode);
writeResponseBodyMessage(request, response, "Oops, come back
later!");
- callback.succeeded();
return true;
}
return false;
}
}
- private class LogHandler extends Handler.Abstract {
+ private class LogHandler extends Handler.Wrapper {
+
+ LogHandler(Handler handler) {
+ super(handler);
+ }
+
@Override
- public boolean handle(Request req, Response response, Callback
callback) {
+ public boolean handle(Request req, Response response, Callback
callback) throws Exception {
+
LOGGER.info(
"{} {}{}",
req.getMethod(),
req.getHttpURI().getDecodedPath(),
req.getHttpURI().getQuery() != null ? "?" +
req.getHttpURI().getQuery() : "");
- Map<String, String> headers = new
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (HttpField header : req.getHeaders()) {
- headers.put(header.getName(),
header.getValueList().stream().collect(Collectors.joining(", ")));
+ Map<String, String> requestHeaders =
+ toUnmodifiableMap(req.getHeaders()); // capture request
headers before other handlers modify them
+ LogEntry logEntry = new LogEntry(req.getMethod(),
req.getHttpURI().getPathQuery(), requestHeaders);
+ logEntries.add(logEntry);
+ // prevent closing the response before logging (assume all writes
are synchronous for simplicity)
+ boolean result = super.handle(req, response, callback);
+ // capture response headers after other handlers modified them
+ // at this point in time the connection may have been already
closed (i.e. last chunk already sent)
+
logEntry.setResponseHeaders(toUnmodifiableMap(response.getHeaders()));
+ if (result) {
+ callback.succeeded();
}
- logEntries.add(new LogEntry(
- req.getMethod(), req.getHttpURI().getPathQuery(),
Collections.unmodifiableMap(headers)));
- return false;
+ return result;
+ }
+
+ Map<String, String> toUnmodifiableMap(HttpFields headers) {
+ Map<String, String> map = new
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ for (HttpField header : headers) {
+ map.put(header.getName(),
header.getValueList().stream().collect(Collectors.joining(", ")));
+ }
+ return Collections.unmodifiableMap(map);
}
}
@@ -370,7 +527,6 @@ public class HttpServer {
if (ExpectContinue.FAIL.equals(expectContinue) &&
req.getHeaders().get(HttpHeader.EXPECT) != null) {
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
writeResponseBodyMessage(req, response, "Expectation was set
to fail");
- callback.succeeded();
return true;
}
@@ -379,14 +535,12 @@ public class HttpServer {
if (!file.isFile() || path.endsWith("/")) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
writeResponseBodyMessage(req, response, "Not found");
- callback.succeeded();
return true;
}
long ifUnmodifiedSince =
req.getHeaders().getDateField(HttpHeader.IF_UNMODIFIED_SINCE);
if (ifUnmodifiedSince != -1L && file.lastModified() >
ifUnmodifiedSince) {
response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
writeResponseBodyMessage(req, response, "Precondition
failed");
- callback.succeeded();
return true;
}
long offset = 0L;
@@ -398,14 +552,12 @@ public class HttpServer {
if (offset >= file.length()) {
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
writeResponseBodyMessage(req, response, "Range not
satisfiable");
- callback.succeeded();
return true;
}
}
String encoding =
req.getHeaders().get(HttpHeader.ACCEPT_ENCODING);
if ((encoding != null && !"identity".equals(encoding)) ||
ifUnmodifiedSince == -1L) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- callback.succeeded();
return true;
}
}
@@ -428,27 +580,27 @@ public class HttpServer {
}
}
if (HttpMethod.HEAD.is(req.getMethod())) {
- callback.succeeded();
return true;
}
- try (FileInputStream is = new FileInputStream(file);
- OutputStream os = Response.asBufferedOutputStream(req,
response)) {
- if (offset > 0L) {
- long skipped = is.skip(offset);
- while (skipped < offset && is.read() >= 0) {
- skipped++;
- }
- }
- IO.copy(is, os);
+ Content.Source contentSource =
+ Content.Source.from(new ByteBufferPool.Sized(null),
file.toPath(), offset, -1);
+ try (Blocker.Callback fileReadCallback = Blocker.callback()) {
+ Content.copy(contentSource, response, fileReadCallback);
+ fileReadCallback.block();
}
} else if (HttpMethod.PUT.is(req.getMethod())) {
if (!webDav) {
file.getParentFile().mkdirs();
}
if (file.getParentFile().exists()) {
- try (InputStream is = Content.Source.asInputStream(req);
- FileOutputStream os = new FileOutputStream(file)) {
- IO.copy(is, os);
+ try (SeekableByteChannel channel = Files.newByteChannel(
+ file.toPath(),
+ StandardOpenOption.CREATE,
+ StandardOpenOption.WRITE,
+ StandardOpenOption.TRUNCATE_EXISTING);
+ Blocker.Callback fileWriteCallback =
Blocker.callback()) {
+ Content.copy(req, Content.Sink.from(channel),
fileWriteCallback);
+ fileWriteCallback.block();
} catch (IOException e) {
file.delete();
throw e;
@@ -474,14 +626,15 @@ public class HttpServer {
} else {
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
- callback.succeeded();
return true;
}
}
private void writeResponseBodyMessage(Request request, Response response,
String message) throws IOException {
- try (OutputStream outputStream =
Response.asBufferedOutputStream(request, response)) {
- outputStream.write(message.getBytes(StandardCharsets.UTF_8));
+ // write synchronously to avoid closing the response too early
+ try (Blocker.Callback callback = Blocker.callback()) {
+ Content.Sink.write(response, false, message, callback);
+ callback.block();
}
}
@@ -510,7 +663,6 @@ public class HttpServer {
}
writeResponseBodyMessage(req, response,
buildRFC9457Message(rfc9457Payload));
}
- callback.succeeded();
return true;
}
}
@@ -542,7 +694,6 @@ public class HttpServer {
location.append("/repo").append(path.substring(9));
Response.sendRedirect(
req, response, callback,
HttpServletResponse.SC_MOVED_PERMANENTLY, location.toString(), false);
- callback.succeeded();
return true;
}
}
@@ -562,7 +713,6 @@ public class HttpServer {
}
response.getHeaders().add(HttpHeader.WWW_AUTHENTICATE, "Basic
realm=\"Test-Realm\"");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- callback.succeeded();
return true;
}
return false;
@@ -579,7 +729,6 @@ public class HttpServer {
}
response.getHeaders().add(HttpHeader.PROXY_AUTHENTICATE,
"basic realm=\"Test-Realm\"");
response.setStatus(HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED);
- callback.succeeded();
return true;
} else {
return false;
diff --git
a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java
b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java
index 6f8e65294..316657d43 100644
---
a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java
+++
b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/HttpTransporterTest.java
@@ -35,6 +35,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.DefaultRepositoryCache;
@@ -66,6 +67,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -74,12 +77,13 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
/**
* Common set of tests against Http transporter.
*/
@SuppressWarnings({"checkstyle:MethodName"})
-public class HttpTransporterTest {
+public abstract class HttpTransporterTest {
protected static final Path KEY_STORE_PATH = Paths.get("target/keystore");
@@ -192,6 +196,9 @@ public class HttpTransporterTest {
TestFileUtils.writeString(new File(repoDir, "dir/oldFile.txt"),
"oldTest", OLD_FILE_TIMESTAMP);
TestFileUtils.writeString(new File(repoDir, "empty.txt"), "");
TestFileUtils.writeString(new File(repoDir, "some space.txt"),
"space");
+ try (InputStream is = getCompressibleFileStream()) {
+ Files.copy(is, repoDir.toPath().resolve("compressible-file.xml"));
+ }
File resumable = new File(repoDir, "resume.txt");
TestFileUtils.writeString(resumable, "resumable");
resumable.setLastModified(System.currentTimeMillis() - 90 * 1000);
@@ -199,6 +206,10 @@ public class HttpTransporterTest {
newTransporter(httpServer.getHttpUrl());
}
+ private static InputStream getCompressibleFileStream() {
+ return
HttpTransporterTest.class.getClassLoader().getResourceAsStream("compressible-file.xml");
+ }
+
@AfterEach
protected void tearDown() throws Exception {
if (transporter != null) {
@@ -431,15 +442,48 @@ public class HttpTransporterTest {
assertEquals(OLD_FILE_TIMESTAMP, file.lastModified());
}
- @Test
- protected void testGet_CompressionUsedWithPom() throws Exception {
- File file = TestFileUtils.createTempFile("pom");
- GetTask task = new
GetTask(URI.create("repo/artifact.pom")).setDataPath(file.toPath());
+ /**
+ * Provides compression algorithms supported by the transporter
implementation.
+ * This should be the string value passed in the {@code Accept-Encoding}
header.
+ *
+ * @return stream of supported compression algorithm names
+ * @see <a
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding#directives">Accept-Encoding
directives</a>
+ */
+ protected abstract Stream<String> supportedCompressionAlgorithms();
+
+ @ParameterizedTest
+ // DEFLATE isn't supported by Jetty server
(https://github.com/jetty/jetty.project/issues/280)
+ @ValueSource(strings = {"br", "gzip", "zstd"})
+ protected void testGet_WithCompression(String encoding) throws Exception {
+ assumeTrue(
+ supportedCompressionAlgorithms().anyMatch(supported ->
supported.equals(encoding)),
+ () -> "Transporter does not support compression algorithm: " +
encoding);
+ RecordingTransportListener listener = new RecordingTransportListener();
+ // requires a file with at least 48/50 bytes (otherwise compression is
disabled,
+ //
https://github.com/jetty/jetty.project/blob/2264d3d9f9586f3e5e9040fba779ed72e931cb46/jetty-core/jetty-compression/jetty-compression-brotli/src/main/java/org/eclipse/jetty/compression/brotli/BrotliCompression.java#L61)
+ GetTask task = new GetTask(URI.create(encoding +
"/repo/compressible-file.xml")).setListener(listener);
transporter.get(task);
- String acceptEncoding =
httpServer.getLogEntries().get(0).getHeaders().get("Accept-Encoding");
+ String acceptEncoding =
+
httpServer.getLogEntries().get(0).getRequestHeaders().get("Accept-Encoding");
assertNotNull(acceptEncoding, "Missing Accept-Encoding header when
retrieving pom");
- // support either gzip or deflate as the transporter implementation
may vary
- assertTrue(acceptEncoding.contains("gzip") ||
acceptEncoding.contains("deflate"));
+ assertTrue(acceptEncoding.contains(encoding));
+ // check original response header sent by server (client transparently
handles compression and removes it)
+ // see https://issues.apache.org/jira/browse/HTTPCORE-792
+ // and https://github.com/mizosoft/methanol/issues/182
+ for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
+ assertEquals(encoding,
log.getResponseHeaders().get("Content-Encoding"));
+ }
+ String expectedResourceData;
+ try (InputStream is = getCompressibleFileStream()) {
+ expectedResourceData = new String(is.readAllBytes(),
StandardCharsets.UTF_8);
+ }
+ assertEquals(expectedResourceData, task.getDataString());
+ assertEquals(0L, listener.getDataOffset());
+ // data length is unknown as chunked transfer encoding is used with
compression
+ assertEquals(-1, listener.getDataLength());
+ assertEquals(1, listener.getStartedCount());
+ assertTrue(listener.getProgressedCount() > 0, "Count: " +
listener.getProgressedCount());
+ assertEquals(task.getDataString(),
listener.getBaos().toString(StandardCharsets.UTF_8));
}
@Test
@@ -1235,7 +1279,7 @@ public class HttpTransporterTest {
transporter.get(new GetTask(URI.create("repo/file.txt")));
assertEquals(1, httpServer.getLogEntries().size());
for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
- assertEquals("SomeTest/1.0", log.getHeaders().get("User-Agent"));
+ assertEquals("SomeTest/1.0",
log.getRequestHeaders().get("User-Agent"));
}
}
@@ -1251,7 +1295,7 @@ public class HttpTransporterTest {
assertEquals(1, httpServer.getLogEntries().size());
for (HttpServer.LogEntry log : httpServer.getLogEntries()) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
- assertEquals(entry.getValue(),
log.getHeaders().get(entry.getKey()), entry.getKey());
+ assertEquals(entry.getValue(),
log.getRequestHeaders().get(entry.getKey()), entry.getKey());
}
}
}
@@ -1321,8 +1365,8 @@ public class HttpTransporterTest {
transporter.get(task);
assertEquals("test", task.getDataString());
assertEquals(1, httpServer.getLogEntries().size());
-
assertNotNull(httpServer.getLogEntries().get(0).getHeaders().get("Authorization"));
-
assertNotNull(httpServer.getLogEntries().get(0).getHeaders().get("Proxy-Authorization"));
+
assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Authorization"));
+
assertNotNull(httpServer.getLogEntries().get(0).getRequestHeaders().get("Proxy-Authorization"));
}
@Test
diff --git a/maven-resolver-test-http/src/main/resources/compressible-file.xml
b/maven-resolver-test-http/src/main/resources/compressible-file.xml
new file mode 100644
index 000000000..706a316f5
--- /dev/null
+++ b/maven-resolver-test-http/src/main/resources/compressible-file.xml
@@ -0,0 +1,42 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ -->
+<metadata modelVersion="1.1.0">
+<groupId>org.apache.maven.plugins</groupId>
+<artifactId>maven-jar-plugin</artifactId>
+<version>3.0.3-SNAPSHOT</version>
+<versioning>
+<snapshot>
+<timestamp>20171206.232134</timestamp>
+<buildNumber>2496</buildNumber>
+</snapshot>
+<lastUpdated>20171206232134</lastUpdated>
+<snapshotVersions>
+<snapshotVersion>
+<extension>jar</extension>
+<value>3.0.3-20171206.232134-2496</value>
+<updated>20171206232134</updated>
+</snapshotVersion>
+<snapshotVersion>
+<extension>pom</extension>
+<value>3.0.3-20171206.232134-2496</value>
+<updated>20171206232134</updated>
+</snapshotVersion>
+</snapshotVersions>
+</versioning>
+</metadata>
\ No newline at end of file
diff --git
a/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java
b/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java
index 027d605cd..d08168773 100644
---
a/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java
+++
b/maven-resolver-transport-apache/src/test/java/org/eclipse/aether/transport/apache/ApacheTransporterTest.java
@@ -21,6 +21,7 @@ package org.eclipse.aether.transport.apache;
import java.io.File;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.util.stream.Stream;
import org.apache.http.pool.ConnPoolControl;
import org.apache.http.pool.PoolStats;
@@ -47,6 +48,11 @@ class ApacheTransporterTest extends HttpTransporterTest {
super(() -> new ApacheTransporterFactory(standardChecksumExtractor(),
new PathProcessorSupport()));
}
+ @Override
+ protected Stream<String> supportedCompressionAlgorithms() {
+ return Stream.of("gzip", "deflate");
+ }
+
@Override
@Disabled
@Test
@@ -86,7 +92,8 @@ class ApacheTransporterTest extends HttpTransporterTest {
assertTrue(listener.getProgressedCount() > 0, "Count: " +
listener.getProgressedCount());
assertEquals("upload", TestFileUtils.readString(new File(repoDir,
"dir1/dir2/file.txt")));
- assertEquals(5, httpServer.getLogEntries().size());
+ assertEquals(
+ 5, httpServer.getLogEntries().size(), "Expected 5 requests but
got: " + httpServer.getLogEntries());
assertEquals("OPTIONS", httpServer.getLogEntries().get(0).getMethod());
assertEquals("MKCOL", httpServer.getLogEntries().get(1).getMethod());
assertEquals("/repo/dir1/dir2/",
httpServer.getLogEntries().get(1).getPath());
diff --git
a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk/pom.xml
b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk/pom.xml
index ca0d4061a..e5cad9189 100644
--- a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk/pom.xml
+++ b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk/pom.xml
@@ -58,6 +58,10 @@
<groupId>com.github.mizosoft.methanol</groupId>
<artifactId>methanol</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.github.mizosoft.methanol</groupId>
+ <artifactId>methanol-brotli</artifactId>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
diff --git
a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/pom.xml
b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/pom.xml
index 40ee490da..0e86cddfe 100644
--- a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/pom.xml
+++ b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/pom.xml
@@ -57,6 +57,10 @@
<groupId>com.github.mizosoft.methanol</groupId>
<artifactId>methanol</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.github.mizosoft.methanol</groupId>
+ <artifactId>methanol-brotli</artifactId>
+ </dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
diff --git
a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java
b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java
index c480e8f55..cf37e4c2d 100644
---
a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java
+++
b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/test/java/org/eclipse/aether/transport/jdk/JdkTransporterTest.java
@@ -20,6 +20,7 @@ package org.eclipse.aether.transport.jdk;
import java.net.ConnectException;
import java.net.URI;
+import java.util.stream.Stream;
import org.eclipse.aether.internal.impl.DefaultPathProcessor;
import org.eclipse.aether.internal.test.util.TestUtils;
@@ -42,6 +43,11 @@ import static org.junit.jupiter.api.Assertions.fail;
*/
class JdkTransporterTest extends HttpTransporterTest {
+ @Override
+ protected Stream<String> supportedCompressionAlgorithms() {
+ return Stream.of("gzip", "deflate", "br");
+ }
+
@Override
@Disabled
@Test
diff --git a/maven-resolver-transport-jdk-parent/pom.xml
b/maven-resolver-transport-jdk-parent/pom.xml
index 2122794b5..36e5f24a3 100644
--- a/maven-resolver-transport-jdk-parent/pom.xml
+++ b/maven-resolver-transport-jdk-parent/pom.xml
@@ -45,6 +45,11 @@
<artifactId>methanol</artifactId>
<version>1.9.0</version>
</dependency>
+ <dependency>
+ <groupId>com.github.mizosoft.methanol</groupId>
+ <artifactId>methanol-brotli</artifactId>
+ <version>1.9.0</version>
+ </dependency>
</dependencies>
</dependencyManagement>
</project>
diff --git a/maven-resolver-transport-jetty/pom.xml
b/maven-resolver-transport-jetty/pom.xml
index c398e2901..2413c0c2b 100644
--- a/maven-resolver-transport-jetty/pom.xml
+++ b/maven-resolver-transport-jetty/pom.xml
@@ -72,7 +72,19 @@
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client-transport</artifactId>
</dependency>
-
+ <!-- supported compressions -->
+ <dependency>
+ <groupId>org.eclipse.jetty.compression</groupId>
+ <artifactId>jetty-compression-gzip</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.compression</groupId>
+ <artifactId>jetty-compression-brotli</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.compression</groupId>
+ <artifactId>jetty-compression-zstandard</artifactId>
+ </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
diff --git
a/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java
b/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java
index 94bfaa92e..4917ad734 100644
---
a/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java
+++
b/maven-resolver-transport-jetty/src/test/java/org/eclipse/aether/transport/jetty/JettyTransporterTest.java
@@ -18,6 +18,8 @@
*/
package org.eclipse.aether.transport.jetty;
+import java.util.stream.Stream;
+
import org.eclipse.aether.internal.test.util.http.HttpTransporterTest;
import org.eclipse.aether.spi.io.PathProcessorSupport;
import org.junit.jupiter.api.Disabled;
@@ -28,6 +30,11 @@ import org.junit.jupiter.api.Test;
*/
class JettyTransporterTest extends HttpTransporterTest {
+ @Override
+ protected Stream<String> supportedCompressionAlgorithms() {
+ return Stream.of("gzip", "deflate", "br", "zstd");
+ }
+
@Override
@Disabled
@Test