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 defaultthis 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 nullfor 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 nullfor 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 }
148