上篇我们将来缓存拦截器,鉴于本系列是将Okhttp的所以就没有将太多关于缓存相关的东西,后面又机会在开一个系列。今天我们讲讲倒数第二个拦截器—连接拦截器,这才是真正的开始向服务端发起进攻,同志们是不是已经急不可耐了,哈哈,马上到来。
源码
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public Response intercept(Chain chain) throws IOException { RealInterceptorChain realChain = (RealInterceptorChain) chain; Request request = realChain.request(); StreamAllocation streamAllocation = realChain.streamAllocation(); // We need the network to satisfy this request. Possibly for validating a conditional GET. boolean doExtensiveHealthChecks = !request.method().equals("GET"); HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks); RealConnection connection = streamAllocation.connection(); return realChain.proceed(request, streamAllocation, httpCodec, connection); } |
此处的逻辑很简单,因为已经转嫁到其他的类对象上去了。前面我们提到过RealInterceptorChain构造函数有四个重要的属性(Request、StreamAllocation, HttpCodec、Connection)第一个不用说一开始就必须有,第二个是在重试拦截器中实例化的,第三、第四就是在本节中腰实例化出来的。
获取httpcodec
通过StreamAllocation的newStream()方法可以的得到一个HttpCodec。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) { // 略 try { // 1.查找是否有完好的连接,仅在http2.0有用,如果没有将会实例化一个 RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks); // 2.实例化httpcodec,如果是http2.0实例化Http2Codec否则Http2Codec HttpCodec resultCodec = resultConnection.newCodec(client, this); synchronized (connectionPool) { codec = resultCodec; return resultCodec; } } catch (IOException e) { throw new RouteException(e); } } |
获取连接并连接
流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) throws IOException { Route selectedRoute; synchronized (connectionPool) { // 异常的处理 if (released) throw new IllegalStateException("released"); if (codec != null) throw new IllegalStateException("codec != null"); if (canceled) throw new IOException("Canceled"); // 使用已存在的连接 // Attempt to use an already-allocated connection. RealConnection allocatedConnection = this.connection; if (allocatedConnection != null && !allocatedConnection.noNewStreams) { return allocatedConnection; } // 从缓存中获取 // Attempt to get a connection from the pool. Internal.instance.get(connectionPool, address, this); if (connection != null) { return connection; } selectedRoute = route; } // 线路的选择,多ip的支持 // If we need a route, make one. This is a blocking operation. if (selectedRoute == null) { // 里面又个神奇的递归 selectedRoute = routeSelector.next(); } // Create a connection and assign it to this allocation immediately. This makes it possible for // an asynchronous cancel() to interrupt the handshake we're about to do. // 以上都不符合,创建一个连接 RealConnection result; synchronized (connectionPool) { route = selectedRoute; refusedStreamCount = 0; result = new RealConnection(connectionPool, selectedRoute); acquire(result); if (canceled) throw new IOException("Canceled"); } // 连接 并握手 // Do TCP + TLS handshakes. This is a blocking operation. result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled); routeDatabase().connected(result.route()); Socket socket = null; // 放到缓存中 synchronized (connectionPool) { // Pool the connection. Internal.instance.put(connectionPool, result); // If another multiplexed connection to the same address was created concurrently, then // release this connection and acquire that one. if (result.isMultiplexed()) { socket = Internal.instance.deduplicate(connectionPool, address, this); result = connection; } } closeQuietly(socket); return result; } |
连接
建立连接是比较重要的一步了。如果是Https还有证书步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
public void connect( int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) { if (protocol != null) throw new IllegalStateException("already connected"); // 线路的选择 RouteException routeException = null; List |
我们进入connectSocket()函数看看,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private void connectSocket(int connectTimeout, int readTimeout) throws IOException { Proxy proxy = route.proxy(); Address address = route.address(); // 根据代理类型处理socket,为true rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP ? address.socketFactory().createSocket() : new Socket(proxy); rawSocket.setSoTimeout(readTimeout); try { // 连接socket,之所以这样写是因为支持不同的平台 /** * 里面实际上是 socket.connect(address, connectTimeout); * */ Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); } catch (ConnectException e) { ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress()); ce.initCause(e); throw ce; } // 得到输入/输出流 source = Okio.buffer(Okio.source(rawSocket)); sink = Okio.buffer(Okio.sink(rawSocket)); } |
如果使用的是https协议,并且配置有真实将会协议升级。
Https协议的建立
如果使用的是https协议socket连接完成后还有一步,就是Tls的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException { Address address = route.address(); SSLSocketFactory sslSocketFactory = address.sslSocketFactory(); boolean success = false; SSLSocket sslSocket = null; try { // 在原来socket上加一层ssl // Create the wrapper over the connected socket. sslSocket = (SSLSocket) sslSocketFactory.createSocket( rawSocket, address.url().host(), address.url().port(), true /* autoClose */); // Configure the socket's ciphers, TLS versions, and extensions. ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket); if (connectionSpec.supportsTlsExtensions()) { Platform.get().configureTlsExtensions( sslSocket, address.url().host(), address.protocols()); } // Force handshake. This can throw! sslSocket.startHandshake(); Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession()); // Verify that the socket's certificates are acceptable for the target host. if (!address.hostnameVerifier().verify(address.url().host(), sslSocket.getSession())) { X509Certificate cert = (X509Certificate) unverifiedHandshake.peerCertificates().get(0); throw new SSLPeerUnverifiedException("Hostname " + address.url().host() + " not verified:" + "\n certificate: " + CertificatePinner.pin(cert) + "\n DN: " + cert.getSubjectDN().getName() + "\n subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert)); } // Check that the certificate pinner is satisfied by the certificates presented. address.certificatePinner().check(address.url().host(), unverifiedHandshake.peerCertificates()); // Success! Save the handshake and the ALPN protocol. String maybeProtocol = connectionSpec.supportsTlsExtensions() ? Platform.get().getSelectedProtocol(sslSocket) : null; socket = sslSocket; source = Okio.buffer(Okio.source(socket)); sink = Okio.buffer(Okio.sink(socket)); handshake = unverifiedHandshake; protocol = maybeProtocol != null ? Protocol.get(maybeProtocol) : Protocol.HTTP_1_1; success = true; } catch (AssertionError e) { if (Util.isAndroidGetsocknameError(e)) throw new IOException(e); throw e; } finally { if (sslSocket != null) { Platform.get().afterHandshake(sslSocket); } if (!success) { closeQuietly(sslSocket); } } } |
如果对https熟悉的话,应该知道,https就是在http的基础上加了一层。
到此链接完成RealConnection实例化完成。
然后就是进行下一拦截器。
总结
这个拦截器原理流程都比较简单,重点是要把Socket和Http协议相关知识理解清楚。此文知识抛砖引玉,里面的一些细节没有讲,建议读者更具这系列文章自己去梳理。