This is an automated email from the ASF dual-hosted git repository.

cstamas 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 968692dd4 Align configuration properties and more (#1785)
968692dd4 is described below

commit 968692dd4e67504db9c031dec7aa99c429cfa44b
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Feb 5 18:39:55 2026 +0100

    Align configuration properties and more (#1785)
    
    Initial goal is to make Resolver 1.x configuration keys transparently 
supported, but also reduce the copy pasta around "common" HTTP configuration as 
well. Transport still needs to take care about their own "native" params 
(supported only by them).
    
    Changes:
    * removed "smart" from connector checksums, they are **included checksums** 
(aligned with enum)
    * removed "maven2" from newly (in 2.x) config property (class is named like 
it, but is totally internal). Property returned to "checksums".
    * made shared helper class for transports, that provides getter, validation 
and transformation for all "common" HTTP configurations, and reuse them.
    * checked other (important) properties, like split repo, and they already 
has "legacy" key support in place
    
    Fixes #1783
---
 .../connector/basic/BasicRepositoryConnector.java  |  11 +-
 .../BasicRepositoryConnectorConfigurationKeys.java |   8 +-
 .../impl/Maven2RepositoryLayoutFactory.java        |   9 +-
 .../aether/transport/apache/ApacheTransporter.java | 150 ++--------
 .../aether/transport/jdk/JdkTransporter.java       | 151 ++---------
 .../aether/transport/jetty/JettyTransporter.java   |  52 +---
 .../aether/transport/minio/MinioTransporter.java   |  13 +-
 .../aether/transport/wagon/WagonTransporter.java   |  19 +-
 .../aether/util/connector/package-info.java        |  24 ++
 .../transport/http/HttpTransporterUtils.java       | 302 +++++++++++++++++++++
 .../connector/transport/http/package-info.java     |  24 ++
 .../util/connector/transport/package-info.java     |  24 ++
 12 files changed, 459 insertions(+), 328 deletions(-)

diff --git 
a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
 
b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
index 3d4d748fa..399d363ab 100644
--- 
a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
+++ 
b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
@@ -69,14 +69,14 @@ import org.slf4j.LoggerFactory;
 
 import static java.util.Objects.requireNonNull;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_DOWNSTREAM_THREADS;
+import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_INCLUDED_CHECKSUMS;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_PARALLEL_PUT;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_PERSISTED_CHECKSUMS;
-import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_SMART_CHECKSUMS;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_THREADS;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.CONFIG_PROP_UPSTREAM_THREADS;
+import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_INCLUDED_CHECKSUMS;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_PARALLEL_PUT;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_PERSISTED_CHECKSUMS;
-import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_SMART_CHECKSUMS;
 import static 
org.eclipse.aether.connector.basic.BasicRepositoryConnectorConfigurationKeys.DEFAULT_THREADS;
 
 /**
@@ -105,7 +105,7 @@ final class BasicRepositoryConnector implements 
RepositoryConnector {
 
     private final int maxUpstreamThreads;
 
-    private final boolean smartChecksums;
+    private final boolean includedChecksums;
 
     private final boolean parallelPut;
 
@@ -161,7 +161,8 @@ final class BasicRepositoryConnector implements 
RepositoryConnector {
                 CONFIG_PROP_DOWNSTREAM_THREADS + "." + repository.getId(),
                 CONFIG_PROP_DOWNSTREAM_THREADS,
                 CONFIG_PROP_THREADS);
-        smartChecksums = ConfigUtils.getBoolean(session, 
DEFAULT_SMART_CHECKSUMS, CONFIG_PROP_SMART_CHECKSUMS);
+        includedChecksums = ConfigUtils.getBoolean(
+                session, DEFAULT_INCLUDED_CHECKSUMS, 
CONFIG_PROP_INCLUDED_CHECKSUMS, "aether.connector.smartChecksums");
         parallelPut = ConfigUtils.getBoolean(
                 session,
                 DEFAULT_PARALLEL_PUT,
@@ -532,7 +533,7 @@ final class BasicRepositoryConnector implements 
RepositoryConnector {
                     transporter.get(task);
                     try {
                         checksumValidator.validate(
-                                listener.getChecksums(), smartChecksums ? 
task.getChecksums() : null);
+                                listener.getChecksums(), includedChecksums ? 
task.getChecksums() : null);
                         break;
                     } catch (ChecksumFailureException e) {
                         boolean retry = trial < lastTrial && e.isRetryWorthy();
diff --git 
a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorConfigurationKeys.java
 
b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorConfigurationKeys.java
index d9445635c..154595ced 100644
--- 
a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorConfigurationKeys.java
+++ 
b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorConfigurationKeys.java
@@ -105,16 +105,16 @@ public final class 
BasicRepositoryConnectorConfigurationKeys {
 
     /**
      * Flag indicating that instead of comparing the external checksum fetched 
from the remote repo with the
-     * calculated one, it should try to extract the reference checksum from 
the actual artifact response headers
+     * calculated one, it should try to extract the reference checksum 
included in the actual artifact response headers
      * This only works for HTTP transports.
      *
      * @since 0.9.0.M3
      * @configurationSource {@link 
RepositorySystemSession#getConfigProperties()}
      * @configurationType {@link java.lang.Boolean}
-     * @configurationDefaultValue {@link #DEFAULT_SMART_CHECKSUMS}
+     * @configurationDefaultValue {@link #DEFAULT_INCLUDED_CHECKSUMS}
      * @configurationRepoIdSuffix No
      */
-    public static final String CONFIG_PROP_SMART_CHECKSUMS = 
CONFIG_PROPS_PREFIX + "smartChecksums";
+    public static final String CONFIG_PROP_INCLUDED_CHECKSUMS = 
CONFIG_PROPS_PREFIX + "includedChecksums";
 
-    public static final boolean DEFAULT_SMART_CHECKSUMS = true;
+    public static final boolean DEFAULT_INCLUDED_CHECKSUMS = true;
 }
diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
index fdfc0c5ba..63de6a232 100644
--- 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
@@ -53,8 +53,6 @@ import static java.util.Objects.requireNonNull;
 public final class Maven2RepositoryLayoutFactory implements 
RepositoryLayoutFactory {
     public static final String NAME = "maven2";
 
-    private static final String CONFIG_PROPS_PREFIX = 
ConfigurationProperties.PREFIX_LAYOUT + NAME + ".";
-
     /**
      * Comma-separated list of checksum algorithms with which checksums are 
validated (downloaded) and generated
      * (uploaded) with this layout. Resolver by default supports following 
algorithms: MD5, SHA-1, SHA-256 and
@@ -68,7 +66,8 @@ public final class Maven2RepositoryLayoutFactory implements 
RepositoryLayoutFact
      * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
      * @configurationRepoIdSuffix Yes
      */
-    public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = 
CONFIG_PROPS_PREFIX + "checksumAlgorithms";
+    public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS =
+            ConfigurationProperties.PREFIX_CHECKSUMS + "checksumAlgorithms";
 
     public static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
 
@@ -86,7 +85,7 @@ public final class Maven2RepositoryLayoutFactory implements 
RepositoryLayoutFact
      * @configurationRepoIdSuffix Yes
      */
     public static final String CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS =
-            CONFIG_PROPS_PREFIX + "uploadChecksumAlgorithms";
+            ConfigurationProperties.PREFIX_CHECKSUMS + 
"uploadChecksumAlgorithms";
 
     /**
      * Comma-separated list of checksum algorithms with which checksums are 
validated (downloaded) with this layout.
@@ -102,7 +101,7 @@ public final class Maven2RepositoryLayoutFactory implements 
RepositoryLayoutFact
      * @configurationRepoIdSuffix Yes
      */
     public static final String CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS =
-            CONFIG_PROPS_PREFIX + "downloadChecksumAlgorithms";
+            ConfigurationProperties.PREFIX_CHECKSUMS + 
"downloadChecksumAlgorithms";
 
     private float priority;
 
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 1d9e655fe..505e6314b 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
@@ -23,17 +23,13 @@ import java.io.InputStream;
 import java.io.InterruptedIOException;
 import java.io.OutputStream;
 import java.io.UncheckedIOException;
-import java.net.InetAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.UnknownHostException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
-import java.util.Collections;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -83,7 +79,6 @@ import org.apache.http.impl.client.LaxRedirectStrategy;
 import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.EntityUtils;
-import org.eclipse.aether.ConfigurationProperties;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.repository.AuthenticationContext;
 import org.eclipse.aether.repository.Proxy;
@@ -100,6 +95,7 @@ import org.eclipse.aether.spi.io.PathProcessor;
 import org.eclipse.aether.transfer.NoTransporterException;
 import org.eclipse.aether.transfer.TransferCancelledException;
 import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -175,83 +171,24 @@ final class ApacheTransporter extends AbstractTransporter 
implements HttpTranspo
         this.repoAuthContext = AuthenticationContext.forRepository(session, 
repository);
         this.proxyAuthContext = AuthenticationContext.forProxy(session, 
repository);
 
-        String httpsSecurityMode = ConfigUtils.getString(
-                session,
-                ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT,
-                ConfigurationProperties.HTTPS_SECURITY_MODE + "." + 
repository.getId(),
-                ConfigurationProperties.HTTPS_SECURITY_MODE);
-        final int connectionMaxTtlSeconds = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_CONNECTION_MAX_TTL,
-                ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_CONNECTION_MAX_TTL);
-        final int maxConnectionsPerRoute = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_MAX_CONNECTIONS_PER_ROUTE,
-                ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE);
+        String httpsSecurityMode = 
HttpTransporterUtils.getHttpsSecurityMode(session, repository);
+        final int connectionMaxTtlSeconds = 
HttpTransporterUtils.getHttpConnectionMaxTtlSeconds(session, repository);
+        final int maxConnectionsPerRoute = 
HttpTransporterUtils.getHttpMaxConnectionsPerRoute(session, repository);
         this.state = new LocalState(
                 session,
                 repository,
                 new ConnMgrConfig(
                         session, repoAuthContext, httpsSecurityMode, 
connectionMaxTtlSeconds, maxConnectionsPerRoute));
 
-        this.headers = ConfigUtils.getMap(
-                session,
-                Collections.emptyMap(),
-                ConfigurationProperties.HTTP_HEADERS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_HEADERS);
-
-        this.preemptiveAuth = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_AUTH,
-                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH);
-        this.preemptivePutAuth = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_PUT_AUTH,
-                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH);
-        this.supportWebDav = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_SUPPORT_WEBDAV,
-                ConfigurationProperties.HTTP_SUPPORT_WEBDAV + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_SUPPORT_WEBDAV);
-        String credentialEncoding = ConfigUtils.getString(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_CREDENTIAL_ENCODING,
-                ConfigurationProperties.HTTP_CREDENTIAL_ENCODING + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_CREDENTIAL_ENCODING);
-        int connectTimeout = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT,
-                ConfigurationProperties.CONNECT_TIMEOUT + "." + 
repository.getId(),
-                ConfigurationProperties.CONNECT_TIMEOUT);
-        int requestTimeout = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
-                ConfigurationProperties.REQUEST_TIMEOUT + "." + 
repository.getId(),
-                ConfigurationProperties.REQUEST_TIMEOUT);
-        int retryCount = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_COUNT,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT);
-        long retryInterval = ConfigUtils.getLong(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL);
-        long retryIntervalMax = ConfigUtils.getLong(
-                session,
-                
ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL_MAX,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX + "." 
+ repository.getId(),
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX);
-        String serviceUnavailableCodesString = ConfigUtils.getString(
-                session,
-                
ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE 
+ "." + repository.getId(),
-                
ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE);
+        this.headers = HttpTransporterUtils.getHttpHeaders(session, 
repository);
+        this.preemptiveAuth = 
HttpTransporterUtils.isHttpPreemptiveAuth(session, repository);
+        this.preemptivePutAuth = 
HttpTransporterUtils.isHttpPreemptivePutAuth(session, repository);
+        this.supportWebDav = HttpTransporterUtils.isHttpSupportWebDav(session, 
repository);
+        int connectTimeout = 
HttpTransporterUtils.getHttpConnectTimeout(session, repository);
+        int requestTimeout = 
HttpTransporterUtils.getHttpRequestTimeout(session, repository);
+        int retryCount = 
HttpTransporterUtils.getHttpRetryHandlerCount(session, repository);
+        long retryInterval = 
HttpTransporterUtils.getHttpRetryHandlerInterval(session, repository);
+        long retryIntervalMax = 
HttpTransporterUtils.getHttpRetryHandlerIntervalMax(session, repository);
         String retryHandlerName = ConfigUtils.getString(
                 session,
                 HTTP_RETRY_HANDLER_NAME_STANDARD,
@@ -272,10 +209,9 @@ final class ApacheTransporter extends AbstractTransporter 
implements HttpTranspo
                 DEFAULT_FOLLOW_REDIRECTS,
                 CONFIG_PROP_FOLLOW_REDIRECTS + "." + repository.getId(),
                 CONFIG_PROP_FOLLOW_REDIRECTS);
-        String userAgent = ConfigUtils.getString(
-                session, ConfigurationProperties.DEFAULT_USER_AGENT, 
ConfigurationProperties.USER_AGENT);
+        String userAgent = HttpTransporterUtils.getUserAgent(session, 
repository);
 
-        Charset credentialsCharset = Charset.forName(credentialEncoding);
+        Charset credentialsCharset = 
HttpTransporterUtils.getHttpCredentialsEncoding(session, repository);
         Registry<AuthSchemeProvider> authSchemeRegistry = 
RegistryBuilder.<AuthSchemeProvider>create()
                 .register(AuthSchemes.BASIC, new 
BasicSchemeFactory(credentialsCharset))
                 .register(AuthSchemes.DIGEST, new 
DigestSchemeFactory(credentialsCharset))
@@ -296,7 +232,8 @@ final class ApacheTransporter extends AbstractTransporter 
implements HttpTranspo
                 .setConnectTimeout(connectTimeout)
                 // the time to wait for a connection from the connection 
manager/pool
                 .setConnectionRequestTimeout(connectTimeout)
-                .setLocalAddress(getHttpLocalAddress(session, repository))
+                
.setLocalAddress(HttpTransporterUtils.getHttpLocalAddress(session, repository)
+                        .orElse(null))
                 .setCookieSpec(CookieSpecs.STANDARD)
                 .build();
 
@@ -309,18 +246,11 @@ final class ApacheTransporter extends AbstractTransporter 
implements HttpTranspo
             throw new IllegalArgumentException(
                     "Unsupported parameter " + 
CONFIG_PROP_HTTP_RETRY_HANDLER_NAME + " value: " + retryHandlerName);
         }
-        Set<Integer> serviceUnavailableCodes = new HashSet<>();
-        try {
-            for (String code : 
ConfigUtils.parseCommaSeparatedUniqueNames(serviceUnavailableCodesString)) {
-                serviceUnavailableCodes.add(Integer.parseInt(code));
-            }
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException(
-                    "Illegal HTTP codes for " + 
ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE
-                            + " (list of integers): " + 
serviceUnavailableCodesString);
-        }
         ServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new 
ResolverServiceUnavailableRetryStrategy(
-                retryCount, retryInterval, retryIntervalMax, 
serviceUnavailableCodes);
+                retryCount,
+                retryInterval,
+                retryIntervalMax,
+                HttpTransporterUtils.getHttpServiceUnavailableCodes(session, 
repository));
 
         HttpClientBuilder builder = HttpClientBuilder.create()
                 .setUserAgent(userAgent)
@@ -346,48 +276,14 @@ final class ApacheTransporter extends AbstractTransporter 
implements HttpTranspo
             builder.useSystemProperties();
         }
 
-        final String expectContinue = ConfigUtils.getString(
-                session,
-                null,
-                ConfigurationProperties.HTTP_EXPECT_CONTINUE + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_EXPECT_CONTINUE);
-        if (expectContinue != null) {
-            state.setExpectContinue(Boolean.parseBoolean(expectContinue));
-        }
-
-        final boolean reuseConnections = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_REUSE_CONNECTIONS,
-                ConfigurationProperties.HTTP_REUSE_CONNECTIONS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_REUSE_CONNECTIONS);
-        if (!reuseConnections) {
+        HttpTransporterUtils.getHttpExpectContinue(session, 
repository).ifPresent(state::setExpectContinue);
+        if (!HttpTransporterUtils.isHttpReuseConnections(session, repository)) 
{
             
builder.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE);
         }
 
         this.client = builder.build();
     }
 
-    /**
-     * Returns non-null {@link InetAddress} if set in configuration, {@code 
null} otherwise.
-     */
-    private InetAddress getHttpLocalAddress(RepositorySystemSession session, 
RemoteRepository repository) {
-        String bindAddress = ConfigUtils.getString(
-                session,
-                null,
-                ConfigurationProperties.HTTP_LOCAL_ADDRESS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_LOCAL_ADDRESS);
-        if (bindAddress == null) {
-            return null;
-        }
-        try {
-            return InetAddress.getByName(bindAddress);
-        } catch (UnknownHostException uhe) {
-            throw new IllegalArgumentException(
-                    "Given bind address (" + bindAddress + ") cannot be 
resolved for remote repository " + repository,
-                    uhe);
-        }
-    }
-
     private static HttpHost toHost(Proxy proxy) {
         HttpHost host = null;
         if (proxy != null) {
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 31c3d381c..7b78ca427 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
@@ -57,16 +57,14 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.util.Base64;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.Semaphore;
 import java.util.function.Function;
-import java.util.function.Supplier;
 import java.util.regex.Matcher;
 
 import com.github.mizosoft.methanol.Methanol;
@@ -89,6 +87,7 @@ import org.eclipse.aether.spi.io.PathProcessor;
 import org.eclipse.aether.transfer.NoTransporterException;
 import org.eclipse.aether.transfer.TransferCancelledException;
 import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -160,9 +159,9 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
 
     private final Semaphore maxConcurrentRequests;
 
-    private boolean preemptivePutAuth;
+    private final boolean preemptivePutAuth;
 
-    private boolean preemptiveAuth;
+    private final boolean preemptiveAuth;
 
     private PasswordAuthentication serverAuthentication;
 
@@ -201,57 +200,30 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
         }
 
         HashMap<String, String> headers = new HashMap<>();
-        String userAgent = ConfigUtils.getString(
-                session, ConfigurationProperties.DEFAULT_USER_AGENT, 
ConfigurationProperties.USER_AGENT);
+        String userAgent = HttpTransporterUtils.getUserAgent(session, 
repository);
         if (userAgent != null) {
             headers.put(USER_AGENT, userAgent);
         }
-        @SuppressWarnings("unchecked")
-        Map<Object, Object> configuredHeaders = (Map<Object, Object>) 
ConfigUtils.getMap(
-                session,
-                Collections.emptyMap(),
-                ConfigurationProperties.HTTP_HEADERS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_HEADERS);
+        Map<String, String> configuredHeaders = 
HttpTransporterUtils.getHttpHeaders(session, repository);
         if (configuredHeaders != null) {
-            configuredHeaders.forEach((k, v) -> headers.put(String.valueOf(k), 
v != null ? String.valueOf(v) : null));
+            headers.putAll(configuredHeaders);
         }
         headers.put(CACHE_CONTROL, "no-cache, no-store");
 
-        this.connectTimeout = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT,
-                ConfigurationProperties.CONNECT_TIMEOUT + "." + 
repository.getId(),
-                ConfigurationProperties.CONNECT_TIMEOUT);
-        this.requestTimeout = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
-                ConfigurationProperties.REQUEST_TIMEOUT + "." + 
repository.getId(),
-                ConfigurationProperties.REQUEST_TIMEOUT);
-        String expectContinueConf = ConfigUtils.getString(
-                session,
-                null,
-                ConfigurationProperties.HTTP_EXPECT_CONTINUE + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_EXPECT_CONTINUE);
+        this.connectTimeout = 
HttpTransporterUtils.getHttpConnectTimeout(session, repository);
+        this.requestTimeout = 
HttpTransporterUtils.getHttpRequestTimeout(session, repository);
+        Optional<Boolean> expectContinue = 
HttpTransporterUtils.getHttpExpectContinue(session, repository);
         if (javaVersion > 19) {
-            this.expectContinue = expectContinueConf == null ? null : 
Boolean.parseBoolean(expectContinueConf);
+            this.expectContinue = expectContinue.orElse(null);
         } else {
             this.expectContinue = null;
-            if (expectContinueConf != null) {
+            if (expectContinue.isPresent()) {
                 LOGGER.warn(
                         "Configuration for Expect-Continue set but is ignored 
on Java versions below 20 (current java version is {}) due 
https://bugs.openjdk.org/browse/JDK-8286171";,
                         javaVersion);
             }
         }
-        final String httpsSecurityMode = ConfigUtils.getString(
-                session,
-                ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT,
-                ConfigurationProperties.HTTPS_SECURITY_MODE + "." + 
repository.getId(),
-                ConfigurationProperties.HTTPS_SECURITY_MODE);
-
-        if 
(!ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(httpsSecurityMode)
-                && 
!ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode))
 {
-            throw new IllegalArgumentException("Unsupported '" + 
httpsSecurityMode + "' HTTPS security mode.");
-        }
+        final String httpsSecurityMode = 
HttpTransporterUtils.getHttpsSecurityMode(session, repository);
         final boolean insecure = 
ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode);
 
         this.maxConcurrentRequests = new Semaphore(ConfigUtils.getInteger(
@@ -260,16 +232,8 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
                 CONFIG_PROP_MAX_CONCURRENT_REQUESTS + "." + repository.getId(),
                 CONFIG_PROP_MAX_CONCURRENT_REQUESTS));
 
-        this.preemptiveAuth = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_AUTH,
-                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH);
-        this.preemptivePutAuth = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_PUT_AUTH,
-                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH);
+        this.preemptiveAuth = 
HttpTransporterUtils.isHttpPreemptiveAuth(session, repository);
+        this.preemptivePutAuth = 
HttpTransporterUtils.isHttpPreemptivePutAuth(session, repository);
 
         this.headers = headers;
         this.client = createClient(session, repository, insecure);
@@ -477,10 +441,9 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
     }
 
     static String getBasicAuthValue(String username, char[] password) {
-        StringBuilder sb = new StringBuilder(128);
-        sb.append(username).append(':').append(password);
         // Java's HTTP client uses ISO-8859-1 for Basic auth encoding
-        return "Basic " + 
Base64.getEncoder().encodeToString(sb.toString().getBytes(ISO_8859_1));
+        return "Basic "
+                + Base64.getEncoder().encodeToString((username + ':' + 
String.valueOf(password)).getBytes(ISO_8859_1));
     }
 
     private <T> HttpResponse<T> send(HttpRequest request, 
HttpResponse.BodyHandler<T> responseBodyHandler)
@@ -577,7 +540,9 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
             builder.sslParameters(sslParameters);
         }
 
-        setLocalAddress(builder, () -> getHttpLocalAddress(session, 
repository));
+        setLocalAddress(
+                builder,
+                HttpTransporterUtils.getHttpLocalAddress(session, 
repository).orElse(null));
 
         if (repository.getProxy() != null) {
             ProxySelector proxy = ProxySelector.of(new InetSocketAddress(
@@ -609,7 +574,7 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
         return builder.build();
     }
 
-    public class RetryLoggingListener implements RetryInterceptor.Listener {
+    private static class RetryLoggingListener implements 
RetryInterceptor.Listener {
         private final int maxNumRetries;
 
         RetryLoggingListener(int maxNumRetries) {
@@ -639,51 +604,15 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
         }
     }
 
-    protected void configureRetryHandler(
+    private static void configureRetryHandler(
             RepositorySystemSession session, RemoteRepository repository, 
Methanol.Builder builder) {
-        int retryCount = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_COUNT,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT);
-        long retryInterval = ConfigUtils.getLong(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL);
-        long retryIntervalMax = ConfigUtils.getLong(
-                session,
-                
ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL_MAX,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX + "." 
+ repository.getId(),
-                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX);
-        if (retryCount < 0) {
-            throw new IllegalArgumentException("retryCount must be >= 0");
-        }
-        if (retryInterval < 0L) {
-            throw new IllegalArgumentException("retryInterval must be >= 0");
-        }
-        if (retryIntervalMax < 0L) {
-            throw new IllegalArgumentException("retryIntervalMax must be >= 
0");
-        }
-        String serviceUnavailableCodesString = ConfigUtils.getString(
-                session,
-                
ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE,
-                ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE 
+ "." + repository.getId(),
-                
ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE);
-        Set<Integer> serviceUnavailableCodes = new HashSet<>();
-        try {
-            for (String code : 
ConfigUtils.parseCommaSeparatedUniqueNames(serviceUnavailableCodesString)) {
-                serviceUnavailableCodes.add(Integer.parseInt(code));
-            }
-        } catch (NumberFormatException e) {
-            throw new IllegalArgumentException(
-                    "Illegal HTTP codes for " + 
ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE
-                            + " (list of integers): " + 
serviceUnavailableCodesString);
-        }
+        int retryCount = 
HttpTransporterUtils.getHttpRetryHandlerCount(session, repository);
+        long retryInterval = 
HttpTransporterUtils.getHttpRetryHandlerInterval(session, repository);
+        long retryIntervalMax = 
HttpTransporterUtils.getHttpRetryHandlerIntervalMax(session, repository);
         if (retryCount > 0) {
             Methanol.Interceptor rateLimitingRetryInterceptor = 
RetryInterceptor.newBuilder()
                     .maxRetries(retryCount)
-                    .onStatus(serviceUnavailableCodes::contains)
+                    
.onStatus(HttpTransporterUtils.getHttpServiceUnavailableCodes(session, 
repository)::contains)
                     .listener(new RetryLoggingListener(retryCount))
                     .backoff(RetryInterceptor.BackoffStrategy.linear(
                             Duration.ofMillis(retryInterval), 
Duration.ofMillis(retryIntervalMax)))
@@ -709,37 +638,17 @@ final class JdkTransporter extends AbstractTransporter 
implements HttpTransporte
         }
     }
 
-    private static InetAddress getHttpLocalAddress(RepositorySystemSession 
session, RemoteRepository repository) {
-        String bindAddress = ConfigUtils.getString(
-                session,
-                null,
-                ConfigurationProperties.HTTP_LOCAL_ADDRESS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_LOCAL_ADDRESS);
-        if (bindAddress == null) {
-            return null;
+    private static void setLocalAddress(HttpClient.Builder builder, 
InetAddress address) {
+        if (address == null) {
+            return;
         }
         try {
-            return InetAddress.getByName(bindAddress);
-        } catch (UnknownHostException uhe) {
-            throw new IllegalArgumentException(
-                    "Given bind address (" + bindAddress + ") cannot be 
resolved for remote repository " + repository,
-                    uhe);
-        }
-    }
-
-    private static void setLocalAddress(HttpClient.Builder builder, 
Supplier<InetAddress> addressSupplier) {
-        try {
-            final InetAddress address = addressSupplier.get();
-            if (address == null) {
-                return;
-            }
-
             final Method mtd = 
builder.getClass().getDeclaredMethod("localAddress", InetAddress.class);
             if (!mtd.canAccess(builder)) {
                 mtd.setAccessible(true);
             }
             mtd.invoke(builder, address);
-        } catch (final NoSuchMethodException nsme) {
+        } catch (final NoSuchMethodException ignore) {
             // skip, not yet in the API
         } catch (InvocationTargetException e) {
             throw new IllegalStateException(e.getTargetException());
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 33c54d9fd..b1f37d12b 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
@@ -29,7 +29,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.security.cert.X509Certificate;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -55,6 +54,7 @@ import org.eclipse.aether.spi.io.PathProcessor;
 import org.eclipse.aether.transfer.NoTransporterException;
 import org.eclipse.aether.transfer.TransferCancelledException;
 import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
 import org.eclipse.jetty.client.Authentication;
 import org.eclipse.jetty.client.BasicAuthentication;
 import org.eclipse.jetty.client.HttpClient;
@@ -151,53 +151,22 @@ final class JettyTransporter extends AbstractTransporter 
implements HttpTranspor
         }
 
         HashMap<String, String> headers = new HashMap<>();
-        String userAgent = ConfigUtils.getString(
-                session, ConfigurationProperties.DEFAULT_USER_AGENT, 
ConfigurationProperties.USER_AGENT);
+        String userAgent = HttpTransporterUtils.getUserAgent(session, 
repository);
         if (userAgent != null) {
             headers.put(USER_AGENT, userAgent);
         }
-        @SuppressWarnings("unchecked")
-        Map<Object, Object> configuredHeaders = (Map<Object, Object>) 
ConfigUtils.getMap(
-                session,
-                Collections.emptyMap(),
-                ConfigurationProperties.HTTP_HEADERS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_HEADERS);
+        Map<String, String> configuredHeaders = 
HttpTransporterUtils.getHttpHeaders(session, repository);
         if (configuredHeaders != null) {
-            configuredHeaders.forEach((k, v) -> headers.put(String.valueOf(k), 
v != null ? String.valueOf(v) : null));
+            headers.putAll(configuredHeaders);
         }
 
         this.headers = headers;
 
-        this.connectTimeout = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT,
-                ConfigurationProperties.CONNECT_TIMEOUT + "." + 
repository.getId(),
-                ConfigurationProperties.CONNECT_TIMEOUT);
-        this.requestTimeout = ConfigUtils.getInteger(
-                session,
-                ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
-                ConfigurationProperties.REQUEST_TIMEOUT + "." + 
repository.getId(),
-                ConfigurationProperties.REQUEST_TIMEOUT);
-        this.preemptiveAuth = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_AUTH,
-                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH);
-        this.preemptivePutAuth = ConfigUtils.getBoolean(
-                session,
-                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_PUT_AUTH,
-                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH);
-        final String httpsSecurityMode = ConfigUtils.getString(
-                session,
-                ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT,
-                ConfigurationProperties.HTTPS_SECURITY_MODE + "." + 
repository.getId(),
-                ConfigurationProperties.HTTPS_SECURITY_MODE);
-
-        if 
(!ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(httpsSecurityMode)
-                && 
!ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode))
 {
-            throw new IllegalArgumentException("Unsupported '" + 
httpsSecurityMode + "' HTTPS security mode.");
-        }
+        this.connectTimeout = 
HttpTransporterUtils.getHttpRequestTimeout(session, repository);
+        this.requestTimeout = 
HttpTransporterUtils.getHttpRequestTimeout(session, repository);
+        this.preemptiveAuth = 
HttpTransporterUtils.isHttpPreemptiveAuth(session, repository);
+        this.preemptivePutAuth = 
HttpTransporterUtils.isHttpPreemptivePutAuth(session, repository);
+        final String httpsSecurityMode = 
HttpTransporterUtils.getHttpsSecurityMode(session, repository);
         this.insecure = 
ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode);
 
         this.basicServerAuthenticationResult = new AtomicReference<>(null);
@@ -376,8 +345,7 @@ final class JettyTransporter extends AbstractTransporter 
implements HttpTranspor
                     .send();
         } catch (ExecutionException e) {
             Throwable t = e.getCause();
-            if (t instanceof IOException) {
-                IOException ioex = (IOException) t;
+            if (t instanceof IOException ioex) {
                 if (ioex.getCause() instanceof TransferCancelledException) {
                     throw (TransferCancelledException) ioex.getCause();
                 } else {
diff --git 
a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
 
b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
index 8566e07f9..980c51c8f 100644
--- 
a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
+++ 
b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
@@ -24,7 +24,6 @@ import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -35,7 +34,6 @@ import io.minio.UploadObjectArgs;
 import io.minio.credentials.Provider;
 import io.minio.credentials.StaticProvider;
 import io.minio.errors.ErrorResponseException;
-import org.eclipse.aether.ConfigurationProperties;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.repository.AuthenticationContext;
 import org.eclipse.aether.repository.RemoteRepository;
@@ -46,7 +44,7 @@ import org.eclipse.aether.spi.connector.transport.PutTask;
 import org.eclipse.aether.spi.connector.transport.Transporter;
 import org.eclipse.aether.spi.io.PathProcessor;
 import org.eclipse.aether.transfer.NoTransporterException;
-import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
 
 import static java.util.Objects.requireNonNull;
 
@@ -96,14 +94,9 @@ final class MinioTransporter extends AbstractTransporter 
implements Transporter
         }
 
         HashMap<String, String> headers = new HashMap<>();
-        @SuppressWarnings("unchecked")
-        Map<Object, Object> configuredHeaders = (Map<Object, Object>) 
ConfigUtils.getMap(
-                session,
-                Collections.emptyMap(),
-                ConfigurationProperties.HTTP_HEADERS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_HEADERS);
+        Map<String, String> configuredHeaders = 
HttpTransporterUtils.getHttpHeaders(session, repository);
         if (configuredHeaders != null) {
-            configuredHeaders.forEach((k, v) -> headers.put(String.valueOf(k), 
v != null ? String.valueOf(v) : null));
+            headers.putAll(configuredHeaders);
         }
         this.headers = headers;
 
diff --git 
a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
 
b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
index 107f2b8d2..082f07bca 100644
--- 
a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
+++ 
b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
@@ -56,6 +56,7 @@ import org.eclipse.aether.spi.connector.transport.Transporter;
 import org.eclipse.aether.spi.io.PathProcessor;
 import org.eclipse.aether.transfer.NoTransporterException;
 import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -134,15 +135,8 @@ final class WagonTransporter implements Transporter {
         wagonProxy = getProxy(repository, proxyAuthContext);
 
         headers = new Properties();
-        headers.put(
-                "User-Agent",
-                ConfigUtils.getString(
-                        session, ConfigurationProperties.DEFAULT_USER_AGENT, 
ConfigurationProperties.USER_AGENT));
-        Map<?, ?> headers = ConfigUtils.getMap(
-                session,
-                null,
-                ConfigurationProperties.HTTP_HEADERS + "." + 
repository.getId(),
-                ConfigurationProperties.HTTP_HEADERS);
+        headers.put("User-Agent", HttpTransporterUtils.getUserAgent(session, 
repository));
+        Map<String, String> headers = 
HttpTransporterUtils.getHttpHeaders(session, repository);
         if (headers != null) {
             this.headers.putAll(headers);
         }
@@ -270,13 +264,10 @@ final class WagonTransporter implements Transporter {
             }
         }
 
-        int connectTimeout = ConfigUtils.getInteger(
-                session, ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT, 
ConfigurationProperties.CONNECT_TIMEOUT);
-        int requestTimeout = ConfigUtils.getInteger(
-                session, ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT, 
ConfigurationProperties.REQUEST_TIMEOUT);
+        int connectTimeout = 
HttpTransporterUtils.getHttpConnectTimeout(session, repository);
+        int requestTimeout = 
HttpTransporterUtils.getHttpRequestTimeout(session, repository);
 
         wagon.setTimeout(Math.max(Math.max(connectTimeout, requestTimeout), 
0));
-
         wagon.setInteractive(ConfigUtils.getBoolean(
                 session, ConfigurationProperties.DEFAULT_INTERACTIVE, 
ConfigurationProperties.INTERACTIVE));
 
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/package-info.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/package-info.java
new file mode 100644
index 000000000..487ee9118
--- /dev/null
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+/**
+ * Utilities for connectors.
+ *
+ * @since 2.0.15
+ */
+package org.eclipse.aether.util.connector;
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/http/HttpTransporterUtils.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/http/HttpTransporterUtils.java
new file mode 100644
index 000000000..0a6f2797a
--- /dev/null
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/http/HttpTransporterUtils.java
@@ -0,0 +1,302 @@
+/*
+ * 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.util.connector.transport.http;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import org.eclipse.aether.ConfigurationProperties;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.util.ConfigUtils;
+
+/**
+ * A utility class to read HTTP transport related configuration. It implements 
all HTTP transport related configurations from
+ * {@link ConfigurationProperties} and transport implementations are free to 
use those that are supported by themselves.
+ *
+ * @see ConfigurationProperties
+ * @see RepositorySystemSession#getConfigProperties()
+ * @since 2.0.15
+ */
+public final class HttpTransporterUtils {
+    private HttpTransporterUtils() {}
+
+    /**
+     * Getter for {@link ConfigurationProperties#USER_AGENT}.
+     */
+    public static String getUserAgent(RepositorySystemSession session, 
RemoteRepository repository) {
+        return ConfigUtils.getString(
+                session,
+                ConfigurationProperties.DEFAULT_USER_AGENT,
+                ConfigurationProperties.USER_AGENT,
+                "aether.connector.userAgent");
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTPS_SECURITY_MODE}.
+     */
+    public static String getHttpsSecurityMode(RepositorySystemSession session, 
RemoteRepository repository) {
+        String result = ConfigUtils.getString(
+                session,
+                ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT,
+                ConfigurationProperties.HTTPS_SECURITY_MODE + "." + 
repository.getId(),
+                ConfigurationProperties.HTTPS_SECURITY_MODE);
+        if (!ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(result)
+                && 
!ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(result)) {
+            throw new IllegalArgumentException("Unsupported '" + result + "' 
HTTPS security mode.");
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_CONNECTION_MAX_TTL}.
+     */
+    public static int getHttpConnectionMaxTtlSeconds(RepositorySystemSession 
session, RemoteRepository repository) {
+        int result = ConfigUtils.getInteger(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_CONNECTION_MAX_TTL,
+                ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_CONNECTION_MAX_TTL);
+        if (result < 0) {
+            throw new 
IllegalArgumentException(ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + " 
value must be >= 0");
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link 
ConfigurationProperties#HTTP_MAX_CONNECTIONS_PER_ROUTE}.
+     */
+    public static int getHttpMaxConnectionsPerRoute(RepositorySystemSession 
session, RemoteRepository repository) {
+        int result = ConfigUtils.getInteger(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_MAX_CONNECTIONS_PER_ROUTE,
+                ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE);
+        if (result < 1) {
+            throw new IllegalArgumentException(
+                    ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE + " 
value must be > 0");
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_HEADERS}.
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, String> getHttpHeaders(RepositorySystemSession 
session, RemoteRepository repository) {
+        return (Map<String, String>) ConfigUtils.getMap(
+                session,
+                Collections.emptyMap(),
+                ConfigurationProperties.HTTP_HEADERS + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_HEADERS);
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_PREEMPTIVE_AUTH}.
+     */
+    public static boolean isHttpPreemptiveAuth(RepositorySystemSession 
session, RemoteRepository repository) {
+        return ConfigUtils.getBoolean(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_AUTH,
+                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_PREEMPTIVE_AUTH);
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_PREEMPTIVE_PUT_AUTH}.
+     */
+    public static boolean isHttpPreemptivePutAuth(RepositorySystemSession 
session, RemoteRepository repository) {
+        return ConfigUtils.getBoolean(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_PUT_AUTH,
+                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH);
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_SUPPORT_WEBDAV}.
+     */
+    public static boolean isHttpSupportWebDav(RepositorySystemSession session, 
RemoteRepository repository) {
+        return ConfigUtils.getBoolean(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_SUPPORT_WEBDAV,
+                ConfigurationProperties.HTTP_SUPPORT_WEBDAV + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_SUPPORT_WEBDAV);
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_CREDENTIAL_ENCODING}.
+     */
+    public static Charset getHttpCredentialsEncoding(RepositorySystemSession 
session, RemoteRepository repository) {
+        return Charset.forName(ConfigUtils.getString(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_CREDENTIAL_ENCODING,
+                ConfigurationProperties.HTTP_CREDENTIAL_ENCODING + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_CREDENTIAL_ENCODING));
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#CONNECT_TIMEOUT}.
+     */
+    public static int getHttpConnectTimeout(RepositorySystemSession session, 
RemoteRepository repository) {
+        return ConfigUtils.getInteger(
+                session,
+                ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT,
+                ConfigurationProperties.CONNECT_TIMEOUT + "." + 
repository.getId(),
+                ConfigurationProperties.CONNECT_TIMEOUT);
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#REQUEST_TIMEOUT}.
+     */
+    public static int getHttpRequestTimeout(RepositorySystemSession session, 
RemoteRepository repository) {
+        return ConfigUtils.getInteger(
+                session,
+                ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
+                ConfigurationProperties.REQUEST_TIMEOUT + "." + 
repository.getId(),
+                ConfigurationProperties.REQUEST_TIMEOUT);
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_RETRY_HANDLER_COUNT}.
+     */
+    public static int getHttpRetryHandlerCount(RepositorySystemSession 
session, RemoteRepository repository) {
+        int result = ConfigUtils.getInteger(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_COUNT,
+                ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT);
+        if (result < 0) {
+            throw new IllegalArgumentException(
+                    ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + " value 
must be >= 0");
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_RETRY_HANDLER_INTERVAL}.
+     */
+    public static long getHttpRetryHandlerInterval(RepositorySystemSession 
session, RemoteRepository repository) {
+        long result = ConfigUtils.getLong(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL,
+                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL);
+        if (result < 0) {
+            throw new IllegalArgumentException(
+                    ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL + " 
value must be >= 0");
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link 
ConfigurationProperties#HTTP_RETRY_HANDLER_INTERVAL_MAX}.
+     */
+    public static long getHttpRetryHandlerIntervalMax(RepositorySystemSession 
session, RemoteRepository repository) {
+        long result = ConfigUtils.getLong(
+                session,
+                
ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL_MAX,
+                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX + "." 
+ repository.getId(),
+                ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX);
+        if (result < 0) {
+            throw new IllegalArgumentException(
+                    ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX + 
" value must be >= 0");
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_EXPECT_CONTINUE}.
+     */
+    public static Optional<Boolean> getHttpExpectContinue(
+            RepositorySystemSession session, RemoteRepository repository) {
+        String expectContinue = ConfigUtils.getString(
+                session,
+                null,
+                ConfigurationProperties.HTTP_EXPECT_CONTINUE + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_EXPECT_CONTINUE);
+        if (expectContinue != null) {
+            return Optional.of(Boolean.parseBoolean(expectContinue));
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_REUSE_CONNECTIONS}.
+     */
+    public static boolean isHttpReuseConnections(RepositorySystemSession 
session, RemoteRepository repository) {
+        return ConfigUtils.getBoolean(
+                session,
+                ConfigurationProperties.DEFAULT_HTTP_REUSE_CONNECTIONS,
+                ConfigurationProperties.HTTP_REUSE_CONNECTIONS + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_REUSE_CONNECTIONS);
+    }
+
+    /**
+     * Getter for {@link 
ConfigurationProperties#HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE}.
+     */
+    public static Set<Integer> getHttpServiceUnavailableCodes(
+            RepositorySystemSession session, RemoteRepository repository) {
+        String stringValue = ConfigUtils.getString(
+                session,
+                
ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE,
+                ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE 
+ "." + repository.getId(),
+                
ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE);
+        Set<Integer> result = new HashSet<>();
+        try {
+            for (String code : 
ConfigUtils.parseCommaSeparatedUniqueNames(stringValue)) {
+                result.add(Integer.parseInt(code));
+            }
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(
+                    "Illegal HTTP codes for " + 
ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE
+                            + " (list of integers): " + stringValue);
+        }
+        return result;
+    }
+
+    /**
+     * Getter for {@link ConfigurationProperties#HTTP_LOCAL_ADDRESS}.
+     */
+    public static Optional<InetAddress> getHttpLocalAddress(
+            RepositorySystemSession session, RemoteRepository repository) {
+        String bindAddress = ConfigUtils.getString(
+                session,
+                null,
+                ConfigurationProperties.HTTP_LOCAL_ADDRESS + "." + 
repository.getId(),
+                ConfigurationProperties.HTTP_LOCAL_ADDRESS);
+        if (bindAddress != null) {
+            try {
+                return Optional.of(InetAddress.getByName(bindAddress));
+            } catch (UnknownHostException uhe) {
+                throw new IllegalArgumentException(
+                        "Given bind address (" + bindAddress + ") cannot be 
resolved for remote repository "
+                                + repository,
+                        uhe);
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/http/package-info.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/http/package-info.java
new file mode 100644
index 000000000..0849661b3
--- /dev/null
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/http/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+/**
+ * Utilities for HTTP transporters.
+ *
+ * @since 2.0.15
+ */
+package org.eclipse.aether.util.connector.transport.http;
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/package-info.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/package-info.java
new file mode 100644
index 000000000..4bddaf327
--- /dev/null
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/connector/transport/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+/**
+ * Utilities for transporters.
+ *
+ * @since 2.0.15
+ */
+package org.eclipse.aether.util.connector.transport;

Reply via email to