1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package com.squareup.okhttp.internal.io; 18 19 import com.squareup.okhttp.Address; 20 import com.squareup.okhttp.CertificatePinner; 21 import com.squareup.okhttp.Connection; 22 import com.squareup.okhttp.ConnectionSpec; 23 import com.squareup.okhttp.Handshake; 24 import com.squareup.okhttp.HttpUrl; 25 import com.squareup.okhttp.Protocol; 26 import com.squareup.okhttp.Request; 27 import com.squareup.okhttp.Response; 28 import com.squareup.okhttp.Route; 29 import com.squareup.okhttp.internal.ConnectionSpecSelector; 30 import com.squareup.okhttp.internal.Platform; 31 import com.squareup.okhttp.internal.Util; 32 import com.squareup.okhttp.internal.Version; 33 import com.squareup.okhttp.internal.framed.FramedConnection; 34 import com.squareup.okhttp.internal.http.Http1xStream; 35 import com.squareup.okhttp.internal.http.OkHeaders; 36 import com.squareup.okhttp.internal.http.RouteException; 37 import com.squareup.okhttp.internal.http.StreamAllocation; 38 import com.squareup.okhttp.internal.tls.CertificateChainCleaner; 39 import com.squareup.okhttp.internal.tls.OkHostnameVerifier; 40 import com.squareup.okhttp.internal.tls.TrustRootIndex; 41 import java.io.IOException; 42 import java.lang.ref.Reference; 43 import java.net.ConnectException; 44 import java.net.Proxy; 45 import java.net.Socket; 46 import java.net.SocketTimeoutException; 47 import java.net.UnknownServiceException; 48 import java.security.cert.Certificate; 49 import java.security.cert.X509Certificate; 50 import java.util.ArrayList; 51 import java.util.List; 52 import java.util.concurrent.TimeUnit; 53 import javax.net.ssl.SSLPeerUnverifiedException; 54 import javax.net.ssl.SSLSocket; 55 import javax.net.ssl.SSLSocketFactory; 56 import javax.net.ssl.X509TrustManager; 57 import okio.BufferedSink; 58 import okio.BufferedSource; 59 import okio.Okio; 60 import okio.Source; 61 62 import static com.squareup.okhttp.internal.Util.closeQuietly; 63 import static java.net.HttpURLConnection.HTTP_OK; 64 import static java.net.HttpURLConnection.HTTP_PROXY_AUTH; 65 import static java.util.concurrent.TimeUnit.MILLISECONDS; 66 67 public final class RealConnection implements Connection { 68 private final Route route; 69 70 /** The low-level TCP socket. */ 71 private Socket rawSocket; 72 73 /** 74 * The application layer socket. Either an {@link SSLSocket} layered over {@link #rawSocket}, or 75 * {@link #rawSocket} itself if this connection does not use SSL. 76 */ 77 public Socket socket; 78 private Handshake handshake; 79 private Protocol protocol; 80 public volatile FramedConnection framedConnection; 81 public int streamCount; 82 public BufferedSource source; 83 public BufferedSink sink; 84 public final List<Reference<StreamAllocation>> allocations = new ArrayList<>(); 85 public boolean noNewStreams; 86 public long idleAtNanos = Long.MAX_VALUE; 87 RealConnection(Route route)88 public RealConnection(Route route) { 89 this.route = route; 90 } 91 connect(int connectTimeout, int readTimeout, int writeTimeout, List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled)92 public void connect(int connectTimeout, int readTimeout, int writeTimeout, 93 List<ConnectionSpec> connectionSpecs, boolean connectionRetryEnabled) throws RouteException { 94 if (protocol != null) throw new IllegalStateException("already connected"); 95 96 RouteException routeException = null; 97 ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs); 98 Proxy proxy = route.getProxy(); 99 Address address = route.getAddress(); 100 101 if (route.getAddress().getSslSocketFactory() == null 102 && !connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) { 103 throw new RouteException(new UnknownServiceException( 104 "CLEARTEXT communication not supported: " + connectionSpecs)); 105 } 106 107 while (protocol == null) { 108 try { 109 rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP 110 ? address.getSocketFactory().createSocket() 111 : new Socket(proxy); 112 connectSocket(connectTimeout, readTimeout, writeTimeout, connectionSpecSelector); 113 } catch (IOException e) { 114 Util.closeQuietly(socket); 115 Util.closeQuietly(rawSocket); 116 socket = null; 117 rawSocket = null; 118 source = null; 119 sink = null; 120 handshake = null; 121 protocol = null; 122 123 if (routeException == null) { 124 routeException = new RouteException(e); 125 } else { 126 routeException.addConnectException(e); 127 } 128 129 if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) { 130 throw routeException; 131 } 132 } 133 } 134 } 135 136 /** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */ connectSocket(int connectTimeout, int readTimeout, int writeTimeout, ConnectionSpecSelector connectionSpecSelector)137 private void connectSocket(int connectTimeout, int readTimeout, int writeTimeout, 138 ConnectionSpecSelector connectionSpecSelector) throws IOException { 139 rawSocket.setSoTimeout(readTimeout); 140 try { 141 Platform.get().connectSocket(rawSocket, route.getSocketAddress(), connectTimeout); 142 } catch (ConnectException e) { 143 throw new ConnectException("Failed to connect to " + route.getSocketAddress()); 144 } 145 source = Okio.buffer(Okio.source(rawSocket)); 146 sink = Okio.buffer(Okio.sink(rawSocket)); 147 148 if (route.getAddress().getSslSocketFactory() != null) { 149 connectTls(readTimeout, writeTimeout, connectionSpecSelector); 150 } else { 151 protocol = Protocol.HTTP_1_1; 152 socket = rawSocket; 153 } 154 155 if (protocol == Protocol.SPDY_3 || protocol == Protocol.HTTP_2) { 156 socket.setSoTimeout(0); // Framed connection timeouts are set per-stream. 157 158 FramedConnection framedConnection = new FramedConnection.Builder(true) 159 .socket(socket, route.getAddress().url().host(), source, sink) 160 .protocol(protocol) 161 .build(); 162 framedConnection.sendConnectionPreface(); 163 164 // Only assign the framed connection once the preface has been sent successfully. 165 this.framedConnection = framedConnection; 166 } 167 } 168 connectTls(int readTimeout, int writeTimeout, ConnectionSpecSelector connectionSpecSelector)169 private void connectTls(int readTimeout, int writeTimeout, 170 ConnectionSpecSelector connectionSpecSelector) throws IOException { 171 if (route.requiresTunnel()) { 172 createTunnel(readTimeout, writeTimeout); 173 } 174 175 Address address = route.getAddress(); 176 SSLSocketFactory sslSocketFactory = address.getSslSocketFactory(); 177 boolean success = false; 178 SSLSocket sslSocket = null; 179 try { 180 // Create the wrapper over the connected socket. 181 sslSocket = (SSLSocket) sslSocketFactory.createSocket( 182 rawSocket, address.getUriHost(), address.getUriPort(), true /* autoClose */); 183 184 // Configure the socket's ciphers, TLS versions, and extensions. 185 ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket); 186 if (connectionSpec.supportsTlsExtensions()) { 187 Platform.get().configureTlsExtensions( 188 sslSocket, address.getUriHost(), address.getProtocols()); 189 } 190 191 // Force handshake. This can throw! 192 sslSocket.startHandshake(); 193 Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession()); 194 195 // Verify that the socket's certificates are acceptable for the target host. 196 if (!address.getHostnameVerifier().verify(address.getUriHost(), sslSocket.getSession())) { 197 X509Certificate cert = (X509Certificate) unverifiedHandshake.peerCertificates().get(0); 198 throw new SSLPeerUnverifiedException("Hostname " + address.getUriHost() + " not verified:" 199 + "\n certificate: " + CertificatePinner.pin(cert) 200 + "\n DN: " + cert.getSubjectDN().getName() 201 + "\n subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert)); 202 } 203 204 // Check that the certificate pinner is satisfied by the certificates presented. 205 if (address.getCertificatePinner() != CertificatePinner.DEFAULT) { 206 TrustRootIndex trustRootIndex = trustRootIndex(address.getSslSocketFactory()); 207 List<Certificate> certificates = new CertificateChainCleaner(trustRootIndex) 208 .clean(unverifiedHandshake.peerCertificates()); 209 address.getCertificatePinner().check(address.getUriHost(), certificates); 210 } 211 212 // Success! Save the handshake and the ALPN protocol. 213 String maybeProtocol = connectionSpec.supportsTlsExtensions() 214 ? Platform.get().getSelectedProtocol(sslSocket) 215 : null; 216 socket = sslSocket; 217 source = Okio.buffer(Okio.source(socket)); 218 sink = Okio.buffer(Okio.sink(socket)); 219 handshake = unverifiedHandshake; 220 protocol = maybeProtocol != null 221 ? Protocol.get(maybeProtocol) 222 : Protocol.HTTP_1_1; 223 success = true; 224 } catch (AssertionError e) { 225 if (Util.isAndroidGetsocknameError(e)) throw new IOException(e); 226 throw e; 227 } finally { 228 if (sslSocket != null) { 229 Platform.get().afterHandshake(sslSocket); 230 } 231 if (!success) { 232 closeQuietly(sslSocket); 233 } 234 } 235 } 236 237 private static SSLSocketFactory lastSslSocketFactory; 238 private static TrustRootIndex lastTrustRootIndex; 239 240 /** 241 * Returns a trust root index for {@code sslSocketFactory}. This uses a static, single-element 242 * cache to avoid redoing reflection and SSL indexing in the common case where most SSL 243 * connections use the same SSL socket factory. 244 */ trustRootIndex(SSLSocketFactory sslSocketFactory)245 private static synchronized TrustRootIndex trustRootIndex(SSLSocketFactory sslSocketFactory) { 246 if (sslSocketFactory != lastSslSocketFactory) { 247 X509TrustManager trustManager = Platform.get().trustManager(sslSocketFactory); 248 lastTrustRootIndex = Platform.get().trustRootIndex(trustManager); 249 lastSslSocketFactory = sslSocketFactory; 250 } 251 return lastTrustRootIndex; 252 } 253 254 /** 255 * To make an HTTPS connection over an HTTP proxy, send an unencrypted 256 * CONNECT request to create the proxy connection. This may need to be 257 * retried if the proxy requires authorization. 258 */ createTunnel(int readTimeout, int writeTimeout)259 private void createTunnel(int readTimeout, int writeTimeout) throws IOException { 260 // Make an SSL Tunnel on the first message pair of each SSL + proxy connection. 261 Request tunnelRequest = createTunnelRequest(); 262 HttpUrl url = tunnelRequest.httpUrl(); 263 String requestLine = "CONNECT " + Util.hostHeader(url, true) + " HTTP/1.1"; 264 while (true) { 265 Http1xStream tunnelConnection = new Http1xStream(null, source, sink); 266 source.timeout().timeout(readTimeout, MILLISECONDS); 267 sink.timeout().timeout(writeTimeout, MILLISECONDS); 268 tunnelConnection.writeRequest(tunnelRequest.headers(), requestLine); 269 tunnelConnection.finishRequest(); 270 Response response = tunnelConnection.readResponse().request(tunnelRequest).build(); 271 // The response body from a CONNECT should be empty, but if it is not then we should consume 272 // it before proceeding. 273 long contentLength = OkHeaders.contentLength(response); 274 if (contentLength == -1L) { 275 contentLength = 0L; 276 } 277 Source body = tunnelConnection.newFixedLengthSource(contentLength); 278 Util.skipAll(body, Integer.MAX_VALUE, TimeUnit.MILLISECONDS); 279 body.close(); 280 281 switch (response.code()) { 282 case HTTP_OK: 283 // Assume the server won't send a TLS ServerHello until we send a TLS ClientHello. If 284 // that happens, then we will have buffered bytes that are needed by the SSLSocket! 285 // This check is imperfect: it doesn't tell us whether a handshake will succeed, just 286 // that it will almost certainly fail because the proxy has sent unexpected data. 287 if (!source.buffer().exhausted() || !sink.buffer().exhausted()) { 288 throw new IOException("TLS tunnel buffered too many bytes!"); 289 } 290 return; 291 292 case HTTP_PROXY_AUTH: 293 tunnelRequest = OkHeaders.processAuthHeader( 294 route.getAddress().getAuthenticator(), response, route.getProxy()); 295 if (tunnelRequest != null) continue; 296 throw new IOException("Failed to authenticate with proxy"); 297 298 default: 299 throw new IOException( 300 "Unexpected response code for CONNECT: " + response.code()); 301 } 302 } 303 } 304 305 /** 306 * Returns a request that creates a TLS tunnel via an HTTP proxy, or null if 307 * no tunnel is necessary. Everything in the tunnel request is sent 308 * unencrypted to the proxy server, so tunnels include only the minimum set of 309 * headers. This avoids sending potentially sensitive data like HTTP cookies 310 * to the proxy unencrypted. 311 */ createTunnelRequest()312 private Request createTunnelRequest() throws IOException { 313 return new Request.Builder() 314 .url(route.getAddress().url()) 315 .header("Host", Util.hostHeader(route.getAddress().url(), true)) 316 .header("Proxy-Connection", "Keep-Alive") 317 .header("User-Agent", Version.userAgent()) // For HTTP/1.0 proxies like Squid. 318 .build(); 319 } 320 321 /** Returns true if {@link #connect} has been attempted on this connection. */ isConnected()322 boolean isConnected() { 323 return protocol != null; 324 } 325 getRoute()326 @Override public Route getRoute() { 327 return route; 328 } 329 cancel()330 public void cancel() { 331 // Close the raw socket so we don't end up doing synchronous I/O. 332 Util.closeQuietly(rawSocket); 333 } 334 getSocket()335 @Override public Socket getSocket() { 336 return socket; 337 } 338 allocationLimit()339 public int allocationLimit() { 340 FramedConnection framedConnection = this.framedConnection; 341 return framedConnection != null 342 ? framedConnection.maxConcurrentStreams() 343 : 1; 344 } 345 346 /** Returns true if this connection is ready to host new streams. */ isHealthy(boolean doExtensiveChecks)347 public boolean isHealthy(boolean doExtensiveChecks) { 348 if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) { 349 return false; 350 } 351 352 if (framedConnection != null) { 353 return true; // TODO: check framedConnection.shutdown. 354 } 355 356 if (doExtensiveChecks) { 357 try { 358 int readTimeout = socket.getSoTimeout(); 359 try { 360 socket.setSoTimeout(1); 361 if (source.exhausted()) { 362 return false; // Stream is exhausted; socket is closed. 363 } 364 return true; 365 } finally { 366 socket.setSoTimeout(readTimeout); 367 } 368 } catch (SocketTimeoutException ignored) { 369 // Read timed out; socket is good. 370 } catch (IOException e) { 371 return false; // Couldn't read; socket is closed. 372 } 373 } 374 375 return true; 376 } 377 getHandshake()378 @Override public Handshake getHandshake() { 379 return handshake; 380 } 381 382 /** 383 * Returns true if this is a SPDY connection. Such connections can be used 384 * in multiple HTTP requests simultaneously. 385 */ isMultiplexed()386 public boolean isMultiplexed() { 387 return framedConnection != null; 388 } 389 getProtocol()390 @Override public Protocol getProtocol() { 391 return protocol != null ? protocol : Protocol.HTTP_1_1; 392 } 393 toString()394 @Override public String toString() { 395 return "Connection{" 396 + route.getAddress().url().host() + ":" + route.getAddress().url().port() 397 + ", proxy=" 398 + route.getProxy() 399 + " hostAddress=" 400 + route.getSocketAddress() 401 + " cipherSuite=" 402 + (handshake != null ? handshake.cipherSuite() : "none") 403 + " protocol=" 404 + protocol 405 + '}'; 406 } 407 } 408