| 1 | // License: GPL. For details, see LICENSE file.
|
|---|
| 2 | package org.openstreetmap.josm.io;
|
|---|
| 3 |
|
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
|---|
| 5 |
|
|---|
| 6 | import java.io.IOException;
|
|---|
| 7 | import java.net.InetSocketAddress;
|
|---|
| 8 | import java.net.Proxy;
|
|---|
| 9 | import java.net.Proxy.Type;
|
|---|
| 10 | import java.net.ProxySelector;
|
|---|
| 11 | import java.net.SocketAddress;
|
|---|
| 12 | import java.net.URI;
|
|---|
| 13 | import java.util.Arrays;
|
|---|
| 14 | import java.util.Collections;
|
|---|
| 15 | import java.util.HashSet;
|
|---|
| 16 | import java.util.List;
|
|---|
| 17 | import java.util.Set;
|
|---|
| 18 | import java.util.TreeSet;
|
|---|
| 19 |
|
|---|
| 20 | import org.openstreetmap.josm.Main;
|
|---|
| 21 | import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel;
|
|---|
| 22 | import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel.ProxyPolicy;
|
|---|
| 23 |
|
|---|
| 24 | /**
|
|---|
| 25 | * This is the default proxy selector used in JOSM.
|
|---|
| 26 | *
|
|---|
| 27 | */
|
|---|
| 28 | public class DefaultProxySelector extends ProxySelector {
|
|---|
| 29 |
|
|---|
| 30 | private static final List<Proxy> NO_PROXY_LIST = Collections.singletonList(Proxy.NO_PROXY);
|
|---|
| 31 |
|
|---|
| 32 | /**
|
|---|
| 33 | * The {@link ProxySelector} provided by the JDK will retrieve proxy information
|
|---|
| 34 | * from the system settings, if the system property <tt>java.net.useSystemProxies</tt>
|
|---|
| 35 | * is defined <strong>at startup</strong>. It has no effect if the property is set
|
|---|
| 36 | * later by the application.
|
|---|
| 37 | *
|
|---|
| 38 | * We therefore read the property at class loading time and remember it's value.
|
|---|
| 39 | */
|
|---|
| 40 | private static boolean JVM_WILL_USE_SYSTEM_PROXIES = false;
|
|---|
| 41 | static {
|
|---|
| 42 | String v = System.getProperty("java.net.useSystemProxies");
|
|---|
| 43 | if (v != null && v.equals(Boolean.TRUE.toString())) {
|
|---|
| 44 | JVM_WILL_USE_SYSTEM_PROXIES = true;
|
|---|
| 45 | }
|
|---|
| 46 | }
|
|---|
| 47 |
|
|---|
| 48 | /**
|
|---|
| 49 | * The {@link ProxySelector} provided by the JDK will retrieve proxy information
|
|---|
| 50 | * from the system settings, if the system property <tt>java.net.useSystemProxies</tt>
|
|---|
| 51 | * is defined <strong>at startup</strong>. If the property is set later by the application,
|
|---|
| 52 | * this has no effect.
|
|---|
| 53 | *
|
|---|
| 54 | * @return true, if <tt>java.net.useSystemProxies</tt> was set to true at class initialization time
|
|---|
| 55 | *
|
|---|
| 56 | */
|
|---|
| 57 | public static boolean willJvmRetrieveSystemProxies() {
|
|---|
| 58 | return JVM_WILL_USE_SYSTEM_PROXIES;
|
|---|
| 59 | }
|
|---|
| 60 |
|
|---|
| 61 | private ProxyPolicy proxyPolicy;
|
|---|
| 62 | private InetSocketAddress httpProxySocketAddress;
|
|---|
| 63 | private InetSocketAddress socksProxySocketAddress;
|
|---|
| 64 | private ProxySelector delegate;
|
|---|
| 65 |
|
|---|
| 66 | private final Set<String> errorResources = new HashSet<String>();
|
|---|
| 67 | private final Set<String> errorMessages = new HashSet<String>();
|
|---|
| 68 | private Set<String> proxyExceptions;
|
|---|
| 69 |
|
|---|
| 70 | /**
|
|---|
| 71 | * A typical example is:
|
|---|
| 72 | * <pre>
|
|---|
| 73 | * PropertySelector delegate = PropertySelector.getDefault();
|
|---|
| 74 | * PropertySelector.setDefault(new DefaultPropertySelector(delegate));
|
|---|
| 75 | * </pre>
|
|---|
| 76 | *
|
|---|
| 77 | * @param delegate the proxy selector to delegate to if system settings are used. Usually
|
|---|
| 78 | * this is the proxy selector found by ProxySelector.getDefault() before this proxy
|
|---|
| 79 | * selector is installed
|
|---|
| 80 | */
|
|---|
| 81 | public DefaultProxySelector(ProxySelector delegate) {
|
|---|
| 82 | this.delegate = delegate;
|
|---|
| 83 | initFromPreferences();
|
|---|
| 84 | }
|
|---|
| 85 |
|
|---|
| 86 | protected int parseProxyPortValue(String property, String value) {
|
|---|
| 87 | if (value == null) return 0;
|
|---|
| 88 | int port = 0;
|
|---|
| 89 | try {
|
|---|
| 90 | port = Integer.parseInt(value);
|
|---|
| 91 | } catch (NumberFormatException e) {
|
|---|
| 92 | Main.error(tr("Unexpected format for port number in in preference ''{0}''. Got ''{1}''.", property, value));
|
|---|
| 93 | Main.error(tr("The proxy will not be used."));
|
|---|
| 94 | return 0;
|
|---|
| 95 | }
|
|---|
| 96 | if (port <= 0 || port > 65535) {
|
|---|
| 97 | Main.error(tr("Illegal port number in preference ''{0}''. Got {1}.", property, port));
|
|---|
| 98 | Main.error(tr("The proxy will not be used."));
|
|---|
| 99 | return 0;
|
|---|
| 100 | }
|
|---|
| 101 | return port;
|
|---|
| 102 | }
|
|---|
| 103 |
|
|---|
| 104 | /**
|
|---|
| 105 | * Initializes the proxy selector from the setting in the preferences.
|
|---|
| 106 | *
|
|---|
| 107 | */
|
|---|
| 108 | public final void initFromPreferences() {
|
|---|
| 109 | String value = Main.pref.get(ProxyPreferencesPanel.PROXY_POLICY);
|
|---|
| 110 | if (value.length() == 0) {
|
|---|
| 111 | proxyPolicy = ProxyPolicy.NO_PROXY;
|
|---|
| 112 | } else {
|
|---|
| 113 | proxyPolicy = ProxyPolicy.fromName(value);
|
|---|
| 114 | if (proxyPolicy == null) {
|
|---|
| 115 | Main.warn(tr("Unexpected value for preference ''{0}'' found. Got ''{1}''. Will use no proxy.", ProxyPreferencesPanel.PROXY_POLICY, value));
|
|---|
| 116 | proxyPolicy = ProxyPolicy.NO_PROXY;
|
|---|
| 117 | }
|
|---|
| 118 | }
|
|---|
| 119 | String host = Main.pref.get(ProxyPreferencesPanel.PROXY_HTTP_HOST, null);
|
|---|
| 120 | int port = parseProxyPortValue(ProxyPreferencesPanel.PROXY_HTTP_PORT, Main.pref.get(ProxyPreferencesPanel.PROXY_HTTP_PORT, null));
|
|---|
| 121 | httpProxySocketAddress = null;
|
|---|
| 122 | if (proxyPolicy.equals(ProxyPolicy.USE_HTTP_PROXY)) {
|
|---|
| 123 | if (host != null && !host.trim().isEmpty() && port > 0) {
|
|---|
| 124 | httpProxySocketAddress = new InetSocketAddress(host, port);
|
|---|
| 125 | } else {
|
|---|
| 126 | Main.warn(tr("Unexpected parameters for HTTP proxy. Got host ''{0}'' and port ''{1}''.", host, port));
|
|---|
| 127 | Main.warn(tr("The proxy will not be used."));
|
|---|
| 128 | }
|
|---|
| 129 | }
|
|---|
| 130 |
|
|---|
| 131 | host = Main.pref.get(ProxyPreferencesPanel.PROXY_SOCKS_HOST, null);
|
|---|
| 132 | port = parseProxyPortValue(ProxyPreferencesPanel.PROXY_SOCKS_PORT, Main.pref.get(ProxyPreferencesPanel.PROXY_SOCKS_PORT, null));
|
|---|
| 133 | socksProxySocketAddress = null;
|
|---|
| 134 | if (proxyPolicy.equals(ProxyPolicy.USE_SOCKS_PROXY)) {
|
|---|
| 135 | if (host != null && !host.trim().isEmpty() && port > 0) {
|
|---|
| 136 | socksProxySocketAddress = new InetSocketAddress(host,port);
|
|---|
| 137 | } else {
|
|---|
| 138 | Main.warn(tr("Unexpected parameters for SOCKS proxy. Got host ''{0}'' and port ''{1}''.", host, port));
|
|---|
| 139 | Main.warn(tr("The proxy will not be used."));
|
|---|
| 140 | }
|
|---|
| 141 | }
|
|---|
| 142 | proxyExceptions = new HashSet<String>(
|
|---|
| 143 | Main.pref.getCollection(ProxyPreferencesPanel.PROXY_EXCEPTIONS,
|
|---|
| 144 | Arrays.asList(new String[]{"localhost", "127.0.0.1"}))
|
|---|
| 145 | );
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 | @Override
|
|---|
| 149 | public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
|---|
| 150 | // Just log something. The network stack will also throw an exception which will be caught somewhere else
|
|---|
| 151 | Main.error(tr("Connection to proxy ''{0}'' for URI ''{1}'' failed. Exception was: {2}", sa.toString(), uri.toString(), ioe.toString()));
|
|---|
| 152 | // Remember errors to give a friendly user message asking to review proxy configuration
|
|---|
| 153 | errorResources.add(uri.toString());
|
|---|
| 154 | errorMessages.add(ioe.toString());
|
|---|
| 155 | }
|
|---|
| 156 |
|
|---|
| 157 | /**
|
|---|
| 158 | * Returns the set of current proxy resources that failed to be retrieved.
|
|---|
| 159 | * @return the set of current proxy resources that failed to be retrieved
|
|---|
| 160 | * @since 6523
|
|---|
| 161 | */
|
|---|
| 162 | public final Set<String> getErrorResources() {
|
|---|
| 163 | return new TreeSet<String>(errorResources);
|
|---|
| 164 | }
|
|---|
| 165 |
|
|---|
| 166 | /**
|
|---|
| 167 | * Returns the set of current proxy error messages.
|
|---|
| 168 | * @return the set of current proxy error messages
|
|---|
| 169 | * @since 6523
|
|---|
| 170 | */
|
|---|
| 171 | public final Set<String> getErrorMessages() {
|
|---|
| 172 | return new TreeSet<String>(errorMessages);
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 | /**
|
|---|
| 176 | * Clear the sets of failed resources and error messages.
|
|---|
| 177 | * @since 6523
|
|---|
| 178 | */
|
|---|
| 179 | public final void clearErrors() {
|
|---|
| 180 | errorResources.clear();
|
|---|
| 181 | errorMessages.clear();
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | /**
|
|---|
| 185 | * Determines if proxy errors have occured.
|
|---|
| 186 | * @return {@code true} if errors have occured, {@code false} otherwise.
|
|---|
| 187 | * @since 6523
|
|---|
| 188 | */
|
|---|
| 189 | public final boolean hasErrors() {
|
|---|
| 190 | return !errorResources.isEmpty();
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| 193 | @Override
|
|---|
| 194 | public List<Proxy> select(URI uri) {
|
|---|
| 195 | if (uri != null && proxyExceptions.contains(uri.getHost())) {
|
|---|
| 196 | return NO_PROXY_LIST;
|
|---|
| 197 | }
|
|---|
| 198 | switch(proxyPolicy) {
|
|---|
| 199 | case USE_SYSTEM_SETTINGS:
|
|---|
| 200 | if (!JVM_WILL_USE_SYSTEM_PROXIES) {
|
|---|
| 201 | Main.warn(tr("The JVM is not configured to lookup proxies from the system settings. The property ''java.net.useSystemProxies'' was missing at startup time. Will not use a proxy."));
|
|---|
| 202 | return NO_PROXY_LIST;
|
|---|
| 203 | }
|
|---|
| 204 | // delegate to the former proxy selector
|
|---|
| 205 | return delegate.select(uri);
|
|---|
| 206 | case NO_PROXY:
|
|---|
| 207 | return NO_PROXY_LIST;
|
|---|
| 208 | case USE_HTTP_PROXY:
|
|---|
| 209 | if (httpProxySocketAddress == null)
|
|---|
| 210 | return NO_PROXY_LIST;
|
|---|
| 211 | return Collections.singletonList(new Proxy(Type.HTTP, httpProxySocketAddress));
|
|---|
| 212 | case USE_SOCKS_PROXY:
|
|---|
| 213 | if (socksProxySocketAddress == null)
|
|---|
| 214 | return NO_PROXY_LIST;
|
|---|
| 215 | return Collections.singletonList(new Proxy(Type.SOCKS, socksProxySocketAddress));
|
|---|
| 216 | }
|
|---|
| 217 | // should not happen
|
|---|
| 218 | return null;
|
|---|
| 219 | }
|
|---|
| 220 | }
|
|---|