/******************************************************************************* * Copyright (c) 2011, 2014 Tasktop Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.commons.core.net; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Proxy.Type; import java.net.ProxySelector; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.SSLSocket; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.commons.core.operations.ICancellable; import org.eclipse.mylyn.commons.core.operations.MonitoredOperation; import org.eclipse.mylyn.internal.commons.core.CommonsCorePlugin; import org.eclipse.osgi.util.NLS; /** * Provides network access related utility methods. * * @since 3.7 * @author Steffen Pingel */ public class NetUtil { private static final String PROPERTY_MAX_HTTP_HOST_CONNECTIONS = "org.eclipse.mylyn.http.connections.per.host"; //$NON-NLS-1$ private static final String PROPERTY_MAX_HTTP_TOTAL_CONNECTIONS = "org.eclipse.mylyn.http.total.connections"; //$NON-NLS-1$ private static final int HTTPS_PORT = 443; private static final int HTTP_PORT = 80; private static final int MAX_HTTP_HOST_CONNECTIONS_DEFAULT = 100; private static final int MAX_HTTP_TOTAL_CONNECTIONS_DEFAULT = 1000; private final static String[] enabledProtocols; private final static AtomicBoolean loggedEnabledProtocolsException = new AtomicBoolean(); static { String value = System.getProperty("org.eclipse.mylyn.https.protocols"); //$NON-NLS-1$ enabledProtocols = (value != null) ? value.split(",") : null; //$NON-NLS-1$ } /** * Invokes {@link Socket#connect(java.net.SocketAddress, int)} on <code>socket</code> to connect to * <code>address</code>. * <p> * If an operation is provided a cancellation listener is attached that aborts the connect in case the operation is * aborted while connecting. * * @param socket * the socket * @param address * the address to connect to * @param timeout * the connect timeout * @param operation * the current operation or null * @throws IOException * @see {@link Socket#connect(java.net.SocketAddress, int)} * @deprecated */ @Deprecated public static void connect(final Socket socket, InetSocketAddress address, int timeout, MonitoredOperation<?> operation) throws IOException { if (operation != null) { ICancellable listener = new ICancellable() { public void abort() { try { socket.close(); } catch (IOException e) { // ignore } } }; try { operation.addListener(listener); socket.connect(address, timeout); } finally { operation.removeListener(listener); } } else { socket.connect(address, timeout); } } public static Proxy createProxy(String proxyHost, int proxyPort) { return createProxy(proxyHost, proxyPort, null, null, null); } public static Proxy createProxy(String proxyHost, int proxyPort, String username, String password, String domain) { if (proxyHost != null && proxyHost.length() > 0) { InetSocketAddress sockAddr = new InetSocketAddress(proxyHost, proxyPort); boolean authenticated = (username != null && password != null && username.length() > 0 && password.length() > 0); if (authenticated) { return new AuthenticatedProxy(Type.HTTP, sockAddr, username, password, domain); } else { return new Proxy(Type.HTTP, sockAddr); } } return Proxy.NO_PROXY; } /** * Returns the host portion of <code>url</code>. * * @return the host portion of <code>url</code>; empty string, if url is not valid * @since 3.7 */ public static String getHost(String url) { Assert.isNotNull(url); String result = url; int colonSlashSlash = url.indexOf("://"); //$NON-NLS-1$ if (colonSlashSlash >= 0) { result = url.substring(colonSlashSlash + 3); } int colonPort = result.indexOf(':'); int requestPath = result.indexOf('/'); int substringEnd; // minimum positive, or string length if (colonPort > 0 && requestPath > 0) { substringEnd = Math.min(colonPort, requestPath); } else if (colonPort > 0) { substringEnd = colonPort; } else if (requestPath > 0) { substringEnd = requestPath; } else { substringEnd = result.length(); } return result.substring(0, substringEnd); } /** * Returns the connection port for <code>url</code>. If no port is specified, 443 is returned for URLs that use the * https protocol; otherwise, 80 is returned. * * @return the port portion of <code>url</code> * @throws NumberFormatException * if the port is not a parseable integer * @since 3.7 */ public static int getPort(String url) { Assert.isNotNull(url); int colonSlashSlash = url.indexOf("://"); //$NON-NLS-1$ int firstSlash = url.indexOf("/", colonSlashSlash + 3); //$NON-NLS-1$ int colonPort = url.indexOf(':', colonSlashSlash + 1); if (firstSlash == -1) { firstSlash = url.length(); } if (colonPort < 0 || colonPort > firstSlash) { return isUrlHttps(url) ? HTTPS_PORT : HTTP_PORT; } int requestPath = url.indexOf('/', colonPort + 1); int end = requestPath < 0 ? url.length() : requestPath; String port = url.substring(colonPort + 1, end); if (port.length() == 0) { return isUrlHttps(url) ? HTTPS_PORT : HTTP_PORT; } return Integer.parseInt(port); } public static Proxy getProxy(String host, Proxy.Type proxyType) { Assert.isNotNull(host); Assert.isNotNull(proxyType); return getProxy(host, getPlatformProxyType(proxyType)); } @SuppressWarnings("deprecation") public static Proxy getProxy(String host, String proxyType) { Assert.isNotNull(host); Assert.isNotNull(proxyType); IProxyService service = CommonsCorePlugin.getProxyService(); if (service != null && service.isProxiesEnabled()) { // TODO e3.5 move to new proxy API IProxyData data = service.getProxyDataForHost(host, proxyType); if (data != null && data.getHost() != null) { String proxyHost = data.getHost(); int proxyPort = data.getPort(); // change the IProxyData default port to the Java default port if (proxyPort == -1) { proxyPort = 0; } return createProxy(proxyHost, proxyPort, data.getUserId(), data.getPassword(), null); } } else { try { // fall back to JDK proxy selector URI uri = new URI(proxyType, "//" + host, null); //$NON-NLS-1$ List<Proxy> proxies = ProxySelector.getDefault().select(uri); if (proxies != null && proxies.size() > 0) { Proxy proxy = proxies.iterator().next(); if (proxy != Proxy.NO_PROXY) { return proxy; } } } catch (URISyntaxException e) { // ignore } } return null; } /** * Returns the platform default proxy for <code>url</code> or <code>null</code> if none. */ public static Proxy getProxyForUrl(String url) { String host = getHost(url); Proxy proxy; if (isUrlHttps(url)) { proxy = getProxy(host, IProxyData.HTTPS_PROXY_TYPE); } else { proxy = getProxy(host, IProxyData.HTTP_PROXY_TYPE); } return proxy; } /** * Returns the request path part of <code>url</code>. * * @return the request path portion of <code>url</code>; empty string, if url is not valid or not path is specified * @since 3.7 */ public static String getRequestPath(String url) { int colonSlashSlash = url.indexOf("://"); //$NON-NLS-1$ int requestPath = url.indexOf('/', colonSlashSlash + 3); if (requestPath < 0) { return ""; //$NON-NLS-1$ } else { return url.substring(requestPath); } } /** * Returns true if <code>url</code> uses https as the protocol. * * @since 3.7 */ public static boolean isUrlHttps(String url) { return url.matches("https.*"); //$NON-NLS-1$ } private static String getPlatformProxyType(Type type) { return type == Type.SOCKS ? IProxyData.SOCKS_PROXY_TYPE : IProxyData.HTTP_PROXY_TYPE; } public static Socket configureSocket(Socket socket) { if (socket instanceof SSLSocket && enabledProtocols != null) { try { ((SSLSocket) socket).setEnabledProtocols(enabledProtocols); } catch (IllegalArgumentException e) { if (!loggedEnabledProtocolsException.getAndSet(true)) { StatusHandler.log(new Status(IStatus.ERROR, CommonsCorePlugin.ID_PLUGIN, NLS.bind( "Failed to configure SSL protocols ''{0}''", Arrays.toString(enabledProtocols)))); //$NON-NLS-1$ } } } return socket; } /** * @since 3.12 */ public static int getMaxHttpConnectionsPerHost() { return getSystemPropertyAndParseInt(PROPERTY_MAX_HTTP_HOST_CONNECTIONS, MAX_HTTP_HOST_CONNECTIONS_DEFAULT); } /** * @since 3.12 */ public static int getMaxHttpConnections() { return getSystemPropertyAndParseInt(PROPERTY_MAX_HTTP_TOTAL_CONNECTIONS, MAX_HTTP_TOTAL_CONNECTIONS_DEFAULT); } private static int getSystemPropertyAndParseInt(String key, int defaultValue) { String property = System.getProperty(key); if (property != null) { try { return Integer.parseInt(property); } catch (NumberFormatException e) { StatusHandler.log(new Status(IStatus.WARNING, CommonsCorePlugin.ID_PLUGIN, NLS.bind( "Unable to parse property {0}", key))); //$NON-NLS-1$ } } return defaultValue; } }