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 f7c14faad Expose additional transport details to TransportListener
(#1762)
f7c14faad is described below
commit f7c14faad03149edf7b5bd73d16ff1561f60dfcc
Author: Konrad Windszus <[email protected]>
AuthorDate: Thu Jul 16 17:34:42 2026 +0200
Expose additional transport details to TransportListener (#1762)
For HTTP transporters this is:
- HTTP Version
- SSL Protocol (only HTTPS)
- SSL Cipher Suite (only HTTPS)
- Compression Algorithm (if used and supported by HTTP Client impl)
This closes #1761
* Use dedicated method in TransportListener for transport properties
Expose transport properties for all tasks (Get, Peek, Put).
Reliably capture SSL context from ApacheTransporter with a dedicated
request executor.
* Fix PR review issues in HttpTransportPropertiesBuilder
Remove unused key "NUM_BYTES_TRANSFERRED"
---
.../aether/transfer/HttpTransportProperty.java | 125 +++++++++++++++++++++
.../org/eclipse/aether/transfer/TransferEvent.java | 39 +++++++
.../connector/basic/TransferTransportListener.java | 13 +++
.../aether/spi/connector/transport/PeekTask.java | 14 ++-
.../spi/connector/transport/TransportListener.java | 17 +++
.../http/HttpTransportPropertiesBuilder.java | 64 +++++++++++
.../TransportListenerNotifyingInputStreamTest.java | 5 +-
.../test/util/http/HttpTransporterTest.java | 27 ++++-
.../test/util/http/RecordingTransportListener.java | 15 +++
.../aether/transport/apache/ApacheTransporter.java | 75 ++++++++++++-
.../transport/apache/ApacheTransporterTest.java | 5 +
.../aether/transport/jdk/JdkTransporter.java | 29 ++++-
.../aether/transport/jdk/JdkTransporterTest.java | 6 +
.../aether/transport/jetty/JettyTransporter.java | 60 ++++++++++
14 files changed, 485 insertions(+), 9 deletions(-)
diff --git
a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/HttpTransportProperty.java
b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/HttpTransportProperty.java
new file mode 100644
index 000000000..b66af4c0f
--- /dev/null
+++
b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/HttpTransportProperty.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package org.eclipse.aether.transfer;
+
+/**
+ * Defines transport property keys specific to HTTP transporters.
+ * These keys can be used to expose additional information about the HTTP
transport operation.
+ * Additionally it defines types for the values of these keys, such as {@link
HttpVersion} and {@link SslProtocol}.
+ * @see TransferEvent#getTransportProperties()
+ * @since NEXT
+ */
+public final class HttpTransportProperty {
+
+ private HttpTransportProperty() {
+ // Private constructor to prevent instantiation
+ }
+
+ /**
+ * Transport property keys specific to HTTP transporters.
+ * @see TransferEvent#getTransportProperties()
+ */
+ public enum Key implements TransferEvent.TransportPropertyKey {
+ /**
+ * Transport property key for HTTP version. Value is a {@link
HttpVersion} representing the HTTP version used.
+ */
+ HTTP_VERSION,
+ /**
+ * Transport property key for SSL protocol. Value is a {@link
SslProtocol} representing the SSL protocol used.
+ */
+ SSL_PROTOCOL,
+ /**
+ * Transport property key for SSL cipher suite. Value is a String
representing the SSL cipher suite used (e.g.,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256").
+ * @see <a
href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#jsse-cipher-suite-names">JSSE
Cipher Suite Names</a>.
+ */
+ SSL_CIPHER_SUITE,
+ /**
+ * Transport property key for content coding (usually compression).
Value is a String representing the compression algorithm used (e.g., "gzip",
"br", or "zstd")
+ * @see <a
href="https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding">Content
Coding Values</a>
+ */
+ CONTENT_CODING,
+ }
+
+ /**
+ * HTTP version used for the HTTP transport.
+ */
+ public enum HttpVersion {
+ HTTP_1_0("HTTP/1.0"),
+ HTTP_1_1("HTTP/1.1"),
+ HTTP_2("HTTP/2"),
+ HTTP_3("HTTP/3");
+
+ private final String label;
+
+ HttpVersion(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+ }
+
+ /**
+ * SSL protocol including version used for the HTTP transport.
+ */
+ public enum SslProtocol {
+ SSL_3_0("SSLv3"),
+ TLS_1_0("TLSv1"),
+ TLS_1_1("TLSv1.1"),
+ TLS_1_2("TLSv1.2"),
+ TLS_1_3("TLSv1.3");
+
+ private final String label;
+
+ SslProtocol(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ /**
+ * Converts a standard SSL protocol name to the corresponding
SslProtocol enum value.
+ *
+ * @param name the standard algorithm name of the SSL protocol (e.g.,
"TLSv1.2", "TLSv1.3")
+ * @return the corresponding SslProtocol enum value
+ * @see <a
href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#sslcontext-algorithms">Standard
Names for SSLContext Algorithms</a>
+ */
+ public static SslProtocol fromStandardName(String name) {
+ switch (name) {
+ case "SSLv3":
+ return SSL_3_0;
+ case "TLSv1":
+ return TLS_1_0;
+ case "TLSv1.1":
+ return TLS_1_1;
+ case "TLSv1.2":
+ return TLS_1_2;
+ case "TLSv1.3":
+ return TLS_1_3;
+ default:
+ throw new IllegalArgumentException("Unknown SSL protocol:
" + name);
+ }
+ }
+ }
+}
diff --git
a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferEvent.java
b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferEvent.java
index 409770864..7a6d15d7c 100644
---
a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferEvent.java
+++
b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferEvent.java
@@ -19,6 +19,8 @@
package org.eclipse.aether.transfer;
import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Map;
import org.eclipse.aether.RepositorySystemSession;
@@ -103,6 +105,8 @@ public final class TransferEvent {
private final Exception exception;
+ private final Map<TransportPropertyKey, Object> transportProperties;
+
TransferEvent(Builder builder) {
type = builder.type;
requestType = builder.requestType;
@@ -111,6 +115,7 @@ public final class TransferEvent {
dataBuffer = builder.dataBuffer;
transferredBytes = builder.transferredBytes;
exception = builder.exception;
+ transportProperties = builder.transportProperties;
}
/**
@@ -196,6 +201,23 @@ public final class TransferEvent {
return exception;
}
+ /**
+ * Get the transport properties associated with this transfer.
+ * The keys are transporter specific and the value types are key specific.
+ * This is only potentially not empty for the following events:
+ * <ul>
+ * <li>{@link EventType#CORRUPTED}</li>
+ * <li>{@link EventType#FAILED}</li>
+ * <li>{@link EventType#SUCCEEDED}</li>
+ * </ul>
+ * @return The immutable transport properties associated with this
transfer, may be empty.
+ * @since NEXT
+ * @see HttpTransportProperty.Key HttpTransportProperty.Key for HTTP
specific keys
+ */
+ public Map<TransportPropertyKey, Object> getTransportProperties() {
+ return transportProperties;
+ }
+
@Override
public String toString() {
return getRequestType() + " " + getType() + " " + getResource();
@@ -220,6 +242,8 @@ public final class TransferEvent {
Exception exception;
+ Map<TransportPropertyKey, Object> transportProperties;
+
/**
* Creates a new transfer event builder for the specified session and
the given resource.
*
@@ -231,6 +255,7 @@ public final class TransferEvent {
this.resource = requireNonNull(resource, "transfer resource cannot
be null");
type = EventType.INITIATED;
requestType = RequestType.GET;
+ transportProperties = Collections.emptyMap();
}
private Builder(Builder prototype) {
@@ -241,6 +266,7 @@ public final class TransferEvent {
dataBuffer = prototype.dataBuffer;
transferredBytes = prototype.transferredBytes;
exception = prototype.exception;
+ transportProperties = prototype.transportProperties;
}
/**
@@ -367,6 +393,17 @@ public final class TransferEvent {
return this;
}
+ /**
+ * Sets the transport properties associated with this transfer. The
keys are transporter specific and the value types are key specific.
+ * @param transportProperties The transport properties used in the
underlying transfer, must not be {@code null}.
+ * @return This event builder for chaining, never {@code null}.
+ */
+ public Builder setTransportProperties(Map<TransportPropertyKey,
Object> transportProperties) {
+ requireNonNull(transportProperties, "transportProperties cannot be
null");
+ this.transportProperties =
Collections.unmodifiableMap(transportProperties);
+ return this;
+ }
+
/**
* Builds a new transfer event from the current values of this
builder. The state of the builder itself remains
* unchanged.
@@ -377,4 +414,6 @@ public final class TransferEvent {
return new TransferEvent(this);
}
}
+
+ public interface TransportPropertyKey {}
}
diff --git
a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/TransferTransportListener.java
b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/TransferTransportListener.java
index fe5787960..e089153da 100644
---
a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/TransferTransportListener.java
+++
b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/TransferTransportListener.java
@@ -27,6 +27,7 @@ import
org.eclipse.aether.spi.connector.transport.TransportListener;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferEvent.EventType;
+import org.eclipse.aether.transfer.TransferEvent.TransportPropertyKey;
import org.eclipse.aether.transfer.TransferListener;
class TransferTransportListener<T extends Transfer> extends TransportListener {
@@ -39,10 +40,13 @@ class TransferTransportListener<T extends Transfer> extends
TransportListener {
private ChecksumCalculator checksumCalculator;
+ private Map<TransportPropertyKey, Object> transportProperties;
+
protected TransferTransportListener(T transfer, TransferEvent.Builder
eventBuilder) {
this.transfer = transfer;
this.listener = transfer.getListener();
this.eventBuilder = eventBuilder;
+ this.transportProperties = Collections.emptyMap();
}
protected T getTransfer() {
@@ -83,9 +87,16 @@ class TransferTransportListener<T extends Transfer> extends
TransportListener {
}
}
+ @Override
+ public void transportPropertiesAvailable(Map<TransportPropertyKey, Object>
transportProperties)
+ throws TransferCancelledException {
+ this.transportProperties = transportProperties;
+ }
+
public void transferCorrupted(Exception exception) throws
TransferCancelledException {
if (listener != null) {
eventBuilder.resetType(EventType.CORRUPTED).setException(exception);
+ eventBuilder.setTransportProperties(transportProperties);
listener.transferCorrupted(eventBuilder.build());
}
}
@@ -93,6 +104,7 @@ class TransferTransportListener<T extends Transfer> extends
TransportListener {
public void transferFailed(Exception exception, int classification) {
if (listener != null) {
eventBuilder.resetType(EventType.FAILED).setException(exception);
+ eventBuilder.setTransportProperties(transportProperties);
listener.transferFailed(eventBuilder.build());
}
}
@@ -100,6 +112,7 @@ class TransferTransportListener<T extends Transfer> extends
TransportListener {
public void transferSucceeded() {
if (listener != null) {
eventBuilder.resetType(EventType.SUCCEEDED);
+ eventBuilder.setTransportProperties(transportProperties);
listener.transferSucceeded(eventBuilder.build());
}
}
diff --git
a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PeekTask.java
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PeekTask.java
index 00edc9f17..f94887446 100644
---
a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PeekTask.java
+++
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/PeekTask.java
@@ -19,10 +19,11 @@
package org.eclipse.aether.spi.connector.transport;
import java.net.URI;
+import java.util.Map;
/**
* A task to check the existence of a resource in the remote repository.
<em>Note:</em> The listener returned from
- * {@link #getListener()} is always a noop given that none of its event
methods are relevant in context of this task.
+ * {@link #getListener()} is only used in the context of {@link
TransportListener#transportPropertiesAvailable(Map)}.
*
* @see Transporter#peek(PeekTask)
*/
@@ -41,4 +42,15 @@ public final class PeekTask extends TransportTask {
public String toString() {
return "?? " + getLocation();
}
+
+ /**
+ * Sets the listener that is to be notified during the transfer.
+ *
+ * @param listener The listener to notify of progress, may be {@code null}.
+ * @return This task for chaining, never {@code null}.
+ */
+ public PeekTask setListener(TransportListener listener) {
+ super.setListener(listener);
+ return this;
+ }
}
diff --git
a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/TransportListener.java
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/TransportListener.java
index b6e60645a..4e85ba246 100644
---
a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/TransportListener.java
+++
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/TransportListener.java
@@ -19,8 +19,10 @@
package org.eclipse.aether.spi.connector.transport;
import java.nio.ByteBuffer;
+import java.util.Map;
import org.eclipse.aether.transfer.TransferCancelledException;
+import org.eclipse.aether.transfer.TransferEvent;
/**
* A skeleton class for listeners used to monitor transport operations.
Reusing common regular expression syntax, the
@@ -57,4 +59,19 @@ public abstract class TransportListener {
* @throws TransferCancelledException If the transfer should be aborted.
*/
public void transportProgressed(ByteBuffer data) throws
TransferCancelledException {}
+
+ /**
+ * Notifies the listener about the transport properties that are available
for this transfer.
+ * This method is called either before or after other notifications are
sent:
+ * <ul>
+ * <li>for remote put tasks this is called after {@link
#transportStarted(long, long)}</li>
+ * <li>for remote peek tasks this is is the only event that is called</li>
+ * <li>for all other remote tasks this is called before {@link
#transportStarted(long, long)}</li>
+ * </ul>
+ * @param transportProperties The transport properties associated with
this transfer, may be empty. The keys are transporter specific and the value
types are key specific.
+ * @throws TransferCancelledException If the transfer should be aborted.
+ * @since NEXT
+ */
+ public void
transportPropertiesAvailable(Map<TransferEvent.TransportPropertyKey, Object>
transportProperties)
+ throws TransferCancelledException {}
}
diff --git
a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/http/HttpTransportPropertiesBuilder.java
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/http/HttpTransportPropertiesBuilder.java
new file mode 100644
index 000000000..b633fc0c4
--- /dev/null
+++
b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/http/HttpTransportPropertiesBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+package org.eclipse.aether.spi.connector.transport.http;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.aether.spi.connector.transport.TransportListener;
+import org.eclipse.aether.transfer.HttpTransportProperty.HttpVersion;
+import org.eclipse.aether.transfer.HttpTransportProperty.Key;
+import org.eclipse.aether.transfer.HttpTransportProperty.SslProtocol;
+import org.eclipse.aether.transfer.TransferEvent;
+
+/**
+ * Builder for HTTP transport properties used in {@link
TransportListener#transportPropertiesAvailable(Map)}.
+ * @since NEXT
+ */
+public final class HttpTransportPropertiesBuilder {
+ private final Map<TransferEvent.TransportPropertyKey, Object> properties =
new HashMap<>();
+
+ public HttpTransportPropertiesBuilder(HttpVersion version) {
+ this.properties.put(Key.HTTP_VERSION, version);
+ }
+
+ public HttpTransportPropertiesBuilder withSslProtocol(String name) {
+ return withSslProtocol(SslProtocol.fromStandardName(name));
+ }
+
+ public HttpTransportPropertiesBuilder withSslProtocol(SslProtocol
sslProtocol) {
+ this.properties.put(Key.SSL_PROTOCOL, sslProtocol);
+ return this;
+ }
+
+ public HttpTransportPropertiesBuilder withSslCipherSuite(String
cipherSuite) {
+ this.properties.put(Key.SSL_CIPHER_SUITE, cipherSuite);
+ return this;
+ }
+
+ public HttpTransportPropertiesBuilder withContentCoding(String
contentCoding) {
+ this.properties.put(Key.CONTENT_CODING, contentCoding);
+ return this;
+ }
+
+ public Map<TransferEvent.TransportPropertyKey, Object> build() {
+ return Collections.unmodifiableMap(new HashMap<>(properties));
+ }
+}
diff --git
a/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/TransportListenerNotifyingInputStreamTest.java
b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/TransportListenerNotifyingInputStreamTest.java
index 3e6fdac5c..0362f82c8 100644
---
a/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/TransportListenerNotifyingInputStreamTest.java
+++
b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/TransportListenerNotifyingInputStreamTest.java
@@ -26,7 +26,10 @@ import java.nio.ByteBuffer;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Tests for {@link TransportListenerNotifyingInputStream}.
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 c72964c4d..546a3ac9a 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
@@ -65,6 +65,7 @@ import
org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
import
org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
import org.eclipse.aether.spi.connector.transport.http.HttpTransporterFactory;
import
org.eclipse.aether.spi.connector.transport.http.RFC9457.HttpRFC9457Exception;
+import org.eclipse.aether.transfer.HttpTransportProperty;
import org.eclipse.aether.transfer.NoTransporterException;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
@@ -509,7 +510,12 @@ public abstract class HttpTransporterTest {
protected void testPeek_SSL() throws Exception {
httpServer.addHttp2ConnectorWithMutualTLS();
newTransporter(httpServer.getHttpsUrl());
- transporter.peek(new PeekTask(URI.create("repo/file.txt")));
+ RecordingTransportListener listener = new RecordingTransportListener();
+ PeekTask task = new
PeekTask(URI.create("repo/file.txt")).setListener(listener);
+ transporter.peek(task);
+ assertEquals(
+ HttpTransportProperty.SslProtocol.TLS_1_3,
+
listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_PROTOCOL));
}
@Test
@@ -604,6 +610,10 @@ public abstract class HttpTransporterTest {
*/
protected abstract Stream<String> supportedCompressionAlgorithms();
+ protected boolean exposeContentCodingInTransportProperties() {
+ return true;
+ }
+
@ParameterizedTest
// DEFLATE isn't supported by Jetty server
(https://github.com/jetty/jetty.project/issues/280)
@ValueSource(strings = {"br", "gzip", "zstd"})
@@ -637,6 +647,9 @@ public abstract class HttpTransporterTest {
assertEquals(1, listener.getStartedCount());
assertTrue(listener.getProgressedCount() > 0, "Count: " +
listener.getProgressedCount());
assertEquals(task.getDataString(),
listener.getBaos().toString(StandardCharsets.UTF_8));
+ if (exposeContentCodingInTransportProperties()) {
+ assertEquals(encoding,
listener.getTransportProperties().get(HttpTransportProperty.Key.CONTENT_CODING));
+ }
}
@Test
@@ -772,6 +785,12 @@ public abstract class HttpTransporterTest {
assertEquals(1, listener.getStartedCount());
assertTrue(listener.getProgressedCount() > 0, "Count: " +
listener.getProgressedCount());
assertEquals(task.getDataString(),
listener.getBaos().toString(StandardCharsets.UTF_8));
+ assertEquals(
+ HttpTransportProperty.SslProtocol.TLS_1_3,
+
listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_PROTOCOL));
+ assertEquals(
+ "TLS_AES_256_GCM_SHA384",
+
listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_CIPHER_SUITE));
}
@Test
@@ -931,6 +950,9 @@ public abstract class HttpTransporterTest {
assertEquals(1, listener.getStartedCount());
assertTrue(listener.getProgressedCount() > 0, "Count: " +
listener.getProgressedCount());
assertEquals(task.getDataString(),
listener.getBaos().toString(StandardCharsets.UTF_8));
+ assertEquals(
+ HttpTransportProperty.HttpVersion.HTTP_2,
+
listener.getTransportProperties().get(HttpTransportProperty.Key.HTTP_VERSION));
httpServer.getLogEntries().forEach(log -> {
assertEquals(HttpVersion.HTTP_2, log.getVersion());
});
@@ -1342,6 +1364,9 @@ public abstract class HttpTransporterTest {
assertEquals(supportsPreemptiveAuth() ? 1 : 2,
listener.getStartedCount());
assertTrue(listener.getProgressedCount() > 0, "Count: " +
listener.getProgressedCount());
assertEquals("upload", TestFileUtils.readString(new File(repoDir,
"file.txt")));
+ assertEquals(
+ HttpTransportProperty.SslProtocol.TLS_1_3,
+
listener.getTransportProperties().get(HttpTransportProperty.Key.SSL_PROTOCOL));
}
@Test
diff --git
a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/RecordingTransportListener.java
b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/RecordingTransportListener.java
index 7dac9b666..f7e050cd0 100644
---
a/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/RecordingTransportListener.java
+++
b/maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/RecordingTransportListener.java
@@ -21,9 +21,12 @@ package org.eclipse.aether.internal.test.util.http;
import java.io.ByteArrayOutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
+import java.util.Map;
import org.eclipse.aether.spi.connector.transport.TransportListener;
import org.eclipse.aether.transfer.TransferCancelledException;
+import org.eclipse.aether.transfer.TransferEvent;
+import org.eclipse.aether.transfer.TransferEvent.TransportPropertyKey;
public class RecordingTransportListener extends TransportListener {
@@ -41,6 +44,8 @@ public class RecordingTransportListener extends
TransportListener {
private boolean cancelProgress;
+ private Map<TransferEvent.TransportPropertyKey, Object>
transportProperties;
+
@Override
public void transportStarted(long dataOffset, long dataLength) throws
TransferCancelledException {
startedCount++;
@@ -69,6 +74,12 @@ public class RecordingTransportListener extends
TransportListener {
}
}
+ @Override
+ public void transportPropertiesAvailable(Map<TransportPropertyKey, Object>
transportProperties)
+ throws TransferCancelledException {
+ this.transportProperties = transportProperties;
+ }
+
public ByteArrayOutputStream getBaos() {
return baos;
}
@@ -104,4 +115,8 @@ public class RecordingTransportListener extends
TransportListener {
public void cancelProgress() {
this.cancelProgress = true;
}
+
+ public Map<TransferEvent.TransportPropertyKey, Object>
getTransportProperties() {
+ return transportProperties;
+ }
}
diff --git
a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
index 7a1f92905..defae1430 100644
---
a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
+++
b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
@@ -18,6 +18,8 @@
*/
package org.eclipse.aether.transport.apache;
+import javax.net.ssl.SSLSession;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
@@ -38,12 +40,16 @@ import java.util.function.Function;
import java.util.regex.Matcher;
import org.apache.http.Header;
+import org.apache.http.HttpClientConnection;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
@@ -66,6 +72,7 @@ import org.apache.http.client.utils.URIUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
+import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.NoConnectionReuseStrategy;
@@ -81,6 +88,8 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.util.EntityUtils;
import org.eclipse.aether.Keys;
import org.eclipse.aether.RepositorySystemSession;
@@ -91,13 +100,17 @@ import
org.eclipse.aether.spi.connector.transport.AbstractTransporter;
import org.eclipse.aether.spi.connector.transport.GetTask;
import org.eclipse.aether.spi.connector.transport.PeekTask;
import org.eclipse.aether.spi.connector.transport.PutTask;
+import org.eclipse.aether.spi.connector.transport.TransportListener;
import org.eclipse.aether.spi.connector.transport.TransportTask;
import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
+import
org.eclipse.aether.spi.connector.transport.http.HttpTransportPropertiesBuilder;
import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
import
org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
import org.eclipse.aether.spi.io.PathProcessor;
+import org.eclipse.aether.transfer.HttpTransportProperty.HttpVersion;
import org.eclipse.aether.transfer.NoTransporterException;
import org.eclipse.aether.transfer.TransferCancelledException;
+import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.StringDigestUtil;
import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
@@ -122,6 +135,11 @@ import static
org.eclipse.aether.transport.apache.ApacheTransporterConfiguration
* A transporter for HTTP/HTTPS.
*/
final class ApacheTransporter extends AbstractTransporter implements
HttpTransporter {
+ /**
+ * Custom context attribute name to store the SSL session in the HTTP
context. This is populated by a custom request executor.
+ */
+ private static final String CONTEXT_ATTRIBUTE_NAME_SSL_SESSION =
"ssl.session";
+
private static final Logger LOGGER =
LoggerFactory.getLogger(ApacheTransporter.class);
private final ChecksumExtractor checksumExtractor;
@@ -283,6 +301,20 @@ final class ApacheTransporter extends AbstractTransporter
implements HttpTranspo
builder.useSystemProperties();
}
+ // capture SSL session for logging purposes
(https://issues.apache.org/jira/browse/HTTPCLIENT-2164)
+ builder.setRequestExecutor(new HttpRequestExecutor() {
+
+ @Override
+ public HttpResponse execute(HttpRequest request,
HttpClientConnection conn, HttpContext context)
+ throws IOException, HttpException {
+ if (conn instanceof ManagedHttpClientConnection) {
+ context.setAttribute(
+ CONTEXT_ATTRIBUTE_NAME_SSL_SESSION,
((ManagedHttpClientConnection) conn).getSSLSession());
+ }
+ return super.execute(request, conn, context);
+ }
+ });
+
HttpTransporterUtils.getHttpExpectContinue(session,
repository).ifPresent(state::setExpectContinue);
if (!HttpTransporterUtils.isHttpReuseConnections(session, repository))
{
builder.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE);
@@ -348,7 +380,7 @@ final class ApacheTransporter extends AbstractTransporter
implements HttpTranspo
protected void implPeek(PeekTask task) throws Exception {
HttpHead request = commonHeaders(new HttpHead(resolve(task)));
try {
- execute(request, null);
+ execute(request, null, task.getListener());
} catch (HttpResponseException e) {
throw new HttpTransporterException(e.getStatusCode());
}
@@ -368,7 +400,7 @@ final class ApacheTransporter extends AbstractTransporter
implements HttpTranspo
if (resume) {
resume(request, task);
}
- execute(request, getter);
+ execute(request, getter, task.getListener());
break;
} catch (HttpResponseException e) {
if (resume
@@ -391,25 +423,28 @@ final class ApacheTransporter extends AbstractTransporter
implements HttpTranspo
ApacheRFC9457Reporter.INSTANCE.prepareRequest(request);
}
try {
- execute(request, null);
+ execute(request, null, task.getListener());
} catch (HttpResponseException e) {
if (e.getStatusCode() == HttpStatus.SC_EXPECTATION_FAILED &&
request.containsHeader(HttpHeaders.EXPECT)) {
state.setExpectContinue(false);
request = commonHeaders(entity(new HttpPut(request.getURI()),
entity));
- execute(request, null);
+ execute(request, null, task.getListener());
return;
}
throw new HttpTransporterException(e.getStatusCode());
}
}
- private void execute(HttpUriRequest request, EntityGetter getter) throws
Exception {
+ private void execute(HttpUriRequest request, EntityGetter getter,
TransportListener listener) throws Exception {
try {
SharingHttpContext context = new SharingHttpContext(state);
context.setAuthCache(authCache);
prepare(request, context);
try (CloseableHttpResponse response = client.execute(server,
request, context)) {
try {
+ Map<TransferEvent.TransportPropertyKey, Object>
transportProperties =
+ createTransportProperties(response, context);
+ listener.transportPropertiesAvailable(transportProperties);
handleStatus(response);
if (getter != null) {
getter.handle(response);
@@ -628,6 +663,36 @@ final class ApacheTransporter extends AbstractTransporter
implements HttpTranspo
}
}
+ private static Map<TransferEvent.TransportPropertyKey, Object>
createTransportProperties(
+ CloseableHttpResponse response, HttpCoreContext context) {
+ HttpTransportPropertiesBuilder builder =
+ new
HttpTransportPropertiesBuilder(toHttpVersion(response.getProtocolVersion()));
+ SSLSession sslSession =
context.getAttribute(CONTEXT_ATTRIBUTE_NAME_SSL_SESSION, SSLSession.class);
+ if (sslSession != null) {
+ builder.withSslProtocol(sslSession.getProtocol());
+ builder.withSslCipherSuite(sslSession.getCipherSuite());
+ }
+ // content encoding is not available (see
https://issues.apache.org/jira/browse/HTTPCORE-792)
+ return builder.build();
+ }
+
+ static HttpVersion toHttpVersion(ProtocolVersion version) {
+ switch (version.getMajor()) {
+ case 1:
+ if (version.getMinor() == 0) {
+ return HttpVersion.HTTP_1_0;
+ } else {
+ return HttpVersion.HTTP_1_1;
+ }
+ case 2:
+ return HttpVersion.HTTP_2;
+ case 3:
+ return HttpVersion.HTTP_3;
+ default:
+ throw new IllegalArgumentException("Unknown version " +
version.toString());
+ }
+ }
+
private static Function<String, String> headerGetter(CloseableHttpResponse
closeableHttpResponse) {
return s -> {
Header header = closeableHttpResponse.getFirstHeader(s);
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 ec5965c12..bbd28856e 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
@@ -53,6 +53,11 @@ class ApacheTransporterTest extends HttpTransporterTest {
return Stream.of("gzip", "deflate");
}
+ protected boolean exposeContentCodingInTransportProperties() {
+ // see https://issues.apache.org/jira/browse/HTTPCORE-792
+ return false;
+ }
+
@Override
protected boolean supportsHttp3() {
return false;
diff --git
a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java
b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java
index c53f7fcf3..305693300 100644
---
a/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java
+++
b/maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java
@@ -83,11 +83,14 @@ import org.eclipse.aether.spi.connector.transport.PutTask;
import
org.eclipse.aether.spi.connector.transport.TransportListenerNotifyingInputStream;
import org.eclipse.aether.spi.connector.transport.TransportTask;
import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
+import
org.eclipse.aether.spi.connector.transport.http.HttpTransportPropertiesBuilder;
import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
import
org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
import org.eclipse.aether.spi.io.PathProcessor;
+import org.eclipse.aether.transfer.HttpTransportProperty;
import org.eclipse.aether.transfer.NoTransporterException;
import org.eclipse.aether.transfer.TransferCancelledException;
+import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
import org.slf4j.Logger;
@@ -245,6 +248,7 @@ final class JdkTransporter extends AbstractTransporter
implements HttpTransporte
prepare(request);
try {
HttpResponse<Void> response = send(request.build(),
HttpResponse.BodyHandlers.discarding());
+
task.getListener().transportPropertiesAvailable(createTransportProperties(response));
if (response.statusCode() >= MULTIPLE_CHOICES) {
throw new HttpTransporterException(response.statusCode());
}
@@ -280,6 +284,7 @@ final class JdkTransporter extends AbstractTransporter
implements HttpTransporte
prepare(request);
try {
response = send(request.build(),
HttpResponse.BodyHandlers.ofInputStream());
+
task.getListener().transportPropertiesAvailable(createTransportProperties(response));
if (response.statusCode() >= MULTIPLE_CHOICES) {
if (resume && response.statusCode() ==
PRECONDITION_FAILED) {
closeBody(response);
@@ -366,6 +371,27 @@ final class JdkTransporter extends AbstractTransporter
implements HttpTransporte
}
}
+ private Map<TransferEvent.TransportPropertyKey, Object>
createTransportProperties(HttpResponse<?> response) {
+ HttpTransportPropertiesBuilder builder = new
HttpTransportPropertiesBuilder(toHttpVersion(response.version()));
+ response.sslSession().ifPresent(ssl -> {
+ builder.withSslProtocol(ssl.getProtocol());
+ builder.withSslCipherSuite(ssl.getCipherSuite());
+ });
+ // TODO: add compression algorithm if any
(https://github.com/mizosoft/methanol/issues/182)
+ return builder.build();
+ }
+
+ static HttpTransportProperty.HttpVersion toHttpVersion(HttpClient.Version
version) {
+ switch (version) {
+ case HTTP_1_1:
+ return HttpTransportProperty.HttpVersion.HTTP_1_1;
+ case HTTP_2:
+ return HttpTransportProperty.HttpVersion.HTTP_2;
+ default:
+ throw new IllegalArgumentException("Unsupported HTTP version:
" + version);
+ }
+ }
+
private static Function<String, String> headerGetter(HttpResponse<?>
response) {
return s -> response.headers().firstValue(s).orElse(null);
}
@@ -389,13 +415,13 @@ final class JdkTransporter extends AbstractTransporter
implements HttpTransporte
if (sendRfc9457Accept) {
JdkRFC9457Reporter.INSTANCE.prepareRequest(request);
}
-
if (task.getDataLength() == 0L) {
request.PUT(HttpRequest.BodyPublishers.noBody());
} else {
request.PUT(HttpRequest.BodyPublishers.fromPublisher(
HttpRequest.BodyPublishers.ofInputStream(() -> {
try {
+ // transport properties are not available for
outgoing requests
return new TransportListenerNotifyingInputStream(
task.newInputStream(), task.getListener(),
task.getDataLength());
} catch (IOException e) {
@@ -408,6 +434,7 @@ final class JdkTransporter extends AbstractTransporter
implements HttpTransporte
prepare(request);
try {
HttpResponse<InputStream> response = send(request.build(),
HttpResponse.BodyHandlers.ofInputStream());
+
task.getListener().transportPropertiesAvailable(createTransportProperties(response));
if (response.statusCode() >= MULTIPLE_CHOICES) {
try {
JdkRFC9457Reporter.INSTANCE.generateException(response,
(statusCode, reasonPhrase) -> {
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 73f86fc90..ae645e66c 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
@@ -76,6 +76,12 @@ class JdkTransporterTest extends HttpTransporterTest {
return Stream.of("gzip", "deflate");
}
+ @Override
+ protected boolean exposeContentCodingInTransportProperties() {
+ // see https://issues.apache.org/jira/browse/HTTPCORE-792
+ return false;
+ }
+
@Override
@Disabled
@Test
diff --git
a/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java
b/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java
index a1b594c6d..9d57d5ea4 100644
---
a/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java
+++
b/maven-resolver-transport-jetty/src/main/java/org/eclipse/aether/transport/jetty/JettyTransporter.java
@@ -50,11 +50,14 @@ import org.eclipse.aether.spi.connector.transport.PeekTask;
import org.eclipse.aether.spi.connector.transport.PutTask;
import org.eclipse.aether.spi.connector.transport.TransportTask;
import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
+import
org.eclipse.aether.spi.connector.transport.http.HttpTransportPropertiesBuilder;
import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
import
org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
import org.eclipse.aether.spi.io.PathProcessor;
+import org.eclipse.aether.transfer.HttpTransportProperty;
import org.eclipse.aether.transfer.NoTransporterException;
import org.eclipse.aether.transfer.TransferCancelledException;
+import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
import org.eclipse.jetty.client.Authentication;
@@ -66,6 +69,7 @@ import org.eclipse.jetty.client.Request;
import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.client.transport.HttpClientConnectionFactory;
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
+import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http2.client.HTTP2Client;
import
org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
@@ -74,6 +78,7 @@ import
org.eclipse.jetty.http3.client.HTTP3ClientQuicConfiguration;
import
org.eclipse.jetty.http3.client.transport.ClientConnectionFactoryOverHTTP3;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
+import org.eclipse.jetty.io.EndPoint.SslSessionData;
import org.eclipse.jetty.quic.quiche.client.QuicheClientQuicConfiguration;
import org.eclipse.jetty.quic.quiche.client.QuicheTransport;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -188,7 +193,16 @@ final class JettyTransporter extends AbstractTransporter
implements HttpTranspor
if (preemptiveAuth) {
mayApplyPreemptiveAuth(request);
}
+ // capture raw response headers as described in
https://github.com/jetty/jetty.project/discussions/14404
+ Map<String, HttpField> rawResponseHeaders = new HashMap<>();
+ request.onResponseHeader((r, field) -> {
+ rawResponseHeaders.put(field.getLowerCaseName(), field);
+ return true; // continue processing
+ });
Response response = request.send();
+ Map<TransferEvent.TransportPropertyKey, Object> transportProperties =
+ createTransportProperties(request, rawResponseHeaders);
+ task.getListener().transportPropertiesAvailable(transportProperties);
if (response.getStatus() >= MULTIPLE_CHOICES) {
throw new HttpTransporterException(response.getStatus());
}
@@ -221,6 +235,12 @@ final class JettyTransporter extends AbstractTransporter
implements HttpTranspor
});
}
+ // capture raw response headers as described in
https://github.com/jetty/jetty.project/discussions/14404
+ Map<String, HttpField> rawResponseHeaders = new HashMap<>();
+ request.onResponseHeader((r, field) -> {
+ rawResponseHeaders.put(field.getLowerCaseName(), field);
+ return true; // continue processing
+ });
listener = new InputStreamResponseListener();
request.send(listener);
try {
@@ -233,6 +253,9 @@ final class JettyTransporter extends AbstractTransporter
implements HttpTranspor
throw new RuntimeException(t);
}
}
+ Map<TransferEvent.TransportPropertyKey, Object>
transportProperties =
+ createTransportProperties(request, rawResponseHeaders);
+
task.getListener().transportPropertiesAvailable(transportProperties);
if (response.getStatus() >= MULTIPLE_CHOICES) {
if (resume && response.getStatus() == PRECONDITION_FAILED) {
resume = false;
@@ -297,6 +320,36 @@ final class JettyTransporter extends AbstractTransporter
implements HttpTranspor
}
}
+ private Map<TransferEvent.TransportPropertyKey, Object>
createTransportProperties(
+ Request request, Map<String, HttpField> rawResponseHeaders) {
+ HttpTransportPropertiesBuilder builder =
+ new
HttpTransportPropertiesBuilder(toHttpVersion(request.getVersion()));
+ SslSessionData sslSessionData =
request.getConnection().getSslSessionData();
+ if (sslSessionData != null && sslSessionData.sslSession() != null) {
+ builder.withSslProtocol(sslSessionData.sslSession().getProtocol());
+
builder.withSslCipherSuite(sslSessionData.sslSession().getCipherSuite());
+ }
+ if (rawResponseHeaders.containsKey("content-encoding")) {
+
builder.withContentCoding(rawResponseHeaders.get("content-encoding").getValue());
+ }
+ return builder.build();
+ }
+
+ static HttpTransportProperty.HttpVersion
toHttpVersion(org.eclipse.jetty.http.HttpVersion version) {
+ switch (version) {
+ case HTTP_1_0:
+ return HttpTransportProperty.HttpVersion.HTTP_1_0;
+ case HTTP_1_1:
+ return HttpTransportProperty.HttpVersion.HTTP_1_1;
+ case HTTP_2:
+ return HttpTransportProperty.HttpVersion.HTTP_2;
+ case HTTP_3:
+ return HttpTransportProperty.HttpVersion.HTTP_3;
+ default:
+ throw new IllegalArgumentException("Unknown version " +
version.toString());
+ }
+ }
+
private static Function<String, String> headerGetter(Response response) {
return s -> response.getHeaders().get(s);
}
@@ -312,6 +365,12 @@ final class JettyTransporter extends AbstractTransporter
implements HttpTranspor
mayApplyPreemptiveAuth(request);
}
request.body(PutTaskRequestContent.from(task));
+ // capture raw response headers as described in
https://github.com/jetty/jetty.project/discussions/14404
+ Map<String, HttpField> rawResponseHeaders = new HashMap<>();
+ request.onResponseHeader((r, field) -> {
+ rawResponseHeaders.put(field.getLowerCaseName(), field);
+ return true; // continue processing
+ });
AtomicBoolean started = new AtomicBoolean(false);
Response response;
InputStreamResponseListener listener = new
InputStreamResponseListener();
@@ -344,6 +403,7 @@ final class JettyTransporter extends AbstractTransporter
implements HttpTranspor
})
.send(listener);
response = listener.get(requestTimeout, TimeUnit.MILLISECONDS);
+
task.getListener().transportPropertiesAvailable(createTransportProperties(request,
rawResponseHeaders));
} catch (ExecutionException e) {
Throwable t = e.getCause();
if (t instanceof IOException ioex) {