View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.impl.routing;
29  
30  import java.net.InetSocketAddress;
31  import java.net.Proxy;
32  import java.net.ProxySelector;
33  import java.net.URI;
34  import java.net.URISyntaxException;
35  import java.util.List;
36  
37  import org.apache.hc.client5.http.SchemePortResolver;
38  import org.apache.hc.core5.annotation.Contract;
39  import org.apache.hc.core5.annotation.ThreadingBehavior;
40  import org.apache.hc.core5.http.HttpException;
41  import org.apache.hc.core5.http.HttpHost;
42  import org.apache.hc.core5.http.protocol.HttpContext;
43  
44  /**
45   * {@link org.apache.hc.client5.http.routing.HttpRoutePlanner} implementation
46   * based on {@link ProxySelector}. By default, this class will pick up
47   * the proxy settings of the JVM, either from system properties
48   * or from the browser running the application.
49   *
50   * @since 4.3
51   */
52  @Contract(threading = ThreadingBehavior.STATELESS)
53  public class SystemDefaultRoutePlanner extends DefaultRoutePlanner {
54  
55      private final ProxySelector proxySelector;
56  
57      /**
58       * @param proxySelector the proxy selector, or {@code null} for the system default
59       */
60      public SystemDefaultRoutePlanner(
61              final SchemePortResolver schemePortResolver,
62              final ProxySelector proxySelector) {
63          super(schemePortResolver);
64          this.proxySelector = proxySelector;
65      }
66  
67      /**
68       * @param proxySelector the proxy selector, or {@code null} for the system default
69       */
70      public SystemDefaultRoutePlanner(final ProxySelector proxySelector) {
71          this(null, proxySelector);
72      }
73  
74      @Override
75      protected HttpHost determineProxy(final HttpHost target, final HttpContext context) throws HttpException {
76          final URI targetURI;
77          try {
78              targetURI = new URI(target.toURI());
79          } catch (final URISyntaxException ex) {
80              throw new HttpException("Cannot convert host to URI: " + target, ex);
81          }
82          ProxySelector proxySelectorInstance = this.proxySelector;
83          if (proxySelectorInstance == null) {
84              proxySelectorInstance = ProxySelector.getDefault();
85          }
86          if (proxySelectorInstance == null) {
87              //The proxy selector can be "unset", so we must be able to deal with a null selector
88              return null;
89          }
90          final List<Proxy> proxies = proxySelectorInstance.select(targetURI);
91          final Proxy p = chooseProxy(proxies);
92          HttpHost result = null;
93          if (p.type() == Proxy.Type.HTTP) {
94              // convert the socket address to an HttpHost
95              if (!(p.address() instanceof InetSocketAddress)) {
96                  throw new HttpException("Unable to handle non-Inet proxy address: " + p.address());
97              }
98              final InetSocketAddress isa = (InetSocketAddress) p.address();
99              // assume default scheme (http)
100             result = new HttpHost(null, isa.getAddress(), isa.getHostString(), isa.getPort());
101         }
102 
103         return result;
104     }
105 
106     private Proxy chooseProxy(final List<Proxy> proxies) {
107         Proxy result = null;
108         // check the list for one we can use
109         for (int i=0; (result == null) && (i < proxies.size()); i++) {
110             final Proxy p = proxies.get(i);
111             switch (p.type()) {
112 
113             case DIRECT:
114             case HTTP:
115                 result = p;
116                 break;
117 
118             case SOCKS:
119                 // SOCKS hosts are not handled on the route level.
120                 // The socket may make use of the SOCKS host though.
121                 break;
122             }
123         }
124         if (result == null) {
125             //@@@ log as warning or info that only a socks proxy is available?
126             // result can only be null if all proxies are socks proxies
127             // socks proxies are not handled on the route planning level
128             result = Proxy.NO_PROXY;
129         }
130         return result;
131     }
132 
133 }