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.http.impl.conn;
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.http.HttpException;
38  import org.apache.http.HttpHost;
39  import org.apache.http.HttpRequest;
40  import org.apache.http.annotation.Contract;
41  import org.apache.http.annotation.ThreadingBehavior;
42  import org.apache.http.conn.SchemePortResolver;
43  import org.apache.http.protocol.HttpContext;
44  
45  /**
46   * {@link org.apache.http.conn.routing.HttpRoutePlanner} implementation
47   * based on {@link ProxySelector}. By default, this class will pick up
48   * the proxy settings of the JVM, either from system properties
49   * or from the browser running the application.
50   *
51   * @since 4.3
52   */
53  @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
54  public class SystemDefaultRoutePlanner extends DefaultRoutePlanner {
55  
56      private final ProxySelector proxySelector;
57  
58      /**
59       * @param proxySelector the proxy selector, or {@code null} for the system default
60       */
61      public SystemDefaultRoutePlanner(
62              final SchemePortResolver schemePortResolver,
63              final ProxySelector proxySelector) {
64          super(schemePortResolver);
65          this.proxySelector = proxySelector;
66      }
67  
68      /**
69       * @param proxySelector the proxy selector, or {@code null} for the system default
70       */
71      public SystemDefaultRoutePlanner(final ProxySelector proxySelector) {
72          this(null, proxySelector);
73      }
74  
75      @Override
76      protected HttpHost determineProxy(
77              final HttpHost    target,
78              final HttpRequest request,
79              final HttpContext context) throws HttpException {
80          final URI targetURI;
81          try {
82              targetURI = new URI(target.toURI());
83          } catch (final URISyntaxException ex) {
84              throw new HttpException("Cannot convert host to URI: " + target, ex);
85          }
86          ProxySelector proxySelectorInstance = this.proxySelector;
87          if (proxySelectorInstance == null) {
88              proxySelectorInstance = ProxySelector.getDefault();
89          }
90          if (proxySelectorInstance == null) {
91              //The proxy selector can be "unset", so we must be able to deal with a null selector
92              return null;
93          }
94          final List<Proxy> proxies = proxySelectorInstance.select(targetURI);
95          final Proxy p = chooseProxy(proxies);
96          HttpHost result = null;
97          if (p.type() == Proxy.Type.HTTP) {
98              // convert the socket address to an HttpHost
99              if (!(p.address() instanceof InetSocketAddress)) {
100                 throw new HttpException("Unable to handle non-Inet proxy address: " + p.address());
101             }
102             final InetSocketAddress isa = (InetSocketAddress) p.address();
103             // assume default scheme (http)
104             result = new HttpHost(getHost(isa), isa.getPort());
105         }
106 
107         return result;
108     }
109 
110     private String getHost(final InetSocketAddress isa) {
111 
112         //@@@ Will this work with literal IPv6 addresses, or do we
113         //@@@ need to wrap these in [] for the string representation?
114         //@@@ Having it in this method at least allows for easy workarounds.
115        return isa.isUnresolved() ?
116             isa.getHostName() : isa.getAddress().getHostAddress();
117 
118     }
119 
120     private Proxy chooseProxy(final List<Proxy> proxies) {
121         Proxy result = null;
122         // check the list for one we can use
123         for (int i=0; (result == null) && (i < proxies.size()); i++) {
124             final Proxy p = proxies.get(i);
125             switch (p.type()) {
126 
127             case DIRECT:
128             case HTTP:
129                 result = p;
130                 break;
131 
132             case SOCKS:
133                 // SOCKS hosts are not handled on the route level.
134                 // The socket may make use of the SOCKS host though.
135                 break;
136             }
137         }
138         if (result == null) {
139             //@@@ log as warning or info that only a socks proxy is available?
140             // result can only be null if all proxies are socks proxies
141             // socks proxies are not handled on the route planning level
142             result = Proxy.NO_PROXY;
143         }
144         return result;
145     }
146 
147 }