View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util.repository;
20  
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.StringTokenizer;
27  import java.util.concurrent.CopyOnWriteArrayList;
28  import java.util.regex.Pattern;
29  
30  import org.eclipse.aether.repository.Proxy;
31  import org.eclipse.aether.repository.ProxySelector;
32  import org.eclipse.aether.repository.RemoteRepository;
33  
34  import static java.util.Objects.requireNonNull;
35  
36  /**
37   * A simple proxy selector that selects the first matching proxy from a list of configured proxies.
38   */
39  public final class DefaultProxySelector implements ProxySelector {
40  
41      private final List<ProxyDef> proxies = new CopyOnWriteArrayList<>();
42  
43      /**
44       * Adds the specified proxy definition to the selector. Proxy definitions are ordered, the first matching proxy for
45       * a given repository will be used.
46       *
47       * @param proxy the proxy definition to add, must not be {@code null}
48       * @param nonProxyHosts the list of (case-insensitive) host names to exclude from proxying, may be {@code null}
49       * @return this proxy selector for chaining, never {@code null}
50       */
51      public DefaultProxySelector add(Proxy proxy, String nonProxyHosts) {
52          requireNonNull(proxy, "proxy cannot be null");
53          proxies.add(new ProxyDef(proxy, nonProxyHosts));
54  
55          return this;
56      }
57  
58      public Proxy getProxy(RemoteRepository repository) {
59          requireNonNull(repository, "repository cannot be null");
60          Map<String, ProxyDef> candidates = new HashMap<>();
61  
62          String host = repository.getHost();
63          for (ProxyDef proxy : proxies) {
64              if (!proxy.nonProxyHosts.isNonProxyHost(host)) {
65                  String key = proxy.proxy.getType().toLowerCase(Locale.ENGLISH);
66                  if (!candidates.containsKey(key)) {
67                      candidates.put(key, proxy);
68                  }
69              }
70          }
71  
72          String protocol = repository.getProtocol().toLowerCase(Locale.ENGLISH);
73  
74          if ("davs".equals(protocol)) {
75              protocol = "https";
76          } else if ("dav".equals(protocol)) {
77              protocol = "http";
78          } else if (protocol.startsWith("dav:")) {
79              protocol = protocol.substring("dav:".length());
80          }
81  
82          ProxyDef proxy = candidates.get(protocol);
83          if (proxy == null && "https".equals(protocol)) {
84              proxy = candidates.get("http");
85          }
86  
87          return (proxy != null) ? proxy.proxy : null;
88      }
89  
90      static class NonProxyHosts {
91  
92          private final Pattern[] patterns;
93  
94          NonProxyHosts(String nonProxyHosts) {
95              List<Pattern> patterns = new ArrayList<>();
96              if (nonProxyHosts != null) {
97                  for (StringTokenizer tokenizer = new StringTokenizer(nonProxyHosts, "|"); tokenizer.hasMoreTokens(); ) {
98                      String pattern = tokenizer.nextToken();
99                      pattern = pattern.replace(".", "\\.").replace("*", ".*");
100                     patterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
101                 }
102             }
103             this.patterns = patterns.toArray(new Pattern[0]);
104         }
105 
106         boolean isNonProxyHost(String host) {
107             if (host != null) {
108                 for (Pattern pattern : patterns) {
109                     if (pattern.matcher(host).matches()) {
110                         return true;
111                     }
112                 }
113             }
114             return false;
115         }
116     }
117 
118     static class ProxyDef {
119 
120         final Proxy proxy;
121 
122         final NonProxyHosts nonProxyHosts;
123 
124         ProxyDef(Proxy proxy, String nonProxyHosts) {
125             this.proxy = proxy;
126             this.nonProxyHosts = new NonProxyHosts(nonProxyHosts);
127         }
128     }
129 }