Skip to content

Commit f4e7be9

Browse files
committed
Expose additional transport details to TransportListener
For HTTP Transporters this is: - HTTP Version - SSL Protocol (only HTTPS) - SSL Cipher Suite (only HTTPS) - Compression Algorithm (if used) - Transport Size (if it differs from data size) This closes #1761
1 parent bca6186 commit f4e7be9

File tree

7 files changed

+199
-13
lines changed

7 files changed

+199
-13
lines changed

maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.io.InputStream;
2323
import java.io.OutputStream;
2424
import java.nio.ByteBuffer;
25+
import java.util.Collections;
26+
import java.util.Map;
2527
import java.util.concurrent.atomic.AtomicBoolean;
2628

2729
import org.eclipse.aether.transfer.TransferCancelledException;
@@ -87,11 +89,42 @@ public void get(GetTask task) throws Exception {
8789
* download starts at the first byte of the resource.
8890
* @throws IOException If the transfer encountered an I/O error.
8991
* @throws TransferCancelledException If the transfer was cancelled.
92+
* @deprecated Use {@link #utilGet(GetTask, InputStream, boolean, long, boolean, Map)} instead.
9093
*/
94+
@Deprecated
9195
protected void utilGet(GetTask task, InputStream is, boolean close, long length, boolean resume)
9296
throws IOException, TransferCancelledException {
97+
utilGet(task, is, close, length, resume, Collections.emptyMap());
98+
}
99+
100+
/**
101+
* Performs stream-based I/O for the specified download task and notifies the configured transport listener.
102+
* Subclasses might want to invoke this utility method from within their {@link #implGet(GetTask)} to avoid
103+
* boilerplate I/O code.
104+
*
105+
* @param task The download to perform, must not be {@code null}.
106+
* @param is The input stream to download the data from, must not be {@code null}.
107+
* @param close {@code true} if the supplied input stream should be automatically closed, {@code false} to leave the
108+
* stream open.
109+
* @param length The size in bytes of the downloaded resource or {@code -1} if unknown, not to be confused with the
110+
* length of the supplied input stream which might be smaller if the download is resumed.
111+
* @param resume {@code true} if the download resumes from {@link GetTask#getResumeOffset()}, {@code false} if the
112+
* download starts at the first byte of the resource.
113+
* @param transportProperties the transport properties connected with this download. May be empty.
114+
* @throws IOException If the transfer encountered an I/O error.
115+
* @throws TransferCancelledException If the transfer was cancelled.
116+
* @since NEXT
117+
*/
118+
protected void utilGet(
119+
GetTask task,
120+
InputStream is,
121+
boolean close,
122+
long length,
123+
boolean resume,
124+
Map<TransportListener.TransportPropertyKey, Object> transportProperties)
125+
throws IOException, TransferCancelledException {
93126
try (OutputStream os = task.newOutputStream(resume)) {
94-
task.getListener().transportStarted(resume ? task.getResumeOffset() : 0L, length);
127+
task.getListener().transportStarted(resume ? task.getResumeOffset() : 0L, length, transportProperties);
95128
copy(os, is, task.getListener());
96129
} finally {
97130
if (close) {
@@ -126,11 +159,36 @@ public void put(PutTask task) throws Exception {
126159
* the stream open.
127160
* @throws IOException If the transfer encountered an I/O error.
128161
* @throws TransferCancelledException If the transfer was cancelled.
162+
* @deprecated Use {@link #utilPut(PutTask, OutputStream, boolean, Map)} instead.
129163
*/
164+
@Deprecated
130165
protected void utilPut(PutTask task, OutputStream os, boolean close)
131166
throws IOException, TransferCancelledException {
167+
utilPut(task, os, close, Collections.emptyMap());
168+
}
169+
170+
/**
171+
* Performs stream-based I/O for the specified upload task and notifies the configured transport listener.
172+
* Subclasses might want to invoke this utility method from within their {@link #implPut(PutTask)} to avoid
173+
* boilerplate I/O code.
174+
*
175+
* @param task The upload to perform, must not be {@code null}.
176+
* @param os The output stream to upload the data to, must not be {@code null}.
177+
* @param close {@code true} if the supplied output stream should be automatically closed, {@code false} to leave
178+
* the stream open.
179+
* @param transportProperties the transport properties connected with this upload. May be empty.
180+
* @throws IOException If the transfer encountered an I/O error.
181+
* @throws TransferCancelledException If the transfer was cancelled.
182+
* @since NEXT
183+
*/
184+
protected void utilPut(
185+
PutTask task,
186+
OutputStream os,
187+
boolean close,
188+
Map<TransportListener.TransportPropertyKey, Object> transportProperties)
189+
throws IOException, TransferCancelledException {
132190
try (InputStream is = task.newInputStream()) {
133-
task.getListener().transportStarted(0, task.getDataLength());
191+
task.getListener().transportStarted(0, task.getDataLength(), transportProperties);
134192
copy(os, is, task.getListener());
135193
} finally {
136194
if (close) {

maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/TransportListener.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
package org.eclipse.aether.spi.connector.transport;
2020

2121
import java.nio.ByteBuffer;
22+
import java.util.Collection;
23+
import java.util.Map;
2224

2325
import org.eclipse.aether.transfer.TransferCancelledException;
2426

@@ -34,6 +36,8 @@
3436
*/
3537
public abstract class TransportListener {
3638

39+
public interface TransportPropertyKey {}
40+
3741
/**
3842
* Enables subclassing.
3943
*/
@@ -45,8 +49,26 @@ protected TransportListener() {}
4549
*
4650
* @param dataOffset The byte offset in the resource at which the transfer starts, must not be negative.
4751
* @param dataLength The total number of bytes in the resource or {@code -1} if the length is unknown.
52+
* @param transportProperties The transport properties associated with this transfer, may be empty. The keys are transporter specific and the value types are key specific.
4853
* @throws TransferCancelledException If the transfer should be aborted.
54+
* @since NEXT
4955
*/
56+
public void transportStarted(
57+
long dataOffset, long dataLength, Map<TransportPropertyKey, Object> transportProperties)
58+
throws TransferCancelledException {
59+
transportStarted(dataOffset, dataLength);
60+
}
61+
62+
/**
63+
* Notifies the listener about the start of the data transfer. This event may arise more than once if the transfer
64+
* needs to be restarted (e.g. after an authentication failure).
65+
*
66+
* @param dataOffset The byte offset in the resource at which the transfer starts, must not be negative.
67+
* @param dataLength The total number of bytes in the resource or {@code -1} if the length is unknown.
68+
* @throws TransferCancelledException If the transfer should be aborted.
69+
* @deprecated use {@link #transportStarted(long, long, Map)} instead
70+
*/
71+
@Deprecated
5072
public void transportStarted(long dataOffset, long dataLength) throws TransferCancelledException {}
5173

5274
/**
@@ -57,4 +79,12 @@ public void transportStarted(long dataOffset, long dataLength) throws TransferCa
5779
* @throws TransferCancelledException If the transfer should be aborted.
5880
*/
5981
public void transportProgressed(ByteBuffer data) throws TransferCancelledException {}
82+
83+
/**
84+
* Notifies the listener about the completion of the data transfer.
85+
*
86+
* @param details The collection of detail strings about the transfer, may be empty but not {@code null}.
87+
* @throws TransferCancelledException If the transfer should be aborted.
88+
*/
89+
public void transportCompleted(Collection<String> details) throws TransferCancelledException {}
6090
}

maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/http/HttpTransporter.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,41 @@
1818
*/
1919
package org.eclipse.aether.spi.connector.transport.http;
2020

21+
import org.eclipse.aether.spi.connector.transport.TransportListener;
2122
import org.eclipse.aether.spi.connector.transport.Transporter;
2223

2324
/**
2425
* A transporter using HTTP protocol.
2526
*
2627
* @since 2.0.0
2728
*/
28-
public interface HttpTransporter extends Transporter {}
29+
public interface HttpTransporter extends Transporter {
30+
31+
/**
32+
* Transport property keys specific to HTTP transporters.
33+
* @see org.eclipse.aether.spi.connector.transport.TransporterListener#transportStarted(long, long, java.util.Map)
34+
*/
35+
enum HttpTransportPropertyKey implements TransportListener.TransportPropertyKey {
36+
/**
37+
* Transport property key for HTTP version. Value is a String representing the HTTP version used (e.g., "HTTP/1.1", "HTTP/2").
38+
*/
39+
HTTP_VERSION,
40+
/**
41+
* Transport property key for SSL protocol. Value is a String representing the SSL protocol used (e.g., "TLSv1.2", "TLSv1.3").
42+
*/
43+
SSL_PROTOCOL,
44+
/**
45+
* 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").
46+
*/
47+
SSL_CIPHER_SUITE,
48+
/**
49+
* Transport property key for compression algorithm. Value is a String representing the compression algorithm used (e.g., "gzip", "br", or "zstd").
50+
*/
51+
COMPRESSION_ALGORITHM,
52+
/**
53+
* Transport property key for number of bytes transferred. Value is a Long representing the total number of bytes transferred during the transport operation.
54+
* This may be less than the content length in case of compression.
55+
*/
56+
NUM_BYTES_TRANSFERRED;
57+
}
58+
}

maven-resolver-test-http/src/main/java/org/eclipse/aether/internal/test/util/http/RecordingTransportListener.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.ByteArrayOutputStream;
2222
import java.nio.Buffer;
2323
import java.nio.ByteBuffer;
24+
import java.util.Map;
2425

2526
import org.eclipse.aether.spi.connector.transport.TransportListener;
2627
import org.eclipse.aether.transfer.TransferCancelledException;
@@ -41,8 +42,12 @@ public class RecordingTransportListener extends TransportListener {
4142

4243
private boolean cancelProgress;
4344

45+
private Map<TransportPropertyKey, Object> transportProperties;
46+
4447
@Override
45-
public void transportStarted(long dataOffset, long dataLength) throws TransferCancelledException {
48+
public void transportStarted(
49+
long dataOffset, long dataLength, Map<TransportPropertyKey, Object> transportProperties)
50+
throws TransferCancelledException {
4651
startedCount++;
4752
progressedCount = 0;
4853
this.dataLength = dataLength;
@@ -51,6 +56,7 @@ public void transportStarted(long dataOffset, long dataLength) throws TransferCa
5156
if (cancelStart) {
5257
throw new TransferCancelledException();
5358
}
59+
this.transportProperties = transportProperties;
5460
}
5561

5662
@Override
@@ -104,4 +110,8 @@ public void cancelStart() {
104110
public void cancelProgress() {
105111
this.cancelProgress = true;
106112
}
113+
114+
public Map<TransportPropertyKey, Object> getTransportProperties() {
115+
return transportProperties;
116+
}
107117
}

maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.nio.file.StandardCopyOption;
3434
import java.util.Collections;
3535
import java.util.Date;
36+
import java.util.HashMap;
3637
import java.util.HashSet;
3738
import java.util.List;
3839
import java.util.Map;
@@ -92,6 +93,7 @@
9293
import org.eclipse.aether.spi.connector.transport.GetTask;
9394
import org.eclipse.aether.spi.connector.transport.PeekTask;
9495
import org.eclipse.aether.spi.connector.transport.PutTask;
96+
import org.eclipse.aether.spi.connector.transport.TransportListener;
9597
import org.eclipse.aether.spi.connector.transport.TransportTask;
9698
import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
9799
import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
@@ -674,11 +676,13 @@ public void handle(CloseableHttpResponse response) throws IOException, TransferC
674676
}
675677
}
676678

679+
Map<TransportListener.TransportPropertyKey, Object> transportProperties =
680+
createTransportProperties(response);
677681
final boolean resume = offset > 0L;
678682
final Path dataFile = task.getDataPath();
679683
if (dataFile == null) {
680684
try (InputStream is = entity.getContent()) {
681-
utilGet(task, is, true, length, resume);
685+
utilGet(task, is, true, length, resume, transportProperties);
682686
extractChecksums(response);
683687
}
684688
} else {
@@ -690,7 +694,7 @@ public void handle(CloseableHttpResponse response) throws IOException, TransferC
690694
}
691695
}
692696
try (InputStream is = entity.getContent()) {
693-
utilGet(task, is, true, length, resume);
697+
utilGet(task, is, true, length, resume, transportProperties);
694698
}
695699
tempFile.move();
696700
} finally {
@@ -718,6 +722,16 @@ private void extractChecksums(CloseableHttpResponse response) {
718722
}
719723
}
720724

725+
private static Map<TransportListener.TransportPropertyKey, Object> createTransportProperties(
726+
CloseableHttpResponse response) {
727+
Map<TransportListener.TransportPropertyKey, Object> properties = new HashMap<>();
728+
properties.put(
729+
HttpTransporter.HttpTransportPropertyKey.HTTP_VERSION,
730+
response.getProtocolVersion().toString());
731+
//
732+
return properties;
733+
}
734+
721735
private static Function<String, String> headerGetter(CloseableHttpResponse closeableHttpResponse) {
722736
return s -> {
723737
Header header = closeableHttpResponse.getFirstHeader(s);

maven-resolver-transport-jdk-parent/maven-resolver-transport-jdk11/src/main/java/org/eclipse/aether/transport/jdk/JdkTransporter.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
import org.eclipse.aether.spi.connector.transport.GetTask;
7878
import org.eclipse.aether.spi.connector.transport.PeekTask;
7979
import org.eclipse.aether.spi.connector.transport.PutTask;
80+
import org.eclipse.aether.spi.connector.transport.TransportListener;
81+
import org.eclipse.aether.spi.connector.transport.TransportListener.TransportPropertyKey;
8082
import org.eclipse.aether.spi.connector.transport.TransportTask;
8183
import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
8284
import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
@@ -370,9 +372,11 @@ protected void implGet(GetTask task) throws Exception {
370372

371373
final boolean downloadResumed = offset > 0L;
372374
final Path dataFile = task.getDataPath();
375+
final Map<TransportListener.TransportPropertyKey, Object> transportProperties =
376+
createTransportProperties(response);
373377
if (dataFile == null) {
374378
try (InputStream is = response.body()) {
375-
utilGet(task, is, true, length, downloadResumed);
379+
utilGet(task, is, true, length, downloadResumed, transportProperties);
376380
}
377381
} else {
378382
try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(dataFile)) {
@@ -383,7 +387,7 @@ protected void implGet(GetTask task) throws Exception {
383387
}
384388
}
385389
try (InputStream is = response.body()) {
386-
utilGet(task, is, true, length, downloadResumed);
390+
utilGet(task, is, true, length, downloadResumed, transportProperties);
387391
}
388392
tempFile.move();
389393
} finally {
@@ -415,6 +419,21 @@ protected void implGet(GetTask task) throws Exception {
415419
}
416420
}
417421

422+
private Map<TransportPropertyKey, Object> createTransportProperties(HttpResponse<?> response) {
423+
Map<TransportPropertyKey, Object> props = new HashMap<>();
424+
props.put(HttpTransportPropertyKey.HTTP_VERSION, response.version().toString());
425+
response.sslSession().ifPresent(ssl -> {
426+
props.put(
427+
HttpTransportPropertyKey.SSL_PROTOCOL,
428+
response.sslSession().get().getProtocol());
429+
props.put(
430+
HttpTransportPropertyKey.SSL_CIPHER_SUITE,
431+
response.sslSession().get().getCipherSuite());
432+
});
433+
// TODO: add compression algorithm if any (https://github.com/mizosoft/methanol/issues/182)
434+
return props;
435+
}
436+
418437
private static Function<String, String> headerGetter(HttpResponse<?> response) {
419438
return s -> response.headers().firstValue(s).orElse(null);
420439
}
@@ -436,7 +455,8 @@ protected void implPut(PutTask task) throws Exception {
436455
}
437456
headers.forEach(request::setHeader);
438457
try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) {
439-
utilPut(task, Files.newOutputStream(tempFile.getPath()), true);
458+
// TODO: add properties
459+
utilPut(task, Files.newOutputStream(tempFile.getPath()), true, Collections.emptyMap());
440460
request.PUT(HttpRequest.BodyPublishers.ofFile(tempFile.getPath()));
441461

442462
prepare(request);

0 commit comments

Comments
 (0)