From f1026b120202d80f18ca80342a501faedc8539c0 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 29 Nov 2013 10:12:08 +0000 Subject: [PATCH] Add JreProxySelector to handle global proxy configuration The JreProxySelector is used it to adjust the repositories as they are added to the GrapeEngine. It looks at standard System proeprties like http.proxyHost and http.proxyPort and in addition consults standard JRE Authenticator features for credentials (falling back to System properties http.proxyUser and http.proxyPassword). Fixes gh-136 --- .../boot/cli/compiler/GroovyCompiler.java | 2 - .../cli/compiler/grape/AetherGrapeEngine.java | 51 ++---- .../cli/compiler/grape/JreProxySelector.java | 157 ++++++++++++++++++ 3 files changed, 175 insertions(+), 35 deletions(-) create mode 100644 spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/JreProxySelector.java diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java index af75f3e9aa8..fa394e2c104 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/GroovyCompiler.java @@ -140,8 +140,6 @@ public class GroovyCompiler { RepositoryPolicy.UPDATE_POLICY_NEVER, RepositoryPolicy.CHECKSUM_POLICY_IGNORE)); } - builder.setProxy(AetherGrapeEngine.defaultProxy(repositoryConfiguration - .getUri().getScheme())); repositories.add(builder.build()); } return repositories; diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java index d9afbd6967b..a137359892c 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java @@ -42,7 +42,7 @@ import org.eclipse.aether.impl.DefaultServiceLocator; import org.eclipse.aether.internal.impl.DefaultRepositorySystem; import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepositoryManager; -import org.eclipse.aether.repository.Proxy; +import org.eclipse.aether.repository.ProxySelector; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.ArtifactResult; @@ -81,6 +81,8 @@ public class AetherGrapeEngine implements GrapeEngine { private final List repositories; + private ProxySelector proxySelector = new JreProxySelector(); + public AetherGrapeEngine(GroovyClassLoader classLoader, List remoteRepositories) { this.classLoader = classLoader; @@ -90,36 +92,13 @@ public class AetherGrapeEngine implements GrapeEngine { LocalRepositoryManager localRepositoryManager = this.repositorySystem .newLocalRepositoryManager(session, localRepository); session.setLocalRepositoryManager(localRepositoryManager); + session.setProxySelector(this.proxySelector); this.session = session; - this.repositories = new ArrayList(remoteRepositories); - this.progressReporter = getProgressReporter(session); - } - - public static Proxy defaultProxy(String protocol) { - // TODO: proxy authentication - if ("http".equals(protocol) || "dav".equals(protocol)) { - String proxyHost = System.getProperty("http.proxyHost"); - if (proxyHost != null) { - // Use defaults from normal JVM proxy handler - return new Proxy("http", proxyHost, new Integer(System.getProperty( - "http.proxyPort", "80"))); - } - } - else if ("https".equals(protocol) || "davs".equals(protocol)) { - String secureProxyHost = System.getProperty("https.proxyHost"); - if (secureProxyHost != null) { - return new Proxy("https", secureProxyHost, new Integer( - System.getProperty("https.proxyPort", "443"))); - } + this.repositories = new ArrayList(); + for (RemoteRepository repository : remoteRepositories) { + addRepository(repository); } - else if ("ftp".equals(protocol)) { - String secureProxyHost = System.getProperty("ftp.proxyHost"); - if (secureProxyHost != null) { - return new Proxy("ftp", secureProxyHost, new Integer(System.getProperty( - "ftp.proxyPort", "443"))); - } - } - return null; + this.progressReporter = getProgressReporter(session); } private ServiceLocator createServiceLocator() { @@ -275,11 +254,17 @@ public class AetherGrapeEngine implements GrapeEngine { String root = (String) args.get("root"); RemoteRepository.Builder builder = new RemoteRepository.Builder(name, "default", root); - String protocol = root.contains(":") ? root.substring(0, root.indexOf(":")) - : "none"; - builder.setProxy(AetherGrapeEngine.defaultProxy(protocol)); + RemoteRepository repository = builder.build(); + addRepository(repository); + } - this.repositories.add(builder.build()); + protected void addRepository(RemoteRepository repository) { + if (repository.getProxy() == null) { + RemoteRepository.Builder builder = new RemoteRepository.Builder(repository); + builder.setProxy(this.proxySelector.getProxy(repository)); + repository = builder.build(); + } + this.repositories.add(repository); } @Override diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/JreProxySelector.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/JreProxySelector.java new file mode 100644 index 00000000000..e52d5fdfeae --- /dev/null +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/JreProxySelector.java @@ -0,0 +1,157 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed 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.springframework.boot.cli.compiler.grape; + +import java.net.Authenticator; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.eclipse.aether.repository.Authentication; +import org.eclipse.aether.repository.AuthenticationContext; +import org.eclipse.aether.repository.AuthenticationDigest; +import org.eclipse.aether.repository.Proxy; +import org.eclipse.aether.repository.ProxySelector; +import org.eclipse.aether.repository.RemoteRepository; + +/** + * (Copied from aether source code - not available yet in Maven repo.) + * + * @author dsyer + */ +public final class JreProxySelector implements ProxySelector { + + /** + * Creates a new proxy selector that delegates to + * {@link java.net.ProxySelector#getDefault()}. + */ + public JreProxySelector() { + } + + public Proxy getProxy(RemoteRepository repository) { + List proxies = null; + try { + URI uri = new URI(repository.getUrl()).parseServerAuthority(); + proxies = java.net.ProxySelector.getDefault().select(uri); + } + catch (Exception e) { + // URL invalid or not accepted by selector or no selector at all, simply use + // no proxy + } + if (proxies != null) { + for (java.net.Proxy proxy : proxies) { + if (java.net.Proxy.Type.DIRECT.equals(proxy.type())) { + break; + } + if (java.net.Proxy.Type.HTTP.equals(proxy.type()) + && isValid(proxy.address())) { + InetSocketAddress addr = (InetSocketAddress) proxy.address(); + return new Proxy(Proxy.TYPE_HTTP, addr.getHostName(), addr.getPort(), + JreProxyAuthentication.INSTANCE); + } + } + } + return null; + } + + private static boolean isValid(SocketAddress address) { + if (address instanceof InetSocketAddress) { + /* + * NOTE: On some platforms with java.net.useSystemProxies=true, unconfigured + * proxies show up as proxy objects with empty host and port 0. + */ + InetSocketAddress addr = (InetSocketAddress) address; + if (addr.getPort() <= 0) { + return false; + } + if (addr.getHostName() == null || addr.getHostName().length() <= 0) { + return false; + } + return true; + } + return false; + } + + private static final class JreProxyAuthentication implements Authentication { + + public static final Authentication INSTANCE = new JreProxyAuthentication(); + + public void fill(AuthenticationContext context, String key, + Map data) { + Proxy proxy = context.getProxy(); + if (proxy == null) { + return; + } + if (!AuthenticationContext.USERNAME.equals(key) + && !AuthenticationContext.PASSWORD.equals(key)) { + return; + } + + try { + URL url; + try { + url = new URL(context.getRepository().getUrl()); + } + catch (Exception e) { + url = null; + } + + PasswordAuthentication auth = Authenticator + .requestPasswordAuthentication(proxy.getHost(), null, + proxy.getPort(), "http", + "Credentials for proxy " + proxy, null, url, + Authenticator.RequestorType.PROXY); + if (auth != null) { + context.put(AuthenticationContext.USERNAME, auth.getUserName()); + context.put(AuthenticationContext.PASSWORD, auth.getPassword()); + } + else { + context.put(AuthenticationContext.USERNAME, + System.getProperty("http.proxyUser")); + context.put(AuthenticationContext.PASSWORD, + System.getProperty("http.proxyPassword")); + } + } + catch (SecurityException e) { + // oh well, let's hope the proxy can do without auth + } + } + + public void digest(AuthenticationDigest digest) { + // we don't know anything about the JRE's current authenticator, assume the + // worst (i.e. interactive) + digest.update(UUID.randomUUID().toString()); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj != null && getClass().equals(obj.getClass())); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + } + +}