1
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
83
84
85 private Socket rawSocket;
86
87
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
99
100
104 boolean noNewExchanges;
105
106
110 int routeFailureCount;
111
112 int successCount;
113 private int refusedStreamCount;
114
115
119 private int allocationLimit = 1;
120
121
122 final List<Reference<Transmitter>> transmitters = new ArrayList<>();
123
124
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
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 != null) throw 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
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
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 == null) break;
239
240
241
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
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
271
272
273
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);
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
325 sslSocket = (SSLSocket) sslSocketFactory.createSocket(
326 rawSocket, address.url().host(), address.url().port(), true );
327
328
329 ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
330 if (connectionSpec.supportsTlsExtensions()) {
331 Platform.get().configureTlsExtensions(
332 sslSocket, address.url().host(), address.protocols());
333 }
334
335
336 sslSocket.startHandshake();
337
338 SSLSession sslSocketSession = sslSocket.getSession();
339 Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
340
341
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
358 address.certificatePinner().check(address.url().host(),
359 unverifiedHandshake.peerCertificates());
360
361
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
390 private Request createTunnel(int readTimeout, int writeTimeout, Request tunnelRequest,
391 HttpUrl url) throws IOException {
392
393 String requestLine = "CONNECT " + Util.hostHeader(url, true) + " HTTP/1.1";
394 while (true) {
395 Http1ExchangeCodec tunnelCodec = new Http1ExchangeCodec(null, null, 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
408
409
410
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 == null) throw 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
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")
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
473 boolean isEligible(Address address, @Nullable List<Route> routes) {
474
475 if (transmitters.size() >= allocationLimit || noNewExchanges) return false;
476
477
478 if (!Internal.instance.equalsNonHost(this.route.address(), address)) return false;
479
480
481 if (address.url().host().equals(this.route().address().url().host())) {
482 return true;
483 }
484
485
486
487
488
489
490
491 if (http2Connection == null) return false;
492
493
494 if (routes == null || !routeMatchesAny(routes)) return false;
495
496
497 if (address.hostnameVerifier() != OkHostnameVerifier.INSTANCE) return false;
498 if (!supportsUrl(address.url())) return false;
499
500
501 try {
502 address.certificatePinner().check(address.url().host(), handshake().peerCertificates());
503 } catch (SSLPeerUnverifiedException e) {
504 return false;
505 }
506
507 return true;
508 }
509
510
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;
531 }
532
533 if (!url.host().equals(route.address().url().host())) {
534
535 return handshake != null && OkHostnameVerifier.INSTANCE.verify(
536 url.host(), (X509Certificate) handshake.peerCertificates().get(0));
537 }
538
539 return true;
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, true, true, null);
559 }
560 };
561 }
562
563 @Override public Route route() {
564 return route;
565 }
566
567 public void cancel() {
568
569 closeQuietly(rawSocket);
570 }
571
572 @Override public Socket socket() {
573 return socket;
574 }
575
576
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;
593 }
594 return true;
595 } finally {
596 socket.setSoTimeout(readTimeout);
597 }
598 } catch (SocketTimeoutException ignored) {
599
600 } catch (IOException e) {
601 return false;
602 }
603 }
604
605 return true;
606 }
607
608
609 @Override public void onStream(Http2Stream stream) throws IOException {
610 stream.close(ErrorCode.REFUSED_STREAM, null);
611 }
612
613
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
628 public boolean isMultiplexed() {
629 return http2Connection != null;
630 }
631
632
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
643 refusedStreamCount++;
644 if (refusedStreamCount > 1) {
645 noNewExchanges = true;
646 routeFailureCount++;
647 }
648 } else if (errorCode != ErrorCode.CANCEL) {
649
650 noNewExchanges = true;
651 routeFailureCount++;
652 }
653 } else if (!isMultiplexed() || e instanceof ConnectionShutdownException) {
654 noNewExchanges = true;
655
656
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