1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.net;
18
19import java.io.IOException;
20import java.util.Collections;
21import java.util.List;
22
23final class ProxySelectorImpl extends ProxySelector {
24
25    @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
26        if (uri == null || sa == null || ioe == null) {
27            throw new IllegalArgumentException();
28        }
29    }
30
31    @Override public List<Proxy> select(URI uri) {
32        return Collections.singletonList(selectOneProxy(uri));
33    }
34
35    private Proxy selectOneProxy(URI uri) {
36        if (uri == null) {
37            throw new IllegalArgumentException("uri == null");
38        }
39        String scheme = uri.getScheme();
40        if (scheme == null) {
41            throw new IllegalArgumentException("scheme == null");
42        }
43
44        int port = -1;
45        Proxy proxy = null;
46        String nonProxyHostsKey = null;
47        boolean httpProxyOkay = true;
48        if ("http".equalsIgnoreCase(scheme)) {
49            port = 80;
50            nonProxyHostsKey = "http.nonProxyHosts";
51            proxy = lookupProxy("http.proxyHost", "http.proxyPort", Proxy.Type.HTTP, port);
52        } else if ("https".equalsIgnoreCase(scheme)) {
53            port = 443;
54            nonProxyHostsKey = "https.nonProxyHosts"; // RI doesn't support this
55            proxy = lookupProxy("https.proxyHost", "https.proxyPort", Proxy.Type.HTTP, port);
56        } else if ("ftp".equalsIgnoreCase(scheme)) {
57            port = 80; // not 21 as you might guess
58            nonProxyHostsKey = "ftp.nonProxyHosts";
59            proxy = lookupProxy("ftp.proxyHost", "ftp.proxyPort", Proxy.Type.HTTP, port);
60        } else if ("socket".equalsIgnoreCase(scheme)) {
61            httpProxyOkay = false;
62        } else {
63            return Proxy.NO_PROXY;
64        }
65
66        if (nonProxyHostsKey != null
67                && isNonProxyHost(uri.getHost(), System.getProperty(nonProxyHostsKey))) {
68            return Proxy.NO_PROXY;
69        }
70
71        if (proxy != null) {
72            return proxy;
73        }
74
75        if (httpProxyOkay) {
76            proxy = lookupProxy("proxyHost", "proxyPort", Proxy.Type.HTTP, port);
77            if (proxy != null) {
78                return proxy;
79            }
80        }
81
82        proxy = lookupProxy("socksProxyHost", "socksProxyPort", Proxy.Type.SOCKS, 1080);
83        if (proxy != null) {
84            return proxy;
85        }
86
87        return Proxy.NO_PROXY;
88    }
89
90    /**
91     * Returns the proxy identified by the {@code hostKey} system property, or
92     * null.
93     */
94    private Proxy lookupProxy(String hostKey, String portKey, Proxy.Type type, int defaultPort) {
95        String host = System.getProperty(hostKey);
96        if (host == null || host.isEmpty()) {
97            return null;
98        }
99
100        int port = getSystemPropertyInt(portKey, defaultPort);
101        return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
102    }
103
104    private int getSystemPropertyInt(String key, int defaultValue) {
105        String string = System.getProperty(key);
106        if (string != null) {
107            try {
108                return Integer.parseInt(string);
109            } catch (NumberFormatException ignored) {
110            }
111        }
112        return defaultValue;
113    }
114
115    /**
116     * Returns true if the {@code nonProxyHosts} system property pattern exists
117     * and matches {@code host}.
118     */
119    private boolean isNonProxyHost(String host, String nonProxyHosts) {
120        if (host == null || nonProxyHosts == null) {
121            return false;
122        }
123
124        // construct pattern
125        StringBuilder patternBuilder = new StringBuilder();
126        for (int i = 0; i < nonProxyHosts.length(); i++) {
127            char c = nonProxyHosts.charAt(i);
128            switch (c) {
129            case '.':
130                patternBuilder.append("\\.");
131                break;
132            case '*':
133                patternBuilder.append(".*");
134                break;
135            default:
136                patternBuilder.append(c);
137            }
138        }
139        // check whether the host is the nonProxyHosts.
140        String pattern = patternBuilder.toString();
141        return host.matches(pattern);
142    }
143}
144