[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
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 4dcb500..83f88ce 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.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 @@
 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 @@
 
     private final boolean preemptivePutAuth;
 
-    private final BasicAuthentication.BasicResult basicServerAuthenticationResult;
+    private final boolean insecure;
 
-    private final BasicAuthentication.BasicResult basicProxyAuthenticationResult;
+    private final AtomicReference<BasicAuthentication.BasicResult> basicServerAuthenticationResult;
+
+    private final AtomicReference<BasicAuthentication.BasicResult> basicProxyAuthenticationResult;
 
     JettyTransporter(
             RepositorySystemSession session,
@@ -119,6 +123,8 @@
             ChecksumExtractor checksumExtractor,
             PathProcessor pathProcessor)
             throws NoTransporterException {
+        this.session = session;
+        this.repository = repository;
         this.checksumExtractor = checksumExtractor;
         this.pathProcessor = pathProcessor;
         try {
@@ -177,14 +183,34 @@
                 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);
 
-        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");
+        this.basicServerAuthenticationResult = new AtomicReference<>(null);
+        this.basicProxyAuthenticationResult = new AtomicReference<>(null);
+        try {
+            this.client = createClient();
+        } catch (Exception e) {
+            throw new NoTransporterException(repository, e);
+        }
+    }
+
+    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 @@
                 .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 @@
                     .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 @@
         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 @@
 
     @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.";
-
-    static final Logger LOGGER = LoggerFactory.getLogger(JettyTransporter.class);
-
     @SuppressWarnings("checkstyle:methodlength")
-    private HttpClient getOrCreateClient(RepositorySystemSession session, RemoteRepository repository)
-            throws NoTransporterException {
+    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);
 
-        final String instanceKey = JETTY_INSTANCE_KEY_PREFIX + repository.getId();
+                String username = repoAuthContext.get(AuthenticationContext.USERNAME);
+                String password = repoAuthContext.get(AuthenticationContext.PASSWORD);
 
-        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 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));
-                            }
-                        }
-                    }
-
-                    if (sslContext == null) {
-                        if (insecure) {
-                            sslContext = SSLContext.getInstance("TLS");
-                            X509TrustManager tm = new X509TrustManager() {
-                                @Override
-                                public void checkClientTrusted(X509Certificate[] chain, String authType) {}
-
-                                @Override
-                                public void checkServerTrusted(X509Certificate[] chain, String authType) {}
-
-                                @Override
-                                public X509Certificate[] getAcceptedIssuers() {
-                                    return new X509Certificate[0];
-                                }
-                            };
-                            sslContext.init(null, new X509TrustManager[] {tm}, null);
-                        } else {
-                            sslContext = SSLContext.getDefault();
-                        }
-                    }
-
-                    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);
-                    }
-
-                    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
-                    }
-
-                    HttpClient httpClient = new HttpClient(transport);
-                    httpClient.setConnectTimeout(connectTimeout);
-                    httpClient.setFollowRedirects(true);
-                    httpClient.setMaxRedirects(2);
-
-                    httpClient.setUserAgentField(null); // we manage it
-
-                    if (basicAuthentication != null) {
-                        httpClient.getAuthenticationStore().addAuthentication(basicAuthentication);
-                    }
-
-                    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));
-                                }
-                            }
-                        }
-                    }
-                    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);
+                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);
                 }
-            });
-            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 (sslContext == null) {
+            if (insecure) {
+                sslContext = SSLContext.getInstance("TLS");
+                X509TrustManager tm = new X509TrustManager() {
+                    @Override
+                    public void checkClientTrusted(X509Certificate[] chain, String authType) {}
+
+                    @Override
+                    public void checkServerTrusted(X509Certificate[] chain, String authType) {}
+
+                    @Override
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return new X509Certificate[0];
+                    }
+                };
+                sslContext.init(null, new X509TrustManager[] {tm}, null);
+            } else {
+                sslContext = SSLContext.getDefault();
+            }
         }
+
+        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);
+        }
+
+        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
+        }
+
+        HttpClient httpClient = new HttpClient(transport);
+        httpClient.setConnectTimeout(connectTimeout);
+        httpClient.setFollowRedirects(true);
+        httpClient.setMaxRedirects(2);
+
+        httpClient.setUserAgentField(null); // we manage it
+
+        if (basicAuthentication != null) {
+            httpClient.getAuthenticationStore().addAuthentication(basicAuthentication);
+        }
+
+        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 = new BasicAuthentication.BasicResult(
+                                proxy.getURI(), HttpHeader.PROXY_AUTHORIZATION, username, password);
+                    }
+                }
+            }
+        }
+        if (serverAuth != null) {
+            this.basicServerAuthenticationResult.set(serverAuth);
+        }
+        if (proxyAuth != null) {
+            this.basicProxyAuthenticationResult.set(proxyAuth);
+        }
+
+        httpClient.start();
+        return httpClient;
     }
 }