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 d013a328 [MRESOLVER-584] Jetty fix for GOAWAY (#533) d013a328 is described below commit d013a3289fdfecf30932c63133ae41e1661aa5ab Author: Tamas Cservenak <ta...@cservenak.net> AuthorDate: Fri Aug 2 15:19:46 2024 +0200 [MRESOLVER-584] Jetty fix for GOAWAY (#533) Just shorten the client lifespan to connector transaction duration vs as before, for whole session duration. Also simplify the code. --- https://issues.apache.org/jira/browse/MRESOLVER-584 --- .../aether/transport/jetty/JettyTransporter.java | 310 +++++++++------------ 1 file changed, 134 insertions(+), 176 deletions(-) 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 4dcb500b..83f88ced 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 @@ -70,8 +70,6 @@ import org.eclipse.jetty.http2.client.HTTP2Client; import org.eclipse.jetty.http2.client.http.ClientConnectionFactoryOverHTTP2; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.ACCEPT_ENCODING; import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.CONTENT_LENGTH; @@ -93,6 +91,10 @@ import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.USER final class JettyTransporter extends AbstractTransporter implements HttpTransporter { private static final long MODIFICATION_THRESHOLD = 60L * 1000L; + private final RepositorySystemSession session; + + private final RemoteRepository repository; + private final ChecksumExtractor checksumExtractor; private final PathProcessor pathProcessor; @@ -109,9 +111,11 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor private final boolean preemptivePutAuth; - private final BasicAuthentication.BasicResult basicServerAuthenticationResult; + private final boolean insecure; + + private final AtomicReference<BasicAuthentication.BasicResult> basicServerAuthenticationResult; - private final BasicAuthentication.BasicResult basicProxyAuthenticationResult; + private final AtomicReference<BasicAuthentication.BasicResult> basicProxyAuthenticationResult; JettyTransporter( RepositorySystemSession session, @@ -119,6 +123,8 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor ChecksumExtractor checksumExtractor, PathProcessor pathProcessor) throws NoTransporterException { + this.session = session; + this.repository = repository; this.checksumExtractor = checksumExtractor; this.pathProcessor = pathProcessor; try { @@ -177,14 +183,34 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor 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); - this.client = getOrCreateClient(session, repository); + if (!ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(httpsSecurityMode) + && !ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode)) { + throw new IllegalArgumentException("Unsupported '" + httpsSecurityMode + "' HTTPS security mode."); + } + this.insecure = ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode); + + this.basicServerAuthenticationResult = new AtomicReference<>(null); + this.basicProxyAuthenticationResult = new AtomicReference<>(null); + try { + this.client = createClient(); + } catch (Exception e) { + throw new NoTransporterException(repository, e); + } + } - final String instanceKey = JETTY_INSTANCE_KEY_PREFIX + repository.getId(); - this.basicServerAuthenticationResult = - (BasicAuthentication.BasicResult) session.getData().get(instanceKey + ".serverAuth"); - this.basicProxyAuthenticationResult = - (BasicAuthentication.BasicResult) session.getData().get(instanceKey + ".proxyAuth"); + private void mayApplyPreemptiveAuth(Request request) { + if (basicServerAuthenticationResult.get() != null) { + basicServerAuthenticationResult.get().apply(request); + } + if (basicProxyAuthenticationResult.get() != null) { + basicProxyAuthenticationResult.get().apply(request); + } } private URI resolve(TransportTask task) { @@ -207,12 +233,7 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor .method("HEAD"); request.headers(m -> headers.forEach(m::add)); if (preemptiveAuth) { - if (basicServerAuthenticationResult != null) { - basicServerAuthenticationResult.apply(request); - } - if (basicProxyAuthenticationResult != null) { - basicProxyAuthenticationResult.apply(request); - } + mayApplyPreemptiveAuth(request); } Response response = request.send(); if (response.getStatus() >= MULTIPLE_CHOICES) { @@ -232,12 +253,7 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor .method("GET"); request.headers(m -> headers.forEach(m::add)); if (preemptiveAuth) { - if (basicServerAuthenticationResult != null) { - basicServerAuthenticationResult.apply(request); - } - if (basicProxyAuthenticationResult != null) { - basicProxyAuthenticationResult.apply(request); - } + mayApplyPreemptiveAuth(request); } if (resume) { @@ -335,12 +351,7 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor Request request = client.newRequest(resolve(task)).method("PUT").timeout(requestTimeout, TimeUnit.MILLISECONDS); request.headers(m -> headers.forEach(m::add)); if (preemptiveAuth || preemptivePutAuth) { - if (basicServerAuthenticationResult != null) { - basicServerAuthenticationResult.apply(request); - } - if (basicProxyAuthenticationResult != null) { - basicProxyAuthenticationResult.apply(request); - } + mayApplyPreemptiveAuth(request); } request.body(new PutTaskRequestContent(task)); AtomicBoolean started = new AtomicBoolean(false); @@ -395,176 +406,123 @@ final class JettyTransporter extends AbstractTransporter implements HttpTranspor @Override protected void implClose() { - // noop + try { + this.client.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } } - /** - * Visible for testing. - */ - static final String JETTY_INSTANCE_KEY_PREFIX = JettyTransporterFactory.class.getName() + ".jetty."; + @SuppressWarnings("checkstyle:methodlength") + private HttpClient createClient() throws Exception { + BasicAuthentication.BasicResult serverAuth = null; + BasicAuthentication.BasicResult proxyAuth = null; + SSLContext sslContext = null; + BasicAuthentication basicAuthentication = null; + try (AuthenticationContext repoAuthContext = AuthenticationContext.forRepository(session, repository)) { + if (repoAuthContext != null) { + sslContext = repoAuthContext.get(AuthenticationContext.SSL_CONTEXT, SSLContext.class); + + String username = repoAuthContext.get(AuthenticationContext.USERNAME); + String password = repoAuthContext.get(AuthenticationContext.PASSWORD); + + URI uri = URI.create(repository.getUrl()); + basicAuthentication = new BasicAuthentication(uri, Authentication.ANY_REALM, username, password); + if (preemptiveAuth || preemptivePutAuth) { + serverAuth = new BasicAuthentication.BasicResult(uri, HttpHeader.AUTHORIZATION, username, password); + } + } + } - static final Logger LOGGER = LoggerFactory.getLogger(JettyTransporter.class); + if (sslContext == null) { + if (insecure) { + sslContext = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} - @SuppressWarnings("checkstyle:methodlength") - private HttpClient getOrCreateClient(RepositorySystemSession session, RemoteRepository repository) - throws NoTransporterException { + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} - final String instanceKey = JETTY_INSTANCE_KEY_PREFIX + repository.getId(); + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }; + sslContext.init(null, new X509TrustManager[] {tm}, null); + } else { + sslContext = SSLContext.getDefault(); + } + } - final String httpsSecurityMode = ConfigUtils.getString( + int connectTimeout = ConfigUtils.getInteger( 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."); + ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT, + ConfigurationProperties.CONNECT_TIMEOUT + "." + repository.getId(), + ConfigurationProperties.CONNECT_TIMEOUT); + + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setSslContext(sslContext); + if (insecure) { + sslContextFactory.setEndpointIdentificationAlgorithm(null); + sslContextFactory.setHostnameVerifier((name, context) -> true); } - final boolean insecure = ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode); - try { - AtomicReference<BasicAuthentication.BasicResult> serverAuth = new AtomicReference<>(null); - AtomicReference<BasicAuthentication.BasicResult> proxyAuth = new AtomicReference<>(null); - HttpClient client = (HttpClient) session.getData().computeIfAbsent(instanceKey, () -> { - SSLContext sslContext = null; - BasicAuthentication basicAuthentication = null; - try { - try (AuthenticationContext repoAuthContext = - AuthenticationContext.forRepository(session, repository)) { - if (repoAuthContext != null) { - sslContext = repoAuthContext.get(AuthenticationContext.SSL_CONTEXT, SSLContext.class); - - String username = repoAuthContext.get(AuthenticationContext.USERNAME); - String password = repoAuthContext.get(AuthenticationContext.PASSWORD); - - URI uri = URI.create(repository.getUrl()); - basicAuthentication = - new BasicAuthentication(uri, Authentication.ANY_REALM, username, password); - if (preemptiveAuth || preemptivePutAuth) { - serverAuth.set(new BasicAuthentication.BasicResult( - uri, HttpHeader.AUTHORIZATION, username, password)); - } - } - } + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSslContextFactory(sslContextFactory); - if (sslContext == null) { - if (insecure) { - sslContext = SSLContext.getInstance("TLS"); - X509TrustManager tm = new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) {} + HTTP2Client http2Client = new HTTP2Client(clientConnector); + ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client); - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) {} + HttpClientTransportDynamic transport; + if ("https".equalsIgnoreCase(repository.getProtocol())) { + transport = new HttpClientTransportDynamic( + clientConnector, http2, HttpClientConnectionFactory.HTTP11); // HTTPS, prefer H2 + } else { + transport = new HttpClientTransportDynamic( + clientConnector, HttpClientConnectionFactory.HTTP11, http2); // plaintext HTTP, H2 cannot be used + } - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - }; - sslContext.init(null, new X509TrustManager[] {tm}, null); - } else { - sslContext = SSLContext.getDefault(); - } - } + HttpClient httpClient = new HttpClient(transport); + httpClient.setConnectTimeout(connectTimeout); + httpClient.setFollowRedirects(true); + httpClient.setMaxRedirects(2); - int connectTimeout = ConfigUtils.getInteger( - session, - ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT, - ConfigurationProperties.CONNECT_TIMEOUT + "." + repository.getId(), - ConfigurationProperties.CONNECT_TIMEOUT); - - SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); - sslContextFactory.setSslContext(sslContext); - if (insecure) { - sslContextFactory.setEndpointIdentificationAlgorithm(null); - sslContextFactory.setHostnameVerifier((name, context) -> true); - } + httpClient.setUserAgentField(null); // we manage it - ClientConnector clientConnector = new ClientConnector(); - clientConnector.setSslContextFactory(sslContextFactory); - - HTTP2Client http2Client = new HTTP2Client(clientConnector); - ClientConnectionFactoryOverHTTP2.HTTP2 http2 = - new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client); - - HttpClientTransportDynamic transport; - if ("https".equalsIgnoreCase(repository.getProtocol())) { - transport = new HttpClientTransportDynamic( - clientConnector, http2, HttpClientConnectionFactory.HTTP11); // HTTPS, prefer H2 - } else { - transport = new HttpClientTransportDynamic( - clientConnector, - HttpClientConnectionFactory.HTTP11, - http2); // plaintext HTTP, H2 cannot be used - } + if (basicAuthentication != null) { + httpClient.getAuthenticationStore().addAuthentication(basicAuthentication); + } - HttpClient httpClient = new HttpClient(transport); - httpClient.setConnectTimeout(connectTimeout); - httpClient.setFollowRedirects(true); - httpClient.setMaxRedirects(2); + if (repository.getProxy() != null) { + HttpProxy proxy = new HttpProxy( + repository.getProxy().getHost(), repository.getProxy().getPort()); - httpClient.setUserAgentField(null); // we manage it + httpClient.getProxyConfiguration().addProxy(proxy); + try (AuthenticationContext proxyAuthContext = AuthenticationContext.forProxy(session, repository)) { + if (proxyAuthContext != null) { + String username = proxyAuthContext.get(AuthenticationContext.USERNAME); + String password = proxyAuthContext.get(AuthenticationContext.PASSWORD); - if (basicAuthentication != null) { - httpClient.getAuthenticationStore().addAuthentication(basicAuthentication); - } + BasicAuthentication proxyAuthentication = + new BasicAuthentication(proxy.getURI(), Authentication.ANY_REALM, username, password); - if (repository.getProxy() != null) { - HttpProxy proxy = new HttpProxy( - repository.getProxy().getHost(), - repository.getProxy().getPort()); - - httpClient.getProxyConfiguration().addProxy(proxy); - try (AuthenticationContext proxyAuthContext = - AuthenticationContext.forProxy(session, repository)) { - if (proxyAuthContext != null) { - String username = proxyAuthContext.get(AuthenticationContext.USERNAME); - String password = proxyAuthContext.get(AuthenticationContext.PASSWORD); - - BasicAuthentication proxyAuthentication = new BasicAuthentication( - proxy.getURI(), Authentication.ANY_REALM, username, password); - - httpClient.getAuthenticationStore().addAuthentication(proxyAuthentication); - if (preemptiveAuth || preemptivePutAuth) { - proxyAuth.set(new BasicAuthentication.BasicResult( - proxy.getURI(), HttpHeader.PROXY_AUTHORIZATION, username, password)); - } - } - } + httpClient.getAuthenticationStore().addAuthentication(proxyAuthentication); + if (preemptiveAuth || preemptivePutAuth) { + proxyAuth = new BasicAuthentication.BasicResult( + proxy.getURI(), HttpHeader.PROXY_AUTHORIZATION, username, password); } - if (!session.addOnSessionEndedHandler(() -> { - try { - httpClient.stop(); - } catch (Exception e) { - throw new RuntimeException(e); - } - })) { - LOGGER.warn( - "Using Resolver 2 feature without Resolver 2 session handling, you may leak resources."); - } - httpClient.start(); - return httpClient; - } catch (Exception e) { - throw new WrapperEx(e); } - }); - if (serverAuth.get() != null) { - session.getData().set(instanceKey + ".serverAuth", serverAuth.get()); - } - if (proxyAuth.get() != null) { - session.getData().set(instanceKey + ".proxyAuth", proxyAuth.get()); } - return client; - } catch (WrapperEx e) { - throw new NoTransporterException(repository, e.getCause()); } - } - - private static final class WrapperEx extends RuntimeException { - private WrapperEx(Throwable cause) { - super(cause); + if (serverAuth != null) { + this.basicServerAuthenticationResult.set(serverAuth); } + if (proxyAuth != null) { + this.basicProxyAuthenticationResult.set(proxyAuth); + } + + httpClient.start(); + return httpClient; } }