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 7dfd7898 [MRESOLVER-361] Unreliable TCP and retries (#288) 7dfd7898 is described below commit 7dfd7898a54a84282ab59bb79c4e6c1feaecaf84 Author: Tamas Cservenak <ta...@cservenak.net> AuthorDate: Thu Jun 1 09:17:36 2023 +0200 [MRESOLVER-361] Unreliable TCP and retries (#288) Make httpClient retry for PUT. --- https://issues.apache.org/jira/browse/MRESOLVER-361 --- .../aether/transport/http/HttpTransporter.java | 35 ++++++++++++++++++---- src/site/markdown/configuration.md | 2 ++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java index 471300a0..e788787f 100644 --- a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java +++ b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java @@ -45,6 +45,7 @@ import org.apache.http.HttpStatus; import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthScope; import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.HttpResponseException; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; @@ -71,6 +72,7 @@ import org.apache.http.impl.auth.SPNegoSchemeFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.StandardHttpRequestRetryHandler; import org.apache.http.util.EntityUtils; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; @@ -102,6 +104,15 @@ final class HttpTransporter extends AbstractTransporter { static final String USE_SYSTEM_PROPERTIES = "aether.connector.http.useSystemProperties"; + static final String HTTP_RETRY_HANDLER_NAME = "aether.connector.http.retryHandler.name"; + + private static final String HTTP_RETRY_HANDLER_NAME_STANDARD = "standard"; + + private static final String HTTP_RETRY_HANDLER_NAME_DEFAULT = "default"; + + static final String HTTP_RETRY_HANDLER_REQUEST_SENT_ENABLED = + "aether.connector.http.retryHandler.requestSentEnabled"; + private static final Pattern CONTENT_RANGE_PATTERN = Pattern.compile("\\s*bytes\\s+([0-9]+)\\s*-\\s*([0-9]+)\\s*/.*"); @@ -214,11 +225,20 @@ final class HttpTransporter extends AbstractTransporter { ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_COUNT, ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + "." + repository.getId(), ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT); + String retryHandlerName = ConfigUtils.getString( + session, + HTTP_RETRY_HANDLER_NAME_STANDARD, + HTTP_RETRY_HANDLER_NAME + "." + repository.getId(), + HTTP_RETRY_HANDLER_NAME); + boolean retryHandlerRequestSentEnabled = ConfigUtils.getBoolean( + session, + false, + HTTP_RETRY_HANDLER_REQUEST_SENT_ENABLED + "." + repository.getId(), + HTTP_RETRY_HANDLER_REQUEST_SENT_ENABLED); String userAgent = ConfigUtils.getString( session, ConfigurationProperties.DEFAULT_USER_AGENT, ConfigurationProperties.USER_AGENT); Charset credentialsCharset = Charset.forName(credentialEncoding); - Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create() .register(AuthSchemes.BASIC, new BasicSchemeFactory(credentialsCharset)) .register(AuthSchemes.DIGEST, new DigestSchemeFactory(credentialsCharset)) @@ -226,17 +246,23 @@ final class HttpTransporter extends AbstractTransporter { .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()) .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory()) .build(); - SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(requestTimeout).build(); - RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(connectTimeout) .setConnectionRequestTimeout(connectTimeout) .setSocketTimeout(requestTimeout) .build(); - DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(retryCount, false); + HttpRequestRetryHandler retryHandler; + if (HTTP_RETRY_HANDLER_NAME_STANDARD.equals(retryHandlerName)) { + retryHandler = new StandardHttpRequestRetryHandler(retryCount, retryHandlerRequestSentEnabled); + } else if (HTTP_RETRY_HANDLER_NAME_DEFAULT.equals(retryHandlerName)) { + retryHandler = new DefaultHttpRequestRetryHandler(retryCount, retryHandlerRequestSentEnabled); + } else { + throw new IllegalArgumentException( + "Unsupported parameter " + HTTP_RETRY_HANDLER_NAME + " value: " + retryHandlerName); + } HttpClientBuilder builder = HttpClientBuilder.create() .setUserAgent(userAgent) @@ -248,7 +274,6 @@ final class HttpTransporter extends AbstractTransporter { .setConnectionManagerShared(true) .setDefaultCredentialsProvider(toCredentialsProvider(server, repoAuthContext, proxy, proxyAuthContext)) .setProxy(proxy); - final boolean useSystemProperties = ConfigUtils.getBoolean( session, false, USE_SYSTEM_PROPERTIES + "." + repository.getId(), USE_SYSTEM_PROPERTIES); if (useSystemProperties) { diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md index bba3b212..797e8433 100644 --- a/src/site/markdown/configuration.md +++ b/src/site/markdown/configuration.md @@ -42,6 +42,8 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix `aether.connector.http.preemptiveAuth` | boolean | Should HTTP client use preemptive-authentication for all HTTP verbs (works only w/ BASIC). By default is disabled, as it is considered less secure. | `false` | yes `aether.connector.http.preemptivePutAuth` | boolean | Should HTTP client use preemptive-authentication for HTTP PUTs only (works only w/ BASIC). By default is enabled (same as Wagon). | `true` | yes `aether.connector.http.retryHandler.count` | int | The maximum number of times a request to a remote HTTP server should be retried in case of an error. | `3` | yes +`aether.connector.http.retryHandler.name` | String | The name of retryHandler, supported values are "standard", that obeys RFC-2616, regarding idempotent methods, and "default" that considers requests w/o payload as idempotent. | `standard` | yes +`aether.connector.http.retryHandler.requestSentEnabled` | boolean | Set to `true` if it is acceptable to retry non-idempotent requests, that have been sent. | `false` | yes `aether.connector.http.reuseConnections` | boolean | Should HTTP client reuse connections (in other words, pool connections) or not? | `true` | yes `aether.connector.http.supportWebDav` | boolean | If enabled, transport makes best effort to deploy to WebDAV server. This mode is not recommended, better use real Maven Repository Manager instead. | `false` | yes `aether.connector.http.useSystemProperties` | boolean | If enabled, underlying Apache HttpClient will use system properties as well to configure itself (typically used to set up HTTP Proxy via Java system properties). See <a href="https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/impl/client/HttpClientBuilder.html">HttpClientBuilder</a> for used properties. This mode is **not recommended**, better use documented ways of configuration instead. | [...]