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.regex.Pattern;
28  
29  import org.eclipse.aether.repository.Proxy;
30  import org.eclipse.aether.repository.ProxySelector;
31  import org.eclipse.aether.repository.RemoteRepository;
32  
33  import static java.util.Objects.requireNonNull;
34  
35  /**
36   * A simple proxy selector that selects the first matching proxy from a list of configured proxies.
37   */
38  public final class DefaultProxySelector implements ProxySelector {
39  
40      private final List<ProxyDef> proxies = new ArrayList<>();
41  
42      /**
43       * Adds the specified proxy definition to the selector. Proxy definitions are ordered, the first matching proxy for
44       * a given repository will be used.
45       *
46       * @param proxy The proxy definition to add, must not be {@code null}.
47       * @param nonProxyHosts The list of (case-insensitive) host names to exclude from proxying, may be {@code null}.
48       * @return This proxy selector for chaining, never {@code null}.
49       */
50      public DefaultProxySelector add(Proxy proxy, String nonProxyHosts) {
51          requireNonNull(proxy, "proxy cannot be null");
52          proxies.add(new ProxyDef(proxy, nonProxyHosts));
53  
54          return this;
55      }
56  
57      public Proxy getProxy(RemoteRepository repository) {
58          requireNonNull(repository, "repository cannot be null");
59          Map<String, ProxyDef> candidates = new HashMap<>();
60  
61          String host = repository.getHost();
62          for (ProxyDef proxy : proxies) {
63              if (!proxy.nonProxyHosts.isNonProxyHost(host)) {
64                  String key = proxy.proxy.getType().toLowerCase(Locale.ENGLISH);
65                  if (!candidates.containsKey(key)) {
66                      candidates.put(key, proxy);
67                  }
68              }
69          }
70  
71          String protocol = repository.getProtocol().toLowerCase(Locale.ENGLISH);
72  
73          if ("davs".equals(protocol)) {
74              protocol = "https";
75          } else if ("dav".equals(protocol)) {
76              protocol = "http";
77          } else if (protocol.startsWith("dav:")) {
78              protocol = protocol.substring("dav:".length());
79          }
80  
81          ProxyDef proxy = candidates.get(protocol);
82          if (proxy == null && "https".equals(protocol)) {
83              proxy = candidates.get("http");
84          }
85  
86          return (proxy != null) ? proxy.proxy : null;
87      }
88  
89      static class NonProxyHosts {
90  
91          private final Pattern[] patterns;
92  
93          NonProxyHosts(String nonProxyHosts) {
94              List<Pattern> patterns = new ArrayList<>();
95              if (nonProxyHosts != null) {
96                  for (StringTokenizer tokenizer = new StringTokenizer(nonProxyHosts, "|"); tokenizer.hasMoreTokens(); ) {
97                      String pattern = tokenizer.nextToken();
98                      pattern = pattern.replace(".", "\\.").replace("*", ".*");
99                      patterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
100                 }
101             }
102             this.patterns = patterns.toArray(new Pattern[0]);
103         }
104 
105         boolean isNonProxyHost(String host) {
106             if (host != null) {
107                 for (Pattern pattern : patterns) {
108                     if (pattern.matcher(host).matches()) {
109                         return true;
110                     }
111                 }
112             }
113             return false;
114         }
115     }
116 
117     static class ProxyDef {
118 
119         final Proxy proxy;
120 
121         final NonProxyHosts nonProxyHosts;
122 
123         ProxyDef(Proxy proxy, String nonProxyHosts) {
124             this.proxy = proxy;
125             this.nonProxyHosts = new NonProxyHosts(nonProxyHosts);
126         }
127     }
128 }