1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* Licensed to the Apache Software Foundation (ASF) under one or more
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements.  See the NOTICE file distributed with
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership.
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License.  You may obtain a copy of the License at
7f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *     http://www.apache.org/licenses/LICENSE-2.0
9f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes *
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License.
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
163b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.net;
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException;
203b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilsonimport java.util.Collections;
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.List;
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
233b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilsonfinal class ProxySelectorImpl extends ProxySelector {
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
253b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
26b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes        if (uri == null || sa == null || ioe == null) {
27b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IllegalArgumentException();
28adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
29adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
313b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    @Override public List<Proxy> select(URI uri) {
323b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        return Collections.singletonList(selectOneProxy(uri));
333b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    }
343b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson
353b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    private Proxy selectOneProxy(URI uri) {
36b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes        if (uri == null) {
37b1396870f92135aa140bd2b86221768dea5bc11dElliott Hughes            throw new IllegalArgumentException("uri == null");
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String scheme = uri.getScheme();
403b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (scheme == null) {
413b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            throw new IllegalArgumentException("scheme == null");
42adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
43adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
443b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        int port = -1;
453b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        Proxy proxy = null;
463b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        String nonProxyHostsKey = null;
473b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        boolean httpProxyOkay = true;
483b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if ("http".equalsIgnoreCase(scheme)) {
493b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            port = 80;
503b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            nonProxyHostsKey = "http.nonProxyHosts";
513b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            proxy = lookupProxy("http.proxyHost", "http.proxyPort", Proxy.Type.HTTP, port);
523b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        } else if ("https".equalsIgnoreCase(scheme)) {
533b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            port = 443;
543b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            nonProxyHostsKey = "https.nonProxyHosts"; // RI doesn't support this
553b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            proxy = lookupProxy("https.proxyHost", "https.proxyPort", Proxy.Type.HTTP, port);
563b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        } else if ("ftp".equalsIgnoreCase(scheme)) {
573b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            port = 80; // not 21 as you might guess
583b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            nonProxyHostsKey = "ftp.nonProxyHosts";
593b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            proxy = lookupProxy("ftp.proxyHost", "ftp.proxyPort", Proxy.Type.HTTP, port);
603b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        } else if ("socket".equalsIgnoreCase(scheme)) {
613b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            httpProxyOkay = false;
623b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        } else {
633b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            return Proxy.NO_PROXY;
64adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
65adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
663b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (nonProxyHostsKey != null
673b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                && isNonProxyHost(uri.getHost(), System.getProperty(nonProxyHostsKey))) {
68adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return Proxy.NO_PROXY;
69adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
70adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
713b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (proxy != null) {
723b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            return proxy;
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
753b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (httpProxyOkay) {
763b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            proxy = lookupProxy("proxyHost", "proxyPort", Proxy.Type.HTTP, port);
773b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            if (proxy != null) {
783b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                return proxy;
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
81adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
823b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        proxy = lookupProxy("socksProxyHost", "socksProxyPort", Proxy.Type.SOCKS, 1080);
833b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (proxy != null) {
843b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            return proxy;
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
873b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        return Proxy.NO_PROXY;
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
89adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
903b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    /**
913b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson     * Returns the proxy identified by the {@code hostKey} system property, or
923b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson     * null.
93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
943b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    private Proxy lookupProxy(String hostKey, String portKey, Proxy.Type type, int defaultPort) {
953b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        String host = System.getProperty(hostKey);
963b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (host == null || host.isEmpty()) {
973b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            return null;
98adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1003b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        int port = getSystemPropertyInt(portKey, defaultPort);
1013b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
102adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1043b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    private int getSystemPropertyInt(String key, int defaultValue) {
1053b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        String string = System.getProperty(key);
1063b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (string != null) {
107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            try {
1083b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                return Integer.parseInt(string);
1093b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            } catch (NumberFormatException ignored) {
110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1123b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        return defaultValue;
113adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1153b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    /**
1163b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson     * Returns true if the {@code nonProxyHosts} system property pattern exists
1173b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson     * and matches {@code host}.
118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project     */
1193b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson    private boolean isNonProxyHost(String host, String nonProxyHosts) {
1203b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        if (host == null || nonProxyHosts == null) {
1213b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            return false;
122adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
123adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1243b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        // construct pattern
1253b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        StringBuilder patternBuilder = new StringBuilder();
1263b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        for (int i = 0; i < nonProxyHosts.length(); i++) {
1273b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            char c = nonProxyHosts.charAt(i);
1283b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            switch (c) {
1293b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            case '.':
1303b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                patternBuilder.append("\\.");
1313b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                break;
1323b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            case '*':
1333b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                patternBuilder.append(".*");
1343b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                break;
1353b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson            default:
1363b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson                patternBuilder.append(c);
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1393b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        // check whether the host is the nonProxyHosts.
1403b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        String pattern = patternBuilder.toString();
1413b0348d1081a4645aa38da97ee299fadc040c1b4Jesse Wilson        return host.matches(pattern);
142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
144