1a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath/* 2a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Copyright (C) 2007 The Android Open Source Project 3a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 4a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 5a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * you may not use this file except in compliance with the License. 6a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * You may obtain a copy of the License at 7a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 8a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 9a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 10a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Unless required by applicable law or agreed to in writing, software 11a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 12a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * See the License for the specific language governing permissions and 14a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * limitations under the License. 15a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 16a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 17a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathpackage android.net.http; 18a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 19a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport android.content.Context; 20a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport android.util.Log; 21a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport com.android.org.conscrypt.FileClientSessionCache; 22a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport com.android.org.conscrypt.OpenSSLContextImpl; 23a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport com.android.org.conscrypt.SSLClientSessionCache; 24a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.Header; 25a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.HttpException; 26a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.HttpHost; 27a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.HttpStatus; 28a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.ParseException; 29a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.ProtocolVersion; 30a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.StatusLine; 31a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.message.BasicHttpRequest; 32a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.params.BasicHttpParams; 33a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.params.HttpConnectionParams; 34a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport org.apache.http.params.HttpParams; 35a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 36a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport javax.net.ssl.SSLException; 37a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport javax.net.ssl.SSLSocket; 38a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport javax.net.ssl.SSLSocketFactory; 39a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport javax.net.ssl.TrustManager; 40a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport javax.net.ssl.X509TrustManager; 41a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.io.File; 42a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.io.IOException; 43a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.net.Socket; 44a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.security.KeyManagementException; 45a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.security.cert.X509Certificate; 46a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathimport java.util.Locale; 47a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 48a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath/** 49a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * A Connection connecting to a secure http server or tunneling through 50a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * a http proxy server to a https server. 51a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 52a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathpublic class HttpsConnection extends Connection { 53a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 54a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 55a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * SSL socket factory 56a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 57a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private static SSLSocketFactory mSslSocketFactory = null; 58a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 59a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath static { 60a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // This initialization happens in the zygote. It triggers some 61a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // lazy initialization that can will benefit later invocations of 62a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // initializeEngine(). 63a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath initializeEngine(null); 64a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 65a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 66a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 67a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @param sessionDir directory to cache SSL sessions 68a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 69a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public static void initializeEngine(File sessionDir) { 70a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 71a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath SSLClientSessionCache cache = null; 72a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (sessionDir != null) { 73a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Log.d("HttpsConnection", "Caching SSL sessions in " 74a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath + sessionDir + "."); 75a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath cache = FileClientSessionCache.usingDirectory(sessionDir); 76a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 77a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 78a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath OpenSSLContextImpl sslContext = OpenSSLContextImpl.getPreferred(); 79a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 80a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // here, trust managers is a single trust-all manager 81a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath TrustManager[] trustManagers = new TrustManager[] { 82a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath new X509TrustManager() { 83a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public X509Certificate[] getAcceptedIssuers() { 84a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return null; 85a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 86a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 87a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void checkClientTrusted( 88a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath X509Certificate[] certs, String authType) { 89a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 90a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 91a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public void checkServerTrusted( 92a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath X509Certificate[] certs, String authType) { 93a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 94a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 95a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath }; 96a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 97a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslContext.engineInit(null, trustManagers, null); 98a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslContext.engineGetClientSessionContext().setPersistentCache(cache); 99a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 100a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath synchronized (HttpsConnection.class) { 101a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mSslSocketFactory = sslContext.engineGetSocketFactory(); 102a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 103a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (KeyManagementException e) { 104a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new RuntimeException(e); 105a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (IOException e) { 106a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new RuntimeException(e); 107a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 108a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 109a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 110a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private synchronized static SSLSocketFactory getSocketFactory() { 111a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return mSslSocketFactory; 112a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 113a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 114a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 115a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Object to wait on when suspending the SSL connection 116a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 117a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private Object mSuspendLock = new Object(); 118a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 119a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 120a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * True if the connection is suspended pending the result of asking the 121a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * user about an error. 122a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 123a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private boolean mSuspended = false; 124a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 125a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 126a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * True if the connection attempt should be aborted due to an ssl 127a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * error. 128a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 129a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private boolean mAborted = false; 130a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 131a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // Used when connecting through a proxy. 132a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath private HttpHost mProxyHost; 133a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 134a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 135a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Contructor for a https connection. 136a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 137a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpsConnection(Context context, HttpHost host, HttpHost proxy, 138a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath RequestFeeder requestFeeder) { 139a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath super(context, host, requestFeeder); 140a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mProxyHost = proxy; 141a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 142a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 143a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 144a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Sets the server SSL certificate associated with this 145a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * connection. 146a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @param certificate The SSL certificate 147a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 148a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /* package */ void setCertificate(SslCertificate certificate) { 149a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mCertificate = certificate; 150a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 151a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 152a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 153a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Opens the connection to a http server or proxy. 154a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 155a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @return the opened low level connection 156a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * @throws IOException if the connection fails for any reason. 157a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 158a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath @Override 159a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath AndroidHttpClientConnection openConnection(Request req) throws IOException { 160a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath SSLSocket sslSock = null; 161a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 162a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mProxyHost != null) { 163a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // If we have a proxy set, we first send a CONNECT request 164a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // to the proxy; if the proxy returns 200 OK, we negotiate 165a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // a secure connection to the target server via the proxy. 166a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // If the request fails, we drop it, but provide the event 167a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // handler with the response status and headers. The event 168a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // handler is then responsible for cancelling the load or 169a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // issueing a new request. 170a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath AndroidHttpClientConnection proxyConnection = null; 171a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Socket proxySock = null; 172a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 173a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxySock = new Socket 174a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath (mProxyHost.getHostName(), mProxyHost.getPort()); 175a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 176a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxySock.setSoTimeout(60 * 1000); 177a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 178a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyConnection = new AndroidHttpClientConnection(); 179a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpParams params = new BasicHttpParams(); 180a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpConnectionParams.setSocketBufferSize(params, 8192); 181a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 182a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyConnection.bind(proxySock, params); 183a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch(IOException e) { 184a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (proxyConnection != null) { 185a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyConnection.close(); 186a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 187a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 188a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String errorMessage = e.getMessage(); 189a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (errorMessage == null) { 190a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath errorMessage = 191a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath "failed to establish a connection to the proxy"; 192a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 193a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 194a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException(errorMessage); 195a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 196a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 197a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath StatusLine statusLine = null; 198a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath int statusCode = 0; 199a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath Headers headers = new Headers(); 200a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 201a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath BasicHttpRequest proxyReq = new BasicHttpRequest 202a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath ("CONNECT", mHost.toHostString()); 203a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 204a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // add all 'proxy' headers from the original request, we also need 205a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // to add 'host' header unless we want proxy to answer us with a 206a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // 400 Bad Request 207a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath for (Header h : req.mHttpRequest.getAllHeaders()) { 208a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String headerName = h.getName().toLowerCase(Locale.ROOT); 209a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (headerName.startsWith("proxy") || headerName.equals("keep-alive") 210a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath || headerName.equals("host")) { 211a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyReq.addHeader(h); 212a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 213a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 214a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 215a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyConnection.sendRequestHeader(proxyReq); 216a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyConnection.flush(); 217a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 218a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // it is possible to receive informational status 219a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // codes prior to receiving actual headers; 220a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // all those status codes are smaller than OK 200 221a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // a loop is a standard way of dealing with them 222a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath do { 223a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath statusLine = proxyConnection.parseResponseHeader(headers); 224a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath statusCode = statusLine.getStatusCode(); 225a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } while (statusCode < HttpStatus.SC_OK); 226a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (ParseException e) { 227a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String errorMessage = e.getMessage(); 228a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (errorMessage == null) { 229a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath errorMessage = 230a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath "failed to send a CONNECT request"; 231a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 232a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 233a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException(errorMessage); 234a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (HttpException e) { 235a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String errorMessage = e.getMessage(); 236a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (errorMessage == null) { 237a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath errorMessage = 238a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath "failed to send a CONNECT request"; 239a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 240a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 241a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException(errorMessage); 242a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (IOException e) { 243a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String errorMessage = e.getMessage(); 244a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (errorMessage == null) { 245a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath errorMessage = 246a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath "failed to send a CONNECT request"; 247a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 248a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 249a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException(errorMessage); 250a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 251a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 252a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (statusCode == HttpStatus.SC_OK) { 253a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 254a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslSock = (SSLSocket) getSocketFactory().createSocket( 255a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxySock, mHost.getHostName(), mHost.getPort(), true); 256a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch(IOException e) { 257a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (sslSock != null) { 258a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslSock.close(); 259a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 260a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 261a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String errorMessage = e.getMessage(); 262a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (errorMessage == null) { 263a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath errorMessage = 264a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath "failed to create an SSL socket"; 265a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 266a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException(errorMessage); 267a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 268a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } else { 269a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // if the code is not OK, inform the event handler 270a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath ProtocolVersion version = statusLine.getProtocolVersion(); 271a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 272a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath req.mEventHandler.status(version.getMajor(), 273a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath version.getMinor(), 274a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath statusCode, 275a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath statusLine.getReasonPhrase()); 276a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath req.mEventHandler.headers(headers); 277a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath req.mEventHandler.endData(); 278a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 279a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath proxyConnection.close(); 280a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 281a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // here, we return null to indicate that the original 282a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // request needs to be dropped 283a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return null; 284a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 285a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } else { 286a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // if we do not have a proxy, we simply connect to the host 287a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 288a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslSock = (SSLSocket) getSocketFactory().createSocket( 289a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHost.getHostName(), mHost.getPort()); 290a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslSock.setSoTimeout(SOCKET_TIMEOUT); 291a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch(IOException e) { 292a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (sslSock != null) { 293a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslSock.close(); 294a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 295a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 296a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String errorMessage = e.getMessage(); 297a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (errorMessage == null) { 298a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath errorMessage = "failed to create an SSL socket"; 299a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 300a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 301a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException(errorMessage); 302a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 303a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 304a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 305a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // do handshake and validate server certificates 306a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath SslError error = CertificateChainValidator.getInstance(). 307a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName()); 308a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 309a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // Inform the user if there is a problem 310a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (error != null) { 311a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // handleSslErrorRequest may immediately unsuspend if it wants to 312a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // allow the certificate anyway. 313a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // So we mark the connection as suspended, call handleSslErrorRequest 314a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // then check if we're still suspended and only wait if we actually 315a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // need to. 316a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath synchronized (mSuspendLock) { 317a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mSuspended = true; 318a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 319a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // don't hold the lock while calling out to the event handler 320a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath boolean canHandle = req.getEventHandler().handleSslErrorRequest(error); 321a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if(!canHandle) { 322a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new IOException("failed to handle "+ error); 323a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 324a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath synchronized (mSuspendLock) { 325a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mSuspended) { 326a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 327a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // Put a limit on how long we are waiting; if the timeout 328a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // expires (which should never happen unless you choose 329a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // to ignore the SSL error dialog for a very long time), 330a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // we wake up the thread and abort the request. This is 331a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // to prevent us from stalling the network if things go 332a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // very bad. 333a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mSuspendLock.wait(10 * 60 * 1000); 334a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mSuspended) { 335a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // mSuspended is true if we have not had a chance to 336a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // restart the connection yet (ie, the wait timeout 337a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // has expired) 338a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mSuspended = false; 339a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mAborted = true; 340a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 341a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("HttpsConnection.openConnection():" + 342a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath " SSL timeout expired and request was cancelled!!!"); 343a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 344a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 345a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (InterruptedException e) { 346a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // ignore 347a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 348a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 349a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mAborted) { 350a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // The user decided not to use this unverified connection 351a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // so close it immediately. 352a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath sslSock.close(); 353a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath throw new SSLConnectionClosedByUserException("connection closed by the user"); 354a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 355a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 356a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 357a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 358a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // All went well, we have an open, verified connection. 359a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath AndroidHttpClientConnection conn = new AndroidHttpClientConnection(); 360a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath BasicHttpParams params = new BasicHttpParams(); 361a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192); 362a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath conn.bind(sslSock, params); 363a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 364a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return conn; 365a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 366a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 367a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 368a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Closes the low level connection. 369a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 370a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * If an exception is thrown then it is assumed that the connection will 371a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * have been closed (to the extent possible) anyway and the caller does not 372a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * need to take any further action. 373a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 374a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 375a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath @Override 376a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath void closeConnection() { 377a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // if the connection has been suspended due to an SSL error 378a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mSuspended) { 379a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath // wake up the network thread 380a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath restartConnection(false); 381a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 382a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 383a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath try { 384a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) { 385a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mHttpClientConnection.close(); 386a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 387a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } catch (IOException e) { 388a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) 389a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("HttpsConnection.closeConnection():" + 390a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath " failed closing connection " + mHost); 391a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath e.printStackTrace(); 392a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 393a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 394a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 395a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath /** 396a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Restart a secure connection suspended waiting for user interaction. 397a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 398a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath void restartConnection(boolean proceed) { 399a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (HttpLog.LOGV) { 400a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath HttpLog.v("HttpsConnection.restartConnection():" + 401a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath " proceed: " + proceed); 402a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 403a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 404a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath synchronized (mSuspendLock) { 405a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath if (mSuspended) { 406a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mSuspended = false; 407a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mAborted = !proceed; 408a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath mSuspendLock.notify(); 409a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 410a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 411a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 412a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 413a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath @Override 414a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath String getScheme() { 415a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath return "https"; 416a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 417a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath} 418a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 419a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath/** 420a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * Simple exception we throw if the SSL connection is closed by the user. 421a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath * 422a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath */ 423a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamathclass SSLConnectionClosedByUserException extends SSLException { 424a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath 425a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath public SSLConnectionClosedByUserException(String reason) { 426a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath super(reason); 427a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath } 428a8b46a3d3b6ed1488df10740653829283572903bNarayan Kamath} 429