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 okhttp3.internal.connection;
18
19 import java.io.IOException;
20 import java.lang.ref.Reference;
21 import java.net.ConnectException;
22 import java.net.HttpURLConnection;
23 import java.net.ProtocolException;
24 import java.net.Proxy;
25 import java.net.Socket;
26 import java.net.SocketException;
27 import java.net.SocketTimeoutException;
28 import java.net.UnknownServiceException;
29 import java.security.cert.Certificate;
30 import java.security.cert.X509Certificate;
31 import java.util.ArrayList;
32 import java.util.List;
33 import javax.annotation.Nullable;
34 import javax.net.ssl.SSLPeerUnverifiedException;
35 import javax.net.ssl.SSLSession;
36 import javax.net.ssl.SSLSocket;
37 import javax.net.ssl.SSLSocketFactory;
38 import okhttp3.Address;
39 import okhttp3.Call;
40 import okhttp3.CertificatePinner;
41 import okhttp3.Connection;
42 import okhttp3.ConnectionSpec;
43 import okhttp3.EventListener;
44 import okhttp3.Handshake;
45 import okhttp3.HttpUrl;
46 import okhttp3.Interceptor;
47 import okhttp3.OkHttpClient;
48 import okhttp3.Protocol;
49 import okhttp3.Request;
50 import okhttp3.Response;
51 import okhttp3.Route;
52 import okhttp3.internal.Internal;
53 import okhttp3.internal.Util;
54 import okhttp3.internal.Version;
55 import okhttp3.internal.http.ExchangeCodec;
56 import okhttp3.internal.http1.Http1ExchangeCodec;
57 import okhttp3.internal.http2.ConnectionShutdownException;
58 import okhttp3.internal.http2.ErrorCode;
59 import okhttp3.internal.http2.Http2Connection;
60 import okhttp3.internal.http2.Http2ExchangeCodec;
61 import okhttp3.internal.http2.Http2Stream;
62 import okhttp3.internal.http2.StreamResetException;
63 import okhttp3.internal.platform.Platform;
64 import okhttp3.internal.tls.OkHostnameVerifier;
65 import okhttp3.internal.ws.RealWebSocket;
66 import okio.BufferedSink;
67 import okio.BufferedSource;
68 import okio.Okio;
69
70 import static java.net.HttpURLConnection.HTTP_OK;
71 import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
72 import static java.util.concurrent.TimeUnit.MILLISECONDS;
73 import static okhttp3.internal.Util.closeQuietly;
74
75 public final class RealConnection extends Http2Connection.Listener implements Connection {
76   private static final String NPE_THROW_WITH_NULL = "throw with null exception";
77   private static final int MAX_TUNNEL_ATTEMPTS = 21;
78
79   public final RealConnectionPool connectionPool;
80   private final Route route;
81
82   // The fields below are initialized by connect() and never reassigned.
83
84   /** The low-level TCP socket. */
85   private Socket rawSocket;
86
87   /**
88    * The application layer socket. Either an {@link SSLSocket} layered over {@link #rawSocket}, or
89    * {@link #rawSocket} itself if this connection does not use SSL.
90    */

91   private Socket socket;
92   private Handshake handshake;
93   private Protocol protocol;
94   private Http2Connection http2Connection;
95   private BufferedSource source;
96   private BufferedSink sink;
97
98   // The fields below track connection state and are guarded by connectionPool.
99
100   /**
101    * If true, no new exchanges can be created on this connection. Once true this is always true.
102    * Guarded by {@link #connectionPool}.
103    */

104   boolean noNewExchanges;
105
106   /**
107    * The number of times there was a problem establishing a stream that could be due to route
108    * chosen. Guarded by {@link #connectionPool}.
109    */

110   int routeFailureCount;
111
112   int successCount;
113   private int refusedStreamCount;
114
115   /**
116    * The maximum number of concurrent streams that can be carried by this connection. If {@code
117    * allocations.size() < allocationLimit} then new streams can be created on this connection.
118    */

119   private int allocationLimit = 1;
120
121   /** Current calls carried by this connection. */
122   final List<Reference<Transmitter>> transmitters = new ArrayList<>();
123
124   /** Nanotime timestamp when {@code allocations.size()} reached zero. */
125   long idleAtNanos = Long.MAX_VALUE;
126
127   public RealConnection(RealConnectionPool connectionPool, Route route) {
128     this.connectionPool = connectionPool;
129     this.route = route;
130   }
131
132   /** Prevent further exchanges from being created on this connection. */
133   public void noNewExchanges() {
134     assert (!Thread.holdsLock(connectionPool));
135     synchronized (connectionPool) {
136       noNewExchanges = true;
137     }
138   }
139
140   static RealConnection testConnection(
141       RealConnectionPool connectionPool, Route route, Socket socket, long idleAtNanos) {
142     RealConnection result = new RealConnection(connectionPool, route);
143     result.socket = socket;
144     result.idleAtNanos = idleAtNanos;
145     return result;
146   }
147
148   public void connect(int connectTimeout, int readTimeout, int writeTimeout,
149       int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
150       EventListener eventListener) {
151     if (protocol != nullthrow new IllegalStateException("already connected");
152
153     RouteException routeException = null;
154     List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs();
155     ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);
156
157     if (route.address().sslSocketFactory() == null) {
158       if (!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
159         throw new RouteException(new UnknownServiceException(
160             "CLEARTEXT communication not enabled for client"));
161       }
162       String host = route.address().url().host();
163       if (!Platform.get().isCleartextTrafficPermitted(host)) {
164         throw new RouteException(new UnknownServiceException(
165             "CLEARTEXT communication to " + host + " not permitted by network security policy"));
166       }
167     } else {
168       if (route.address().protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)) {
169         throw new RouteException(new UnknownServiceException(
170             "H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"));
171       }
172     }
173
174     while (true) {
175       try {
176         if (route.requiresTunnel()) {
177           connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
178           if (rawSocket == null) {
179             // We were unable to connect the tunnel but properly closed down our resources.
180             break;
181           }
182         } else {
183           connectSocket(connectTimeout, readTimeout, call, eventListener);
184         }
185         establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
186         eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);
187         break;
188       } catch (IOException e) {
189         closeQuietly(socket);
190         closeQuietly(rawSocket);
191         socket = null;
192         rawSocket = null;
193         source = null;
194         sink = null;
195         handshake = null;
196         protocol = null;
197         http2Connection = null;
198
199         eventListener.connectFailed(call, route.socketAddress(), route.proxy(), null, e);
200
201         if (routeException == null) {
202           routeException = new RouteException(e);
203         } else {
204           routeException.addConnectException(e);
205         }
206
207         if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
208           throw routeException;
209         }
210       }
211     }
212
213     if (route.requiresTunnel() && rawSocket == null) {
214       ProtocolException exception = new ProtocolException("Too many tunnel connections attempted: "
215           + MAX_TUNNEL_ATTEMPTS);
216       throw new RouteException(exception);
217     }
218
219     if (http2Connection != null) {
220       synchronized (connectionPool) {
221         allocationLimit = http2Connection.maxConcurrentStreams();
222       }
223     }
224   }
225
226   /**
227    * Does all the work to build an HTTPS connection over a proxy tunnel. The catch here is that a
228    * proxy server can issue an auth challenge and then close the connection.
229    */

230   private void connectTunnel(int connectTimeout, int readTimeout, int writeTimeout, Call call,
231       EventListener eventListener) throws IOException {
232     Request tunnelRequest = createTunnelRequest();
233     HttpUrl url = tunnelRequest.url();
234     for (int i = 0; i < MAX_TUNNEL_ATTEMPTS; i++) {
235       connectSocket(connectTimeout, readTimeout, call, eventListener);
236       tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url);
237
238       if (tunnelRequest == nullbreak// Tunnel successfully created.
239
240       // The proxy decided to close the connection after an auth challenge. We need to create a new
241       // connection, but this time with the auth credentials.
242       closeQuietly(rawSocket);
243       rawSocket = null;
244       sink = null;
245       source = null;
246       eventListener.connectEnd(call, route.socketAddress(), route.proxy(), null);
247     }
248   }
249
250   /** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */
251   private void connectSocket(int connectTimeout, int readTimeout, Call call,
252       EventListener eventListener) throws IOException {
253     Proxy proxy = route.proxy();
254     Address address = route.address();
255
256     rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
257         ? address.socketFactory().createSocket()
258         : new Socket(proxy);
259
260     eventListener.connectStart(call, route.socketAddress(), proxy);
261     rawSocket.setSoTimeout(readTimeout);
262     try {
263       Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
264     } catch (ConnectException e) {
265       ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
266       ce.initCause(e);
267       throw ce;
268     }
269
270     // The following try/catch block is a pseudo hacky way to get around a crash on Android 7.0
271     // More details:
272     // https://github.com/square/okhttp/issues/3245
273     // https://android-review.googlesource.com/#/c/271775/
274     try {
275       source = Okio.buffer(Okio.source(rawSocket));
276       sink = Okio.buffer(Okio.sink(rawSocket));
277     } catch (NullPointerException npe) {
278       if (NPE_THROW_WITH_NULL.equals(npe.getMessage())) {
279         throw new IOException(npe);
280       }
281     }
282   }
283
284   private void establishProtocol(ConnectionSpecSelector connectionSpecSelector,
285       int pingIntervalMillis, Call call, EventListener eventListener) throws IOException {
286     if (route.address().sslSocketFactory() == null) {
287       if (route.address().protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)) {
288         socket = rawSocket;
289         protocol = Protocol.H2_PRIOR_KNOWLEDGE;
290         startHttp2(pingIntervalMillis);
291         return;
292       }
293
294       socket = rawSocket;
295       protocol = Protocol.HTTP_1_1;
296       return;
297     }
298
299     eventListener.secureConnectStart(call);
300     connectTls(connectionSpecSelector);
301     eventListener.secureConnectEnd(call, handshake);
302
303     if (protocol == Protocol.HTTP_2) {
304       startHttp2(pingIntervalMillis);
305     }
306   }
307
308   private void startHttp2(int pingIntervalMillis) throws IOException {
309     socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
310     http2Connection = new Http2Connection.Builder(true)
311         .socket(socket, route.address().url().host(), source, sink)
312         .listener(this)
313         .pingIntervalMillis(pingIntervalMillis)
314         .build();
315     http2Connection.start();
316   }
317
318   private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
319     Address address = route.address();
320     SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
321     boolean success = false;
322     SSLSocket sslSocket = null;
323     try {
324       // Create the wrapper over the connected socket.
325       sslSocket = (SSLSocket) sslSocketFactory.createSocket(
326           rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
327
328       // Configure the socket's ciphers, TLS versions, and extensions.
329       ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
330       if (connectionSpec.supportsTlsExtensions()) {
331         Platform.get().configureTlsExtensions(
332             sslSocket, address.url().host(), address.protocols());
333       }
334
335       // Force handshake. This can throw!
336       sslSocket.startHandshake();
337       // block for session establishment
338       SSLSession sslSocketSession = sslSocket.getSession();
339       Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
340
341       // Verify that the socket's certificates are acceptable for the target host.
342       if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
343         List<Certificate> peerCertificates = unverifiedHandshake.peerCertificates();
344         if (!peerCertificates.isEmpty()) {
345           X509Certificate cert = (X509Certificate) peerCertificates.get(0);
346           throw new SSLPeerUnverifiedException(
347               "Hostname " + address.url().host() + " not verified:"
348                   + "\n    certificate: " + CertificatePinner.pin(cert)
349                   + "\n    DN: " + cert.getSubjectDN().getName()
350                   + "\n    subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
351         } else {
352           throw new SSLPeerUnverifiedException(
353               "Hostname " + address.url().host() + " not verified (no certificates)");
354         }
355       }
356
357       // Check that the certificate pinner is satisfied by the certificates presented.
358       address.certificatePinner().check(address.url().host(),
359           unverifiedHandshake.peerCertificates());
360
361       // Success! Save the handshake and the ALPN protocol.
362       String maybeProtocol = connectionSpec.supportsTlsExtensions()
363           ? Platform.get().getSelectedProtocol(sslSocket)
364           : null;
365       socket = sslSocket;
366       source = Okio.buffer(Okio.source(socket));
367       sink = Okio.buffer(Okio.sink(socket));
368       handshake = unverifiedHandshake;
369       protocol = maybeProtocol != null
370           ? Protocol.get(maybeProtocol)
371           : Protocol.HTTP_1_1;
372       success = true;
373     } catch (AssertionError e) {
374       if (Util.isAndroidGetsocknameError(e)) throw new IOException(e);
375       throw e;
376     } finally {
377       if (sslSocket != null) {
378         Platform.get().afterHandshake(sslSocket);
379       }
380       if (!success) {
381         closeQuietly(sslSocket);
382       }
383     }
384   }
385
386   /**
387    * To make an HTTPS connection over an HTTP proxy, send an unencrypted CONNECT request to create
388    * the proxy connection. This may need to be retried if the proxy requires authorization.
389    */

390   private Request createTunnel(int readTimeout, int writeTimeout, Request tunnelRequest,
391       HttpUrl url) throws IOException {
392     // Make an SSL Tunnel on the first message pair of each SSL + proxy connection.
393     String requestLine = "CONNECT " + Util.hostHeader(url, true) + " HTTP/1.1";
394     while (true) {
395       Http1ExchangeCodec tunnelCodec = new Http1ExchangeCodec(nullnull, source, sink);
396       source.timeout().timeout(readTimeout, MILLISECONDS);
397       sink.timeout().timeout(writeTimeout, MILLISECONDS);
398       tunnelCodec.writeRequest(tunnelRequest.headers(), requestLine);
399       tunnelCodec.finishRequest();
400       Response response = tunnelCodec.readResponseHeaders(false)
401           .request(tunnelRequest)
402           .build();
403       tunnelCodec.skipConnectBody(response);
404
405       switch (response.code()) {
406         case HTTP_OK:
407           // Assume the server won't send a TLS ServerHello until we send a TLS ClientHello. If
408           // that happens, then we will have buffered bytes that are needed by the SSLSocket!
409           // This check is imperfect: it doesn't tell us whether a handshake will succeed, just
410           // that it will almost certainly fail because the proxy has sent unexpected data.
411           if (!source.getBuffer().exhausted() || !sink.buffer().exhausted()) {
412             throw new IOException("TLS tunnel buffered too many bytes!");
413           }
414           return null;
415
416         case HTTP_PROXY_AUTH:
417           tunnelRequest = route.address().proxyAuthenticator().authenticate(route, response);
418           if (tunnelRequest == nullthrow new IOException("Failed to authenticate with proxy");
419
420           if ("close".equalsIgnoreCase(response.header("Connection"))) {
421             return tunnelRequest;
422           }
423           break;
424
425         default:
426           throw new IOException(
427               "Unexpected response code for CONNECT: " + response.code());
428       }
429     }
430   }
431
432   /**
433    * Returns a request that creates a TLS tunnel via an HTTP proxy. Everything in the tunnel request
434    * is sent unencrypted to the proxy server, so tunnels include only the minimum set of headers.
435    * This avoids sending potentially sensitive data like HTTP cookies to the proxy unencrypted.
436    *
437    * <p>In order to support preemptive authentication we pass a fake “Auth Failed” response to the
438    * authenticator. This gives the authenticator the option to customize the CONNECT request. It can
439    * decline to do so by returning null, in which case OkHttp will use it as-is
440    */

441   private Request createTunnelRequest() throws IOException {
442     Request proxyConnectRequest = new Request.Builder()
443         .url(route.address().url())
444         .method("CONNECT"null)
445         .header("Host", Util.hostHeader(route.address().url(), true))
446         .header("Proxy-Connection""Keep-Alive"// For HTTP/1.0 proxies like Squid.
447         .header("User-Agent", Version.userAgent())
448         .build();
449
450     Response fakeAuthChallengeResponse = new Response.Builder()
451         .request(proxyConnectRequest)
452         .protocol(Protocol.HTTP_1_1)
453         .code(HttpURLConnection.HTTP_PROXY_AUTH)
454         .message("Preemptive Authenticate")
455         .body(Util.EMPTY_RESPONSE)
456         .sentRequestAtMillis(-1L)
457         .receivedResponseAtMillis(-1L)
458         .header("Proxy-Authenticate""OkHttp-Preemptive")
459         .build();
460
461     Request authenticatedRequest = route.address().proxyAuthenticator()
462         .authenticate(route, fakeAuthChallengeResponse);
463
464     return authenticatedRequest != null
465         ? authenticatedRequest
466         : proxyConnectRequest;
467   }
468
469   /**
470    * Returns true if this connection can carry a stream allocation to {@code address}. If non-null
471    * {@code route} is the resolved route for a connection.
472    */

473   boolean isEligible(Address address, @Nullable List<Route> routes) {
474     // If this connection is not accepting new exchanges, we're done.
475     if (transmitters.size() >= allocationLimit || noNewExchanges) return false;
476
477     // If the non-host fields of the address don't overlap, we're done.
478     if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;
479
480     // If the host exactly matches, we're done: this connection can carry the address.
481     if (address.url().host().equals(this.route().address().url().host())) {
482       return true// This connection is a perfect match.
483     }
484
485     // At this point we don't have a hostname match. But we still be able to carry the request if
486     // our connection coalescing requirements are met. See also:
487     // https://hpbn.co/optimizing-application-delivery/#eliminate-domain-sharding
488     // https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/
489
490     // 1. This connection must be HTTP/2.
491     if (http2Connection == nullreturn false;
492
493     // 2. The routes must share an IP address.
494     if (routes == null || !routeMatchesAny(routes)) return false;
495
496     // 3. This connection's server certificate's must cover the new host.
497     if (address.hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;
498     if (!supportsUrl(address.url())) return false;
499
500     // 4. Certificate pinning must match the host.
501     try {
502       address.certificatePinner().check(address.url().host(), handshake().peerCertificates());
503     } catch (SSLPeerUnverifiedException e) {
504       return false;
505     }
506
507     return true// The caller's address can be carried by this connection.
508   }
509
510   /**
511    * Returns true if this connection's route has the same address as any of {@code routes}. This
512    * requires us to have a DNS address for both hosts, which only happens after route planning. We
513    * can't coalesce connections that use a proxy, since proxies don't tell us the origin server's IP
514    * address.
515    */

516   private boolean routeMatchesAny(List<Route> candidates) {
517     for (int i = 0, size = candidates.size(); i < size; i++) {
518       Route candidate = candidates.get(i);
519       if (candidate.proxy().type() == Proxy.Type.DIRECT
520           && route.proxy().type() == Proxy.Type.DIRECT
521           && route.socketAddress().equals(candidate.socketAddress())) {
522         return true;
523       }
524     }
525     return false;
526   }
527
528   public boolean supportsUrl(HttpUrl url) {
529     if (url.port() != route.address().url().port()) {
530       return false// Port mismatch.
531     }
532
533     if (!url.host().equals(route.address().url().host())) {
534       // We have a host mismatch. But if the certificate matches, we're still good.
535       return handshake != null && OkHostnameVerifier.INSTANCE.verify(
536           url.host(), (X509Certificate) handshake.peerCertificates().get(0));
537     }
538
539     return true// Success. The URL is supported.
540   }
541
542   ExchangeCodec newCodec(OkHttpClient client, Interceptor.Chain chain) throws SocketException {
543     if (http2Connection != null) {
544       return new Http2ExchangeCodec(client, this, chain, http2Connection);
545     } else {
546       socket.setSoTimeout(chain.readTimeoutMillis());
547       source.timeout().timeout(chain.readTimeoutMillis(), MILLISECONDS);
548       sink.timeout().timeout(chain.writeTimeoutMillis(), MILLISECONDS);
549       return new Http1ExchangeCodec(client, this, source, sink);
550     }
551   }
552
553   RealWebSocket.Streams newWebSocketStreams(Exchange exchange) throws SocketException {
554     socket.setSoTimeout(0);
555     noNewExchanges();
556     return new RealWebSocket.Streams(true, source, sink) {
557       @Override public void close() throws IOException {
558         exchange.bodyComplete(-1L, truetruenull);
559       }
560     };
561   }
562
563   @Override public Route route() {
564     return route;
565   }
566
567   public void cancel() {
568     // Close the raw socket so we don't end up doing synchronous I/O.
569     closeQuietly(rawSocket);
570   }
571
572   @Override public Socket socket() {
573     return socket;
574   }
575
576   /** Returns true if this connection is ready to host new streams. */
577   public boolean isHealthy(boolean doExtensiveChecks) {
578     if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
579       return false;
580     }
581
582     if (http2Connection != null) {
583       return http2Connection.isHealthy(System.nanoTime());
584     }
585
586     if (doExtensiveChecks) {
587       try {
588         int readTimeout = socket.getSoTimeout();
589         try {
590           socket.setSoTimeout(1);
591           if (source.exhausted()) {
592             return false// Stream is exhausted; socket is closed.
593           }
594           return true;
595         } finally {
596           socket.setSoTimeout(readTimeout);
597         }
598       } catch (SocketTimeoutException ignored) {
599         // Read timed out; socket is good.
600       } catch (IOException e) {
601         return false// Couldn't read; socket is closed.
602       }
603     }
604
605     return true;
606   }
607
608   /** Refuse incoming streams. */
609   @Override public void onStream(Http2Stream stream) throws IOException {
610     stream.close(ErrorCode.REFUSED_STREAM, null);
611   }
612
613   /** When settings are received, adjust the allocation limit. */
614   @Override public void onSettings(Http2Connection connection) {
615     synchronized (connectionPool) {
616       allocationLimit = connection.maxConcurrentStreams();
617     }
618   }
619
620   @Override public Handshake handshake() {
621     return handshake;
622   }
623
624   /**
625    * Returns true if this is an HTTP/2 connection. Such connections can be used in multiple HTTP
626    * requests simultaneously.
627    */

628   public boolean isMultiplexed() {
629     return http2Connection != null;
630   }
631
632   /**
633    * Track a failure using this connection. This may prevent both the connection and its route from
634    * being used for future exchanges.
635    */

636   void trackFailure(@Nullable IOException e) {
637     assert (!Thread.holdsLock(connectionPool));
638     synchronized (connectionPool) {
639       if (e instanceof StreamResetException) {
640         ErrorCode errorCode = ((StreamResetException) e).errorCode;
641         if (errorCode == ErrorCode.REFUSED_STREAM) {
642           // Retry REFUSED_STREAM errors once on the same connection.
643           refusedStreamCount++;
644           if (refusedStreamCount > 1) {
645             noNewExchanges = true;
646             routeFailureCount++;
647           }
648         } else if (errorCode != ErrorCode.CANCEL) {
649           // Keep the connection for CANCEL errors. Everything else wants a fresh connection.
650           noNewExchanges = true;
651           routeFailureCount++;
652         }
653       } else if (!isMultiplexed() || e instanceof ConnectionShutdownException) {
654         noNewExchanges = true;
655
656         // If this route hasn't completed a call, avoid it for new connections.
657         if (successCount == 0) {
658           if (e != null) {
659             connectionPool.connectFailed(route, e);
660           }
661           routeFailureCount++;
662         }
663       }
664     }
665   }
666
667   @Override public Protocol protocol() {
668     return protocol;
669   }
670
671   @Override public String toString() {
672     return "Connection{"
673         + route.address().url().host() + ":" + route.address().url().port()
674         + ", proxy="
675         + route.proxy()
676         + " hostAddress="
677         + route.socketAddress()
678         + " cipherSuite="
679         + (handshake != null ? handshake.cipherSuite() : "none")
680         + " protocol="
681         + protocol
682         + '}';
683   }
684 }
685