+ * Default implementation simply ignores the parameters. + * + * @param host Proxy host + * @param port Proxy port + * @param type Proxy type (SOCKS or HTTP) + */ + public void setProxy(String host, int port, Proxy.Type type) { + } + + /** + * Removes the proxy server so all the subsequent requests will be made directly again. + *
+ * Default implementation does nothing. + */ + public void removeProxy() { + } /** * Removes HTTP request execute intercepters of the given class or subclasses. @@ -198,6 +219,15 @@ public boolean supportsPatch() { return false; } + + /** + * Returns whether this HTTP transport implementation supports proxies. + *
+ * Default implementation returns {@code false}. + */ + public boolean supportsProxy() { + return false; + } /** * Builds a {@code DELETE} request. Index: google-api-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java =================================================================== --- a/google-api-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java +++ b/google-api-client/src/main/java/com/google/api/client/http/apache/ApacheHttpTransport.java @@ -14,8 +14,11 @@ package com.google.api.client.http.apache; +import com.google.api.client.http.apache.PlainSocksSocketFactory; +import com.google.api.client.http.apache.SSLSocksSocketFactory; import com.google.api.client.http.HttpTransport; +import org.apache.http.HttpHost; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; @@ -24,17 +27,17 @@ import org.apache.http.client.methods.HttpPut; import org.apache.http.client.params.ClientPNames; import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; +import java.net.Proxy; + /** * HTTP transport based on the Apache HTTP Client library. *
@@ -42,9 +45,8 @@ *
*+ * The following parameters can be used to customize the behavior of this class: + *
null
) the function simply
+ * returns a new instance of {@link Socket} class using default constructor.
+ *
+ * @return the new socket
+ */
+ public Socket createSocket(final HttpParams params) {
+ Socket sock;
+ if (params == null) {
+ sock = new Socket();
+ } else {
+ String proxyHost = (String) params.getParameter("socks.host");
+ Integer proxyPort = (Integer) params.getParameter("socks.port");
+ if (proxyHost != null && proxyPort != null) {
+ InetSocketAddress socksaddr = new InetSocketAddress(proxyHost, proxyPort);
+ Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
+ sock = new Socket(proxy);
+ } else {
+ sock = new Socket();
+ }
+ }
+ return sock;
+ }
+
+ public Socket connectSocket(final Socket socket, final InetSocketAddress remoteAddress,
+ final InetSocketAddress localAddress, final HttpParams params) throws IOException,
+ ConnectTimeoutException {
+ if (remoteAddress == null) {
+ throw new IllegalArgumentException("Remote address may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ Socket sock = socket != null ? socket : createSocket(params);
+ if (localAddress != null) {
+ sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
+ sock.bind(localAddress);
+ }
+ int timeout = HttpConnectionParams.getConnectionTimeout(params);
+ try {
+ sock.connect(remoteAddress, timeout);
+ } catch (SocketTimeoutException ex) {
+ throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
+ + remoteAddress.getAddress() + " timed out");
+ }
+ return sock;
+ }
+
+ /**
+ * Checks whether a socket connection is secure. This factory creates plain socks socket
+ * connections which are not considered secure.
+ *
+ * @param sock the connected socket
+ *
+ * @return false
+ *
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public final boolean isSecure(Socket sock) throws IllegalArgumentException {
+ if (sock == null) {
+ throw new IllegalArgumentException("Socket may not be null.");
+ }
+ // This check is performed last since it calls a method implemented
+ // by the argument object. getClass() is final in java.lang.Object.
+ if (sock.isClosed()) {
+ throw new IllegalArgumentException("Socket is closed.");
+ }
+ return false;
+ }
+
+}
Index: google-api-client/src/main/java/com/google/api/client/http/apache/SSLSocksSocketFactory.java
===================================================================
new file mode 100644
--- /dev/null
+++ b/google-api-client/src/main/java/com/google/api/client/http/apache/SSLSocksSocketFactory.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache;
+
+import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.scheme.LayeredSchemeSocketFactory;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+@ThreadSafe
+public class SSLSocksSocketFactory implements LayeredSchemeSocketFactory {
+
+ public static final String TLS = "TLS";
+ public static final String SSL = "SSL";
+ public static final String SSLV2 = "SSLv2";
+
+ public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER =
+ new AllowAllHostnameVerifier();
+
+ public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER =
+ new BrowserCompatHostnameVerifier();
+
+ public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER = new StrictHostnameVerifier();
+
+ /**
+ * Gets the default factory, which uses the default JVM settings for secure connections.
+ *
+ * @return the default factory
+ */
+ public static SSLSocksSocketFactory getSocketFactory() {
+ return new SSLSocksSocketFactory();
+ }
+
+ private final javax.net.ssl.SSLSocketFactory socketfactory;
+ private final X509HostnameVerifier hostnameVerifier;
+
+ private static SSLContext createSSLContext(String algorithm, final KeyStore keystore,
+ final String keystorePassword, final KeyStore truststore, final SecureRandom random,
+ final TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyStoreException,
+ UnrecoverableKeyException, KeyManagementException {
+ if (algorithm == null) {
+ algorithm = TLS;
+ }
+ KeyManagerFactory kmfactory =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray() : null);
+ KeyManager[] keymanagers = kmfactory.getKeyManagers();
+ TrustManagerFactory tmfactory =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmfactory.init(truststore);
+ TrustManager[] trustmanagers = tmfactory.getTrustManagers();
+ if (trustmanagers != null && trustStrategy != null) {
+ for (int i = 0; i < trustmanagers.length; i++) {
+ TrustManager tm = trustmanagers[i];
+ if (tm instanceof X509TrustManager) {
+ trustmanagers[i] = new TrustManagerDecorator((X509TrustManager) tm, trustStrategy);
+ }
+ }
+ }
+
+ SSLContext sslcontext = SSLContext.getInstance(algorithm);
+ sslcontext.init(keymanagers, trustmanagers, random);
+ return sslcontext;
+ }
+
+ private static SSLContext createDefaultSSLContext() {
+ try {
+ return createSSLContext(TLS, null, null, null, null, null);
+ } catch (Exception ex) {
+ throw new IllegalStateException("Failure initializing default SSL context", ex);
+ }
+ }
+
+ public SSLSocksSocketFactory(final SSLContext sslContext) {
+ this(sslContext, BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
+ }
+
+ public SSLSocksSocketFactory(final SSLContext sslContext,
+ final X509HostnameVerifier hostnameVerifier) {
+ super();
+ this.socketfactory = sslContext.getSocketFactory();
+ this.hostnameVerifier = hostnameVerifier;
+ }
+
+ private SSLSocksSocketFactory() {
+ this(createDefaultSSLContext());
+ }
+
+ /**
+ * @param params If the parameters contain values for socks.host and socks.port they are used as
+ * the socket server. If the values are not set (or set to null
) the function
+ * simply returns a new instance of {@link Socket} class using default constructor.
+ *
+ * @return the new socket
+ */
+ public Socket createSocket(final HttpParams params) {
+ Socket sock;
+ if (params == null) {
+ sock = new Socket();
+ } else {
+ String proxyHost = (String) params.getParameter("socks.host");
+ Integer proxyPort = (Integer) params.getParameter("socks.port");
+ if (proxyHost != null && proxyPort != null) {
+ InetSocketAddress socksaddr = new InetSocketAddress(proxyHost, proxyPort);
+ Proxy proxy = new Proxy(Proxy.Type.SOCKS, socksaddr);
+ sock = new Socket(proxy);
+ } else {
+ sock = new Socket();
+ }
+ }
+ return sock;
+ }
+
+ public Socket connectSocket(final Socket socket, final InetSocketAddress remoteAddress,
+ final InetSocketAddress localAddress, final HttpParams params) throws IOException,
+ UnknownHostException, ConnectTimeoutException {
+ if (remoteAddress == null) {
+ throw new IllegalArgumentException("Remote address may not be null");
+ }
+ if (params == null) {
+ throw new IllegalArgumentException("HTTP parameters may not be null");
+ }
+ Socket sock = socket != null ? socket : createSocket(params);
+ if (localAddress != null) {
+ sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
+ sock.bind(localAddress);
+ }
+
+ int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
+ int soTimeout = HttpConnectionParams.getSoTimeout(params);
+
+ try {
+ sock.connect(remoteAddress, connTimeout);
+ } catch (SocketTimeoutException ex) {
+ throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
+ + remoteAddress.getAddress() + " timed out");
+ }
+ sock.setSoTimeout(soTimeout);
+ SSLSocket sslsock;
+ // Setup SSL layering if necessary
+ if (sock instanceof SSLSocket) {
+ sslsock = (SSLSocket) sock;
+ } else {
+ sslsock =
+ (SSLSocket) this.socketfactory.createSocket(sock, remoteAddress.getHostName(),
+ remoteAddress.getPort(), true);
+ }
+ if (this.hostnameVerifier != null) {
+ try {
+ this.hostnameVerifier.verify(remoteAddress.getHostName(), sslsock);
+ // verifyHostName() didn't blowup - good!
+ } catch (IOException iox) {
+ // close the socket before re-throwing the exception
+ try {
+ sslsock.close();
+ } catch (Exception x) { /* ignore */
+ }
+ throw iox;
+ }
+ }
+ return sslsock;
+ }
+
+
+ /**
+ * Checks whether a socket connection is secure. This factory creates TLS/SSL socket connections
+ * which, by default, are considered secure. true
+ *
+ * @throws IllegalArgumentException if the argument is invalid
+ */
+ public boolean isSecure(final Socket sock) throws IllegalArgumentException {
+ if (sock == null) {
+ throw new IllegalArgumentException("Socket may not be null");
+ }
+ // This instanceof check is in line with createSocket() above.
+ if (!(sock instanceof SSLSocket)) {
+ throw new IllegalArgumentException("Socket not created by this factory");
+ }
+ // This check is performed last since it calls the argument object.
+ if (sock.isClosed()) {
+ throw new IllegalArgumentException("Socket is closed");
+ }
+ return true;
+ }
+
+ public Socket createLayeredSocket(final Socket socket, final String host, final int port,
+ final boolean autoClose) throws IOException, UnknownHostException {
+ SSLSocket sslSocket =
+ (SSLSocket) this.socketfactory.createSocket(socket, host, port, autoClose);
+ if (this.hostnameVerifier != null) {
+ this.hostnameVerifier.verify(host, sslSocket);
+ }
+ // verifyHostName() didn't blowup - good!
+ return sslSocket;
+ }
+
+ public X509HostnameVerifier getHostnameVerifier() {
+ return this.hostnameVerifier;
+ }
+}
Index: google-api-client/src/main/java/com/google/api/client/http/apache/TrustManagerDecorator.java
===================================================================
new file mode 100644
--- /dev/null
+++ b/google-api-client/src/main/java/com/google/api/client/http/apache/TrustManagerDecorator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.api.client.http.apache;
+
+import org.apache.http.conn.ssl.TrustStrategy;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509TrustManager;
+
+class TrustManagerDecorator implements X509TrustManager {
+
+ private final X509TrustManager trustManager;
+ private final TrustStrategy trustStrategy;
+
+ TrustManagerDecorator(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
+ super();
+ this.trustManager = trustManager;
+ this.trustStrategy = trustStrategy;
+ }
+
+ public void checkClientTrusted(
+ final X509Certificate[] chain, final String authType) throws CertificateException {
+ this.trustManager.checkClientTrusted(chain, authType);
+ }
+
+ public void checkServerTrusted(
+ final X509Certificate[] chain, final String authType) throws CertificateException {
+ if (!this.trustStrategy.isTrusted(chain, authType)) {
+ this.trustManager.checkServerTrusted(chain, authType);
+ }
+ }
+
+ public X509Certificate[] getAcceptedIssuers() {
+ return this.trustManager.getAcceptedIssuers();
+ }
+
+}