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) {

Reply via email to