前言
上一篇文章我们分析了OKhttp中前三个拦截器:RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor,它们主要是在请求建立前做一些预处理。如果请求经过这个三个拦截器后,还继续往下传递,说明是需要进行网络请求的(缓存无法满足),那么就会来到我们今天所有分析的两个拦截器:ConnectInterceptor(负责建立连接)、CallServerInterceptor(读写请求服务)。
ConnectInterceptor(连接拦截器)
连接拦截器ConnectInterceptor代码如下:
/*ConnectInterceptor*/
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(realChain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
可以看到intercept方法中只有4行代码,其中关键代码只有一句
val exchange :ExChange = realChain.call.initExchange(realChain)
从 realChain.call获得一个ExChange对象, realChain.call其实就是RealCall 。下面我们跟到initExchange方法看一下:
/*RealCall*/
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
val connection : RealConnection = exchangeFinder.find()
val codec : ExchangeCodec = connection.newCodec(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
synchronized(this) {
this.requestBodyOpen = true
this.responseBodyOpen = true
}
if (canceled) throw IOException("Canceled")
return result
}
从上面这段代码可以看到exchangeFinder通过find()方法获取到了RealConnection,然后RealConnection通过newCodec() 方法获取到ExchangeCodec实例,ExchangeCodec再作为参数构建了Exchange实例返回。这里稍微来认识下涉及到的几个类:
- RealConnection :能够承载 1 个或多个并发流的远程 Web 服务器连接。
-
ExchangeCodec:接口类,负责真正的IO操作—写请求、读响应,实现类有
Http1ExchangeCodec、Http2ExchangeCodec,分别对应HTTP1.1协议、HTTP2.0协议。 -
Exchange:管理IO操作,是
ExchangeCodec的包装,增加了事件回调;一个请求对应一个Exchange实例。传给下个拦截器CallServerInterceptor使用。 -
ExchangeFinder:接口类,(从连接池中)寻找可用TCP连接(RealConnection),然后通过连接得到
ExchangeCodec,实现类有FastFallbackExchangeFinder,SequentialExchangeFinder(默认)
接下来我们就来看一下exchangeFinder.find()和connection.newCodec()具体做了什么?
ExchangeFinder
ExchangeFinder的作用从名字就可以看出——Exchange寻找者,本质是为请求寻找一个TCP连接。上面我们已经知道它是一个接口类,有两个实现类FastFallbackExchangeFinder,SequentialExchangeFinder。这两个实现类我们等会再来看具体内部是如何实现的。
先来看下ExchangeFinder初始化的地方(实际是实现类的初始化):
fun enterNetworkInterceptorExchange(
request: Request,
newRoutePlanner: Boolean,
chain: RealInterceptorChain,
) {
//前方省略一波代码
···
if (newRoutePlanner) {
val routePlanner = RealRoutePlanner(
client,
createAddress(request.url),
this,
chain,
connectionListener = connectionPool.connectionListener
)
this.exchangeFinder = when {
client.fastFallback -> FastFallbackExchangeFinder(routePlanner, client.taskRunner)
else -> SequentialExchangeFinder(routePlanner)
}
}
}
这个方法是在RetryAndFollowUpInterceptor中被调用的,为连接做准备。这里就可以看着ExchangeFinder的两个实现类的初始化,而且还看到都被传了个一个参数RealRoutePlanner,剧透一下,寻找连接RealConnection的主要判断逻辑都在这个类里面,这个我们稍后再说。这里我们先来看下createAddress()方法:
private fun createAddress(url: HttpUrl): Address {
var sslSocketFactory: SSLSocketFactory? = null
var hostnameVerifier: HostnameVerifier? = null
var certificatePinner: CertificatePinner? = null
if (url.isHttps) {
sslSocketFactory = client.sslSocketFactory
hostnameVerifier = client.hostnameVerifier
certificatePinner = client.certificatePinner
}
return Address(
uriHost = url.host,
uriPort = url.port,
dns = client.dns,
socketFactory = client.socketFactory,
sslSocketFactory = sslSocketFactory,
hostnameVerifier = hostnameVerifier,
certificatePinner = certificatePinner,
proxyAuthenticator = client.proxyAuthenticator,
proxy = client.proxy,
protocols = client.protocols,
connectionSpecs = client.connectionSpecs,
proxySelector = client.proxySelector
)
}
使用url和client配置创建一个Address实例。Address意思是指向服务的连接的地址,可以理解为请求地址及其配置。Address有一个重要作用:相同Address的HTTP请求 共享 相同的连接。这可以作为 HTTP1.1和HTTP2.0 复用连接 的请求的判断。
我们再来看下ExchangerFinder的两个实现类FastFallbackExchangeFinder,SequentialExchangeFinder是如何实现find()的:
先看FastFallbackExchangeFinder:
/*FastFallbackExchangeFinder*/
override fun find(): RealConnection {
var firstException: IOException? = null
try {
while (tcpConnectsInFlight.isNotEmpty() || routePlanner.hasNext()) {
if (routePlanner.isCanceled()) throw IOException("Canceled")
//如果已准备好就启动一个新连接
val now = taskRunner.backend.nanoTime()
var awaitTimeoutNanos = nextTcpConnectAtNanos - now
var connectResult: ConnectResult? = null
if (tcpConnectsInFlight.isEmpty() || awaitTimeoutNanos <= 0) {
connectResult = launchTcpConnect()
nextTcpConnectAtNanos = now + connectDelayNanos
awaitTimeoutNanos = connectDelayNanos
}
// 等待正在进行的连接完成或失败
if (connectResult == null) {
connectResult = awaitTcpConnect(awaitTimeoutNanos, TimeUnit.NANOSECONDS) ?: continue
}
if (connectResult.isSuccess) {
//已得到一个连接TCP成功的连接,取消和延迟所有丢失的竞争连接
cancelInFlightConnects()
// 如果是全新的连接需要走到这一步,进行TLS判断连接,如果是连接池中得到的连接则不用
if (!connectResult.plan.isReady) {
connectResult = connectResult.plan.connectTlsEtc()
}
if (connectResult.isSuccess) {
//在这里加入连接池
return connectResult.plan.handleSuccess()
}
}
val throwable = connectResult.throwable
if (throwable != null) {
if (throwable !is IOException) throw throwable
if (firstException == null) {
firstException = throwable
} else {
firstException.addSuppressed(throwable)
}
}
val nextPlan = connectResult.nextPlan
if (nextPlan != null)
//将下一个从竞争中胜出的连接加入到集合中(deferredPlans),下次获取连接时会进行尝试
routePlanner.deferredPlans.addFirst(nextPlan)
}
}
} finally {
cancelInFlightConnects()
}
throw firstException!!
}
再来看下SequentialExchangeFinder:
/*SequentialExchangeFinder*/
override fun find(): RealConnection {
var firstException: IOException? = null
while (true) {
if (routePlanner.isCanceled()) throw IOException("Canceled")
try {
val plan = routePlanner.plan()
if (!plan.isReady) {
val tcpConnectResult = plan.connectTcp()
val connectResult = when {
tcpConnectResult.isSuccess -> plan.connectTlsEtc()
else -> tcpConnectResult
}
val (_, nextPlan, failure) = connectResult
if (failure != null) throw failure
if (nextPlan != null) {
routePlanner.deferredPlans.addFirst(nextPlan)
continue
}
}
return plan.handleSuccess()
} catch (e: IOException) {
if (firstException == null) {
firstException = e
} else {
firstException.addSuppressed(e)
}
if (!routePlanner.hasNext()) {
throw firstException
}
}
}
}
说一下这两个实现类的区别:两者尝试获取连接的策略不同,FastFallbackExchangeFinder的策略每隔250ms异步启动一个新的尝试(连接到目标地址会有多个IP),只要其中一个IP返回连接成功就返回;SequentialExchangeFinder则是尝试一次路由一个,直到连接成功。所以明显FastFallbackExchangeFinder效率更高,也是OKhttp的默认的方式。
ExchangFinder的find()方法获取的RealConnection后,RealConnection又是如何通过newCodec()获取到ExchangeCodec实例?我们跟进去看下:
/*RealConnection*/
@Throws(SocketException::class)
internal fun newCodec(client: OkHttpClient, chain: RealInterceptorChain): ExchangeCodec {
val socket = this.socket!!
val source = this.source!!
val sink = this.sink!!
val http2Connection = this.http2Connection
return if (http2Connection != null) {
Http2ExchangeCodec(client, this, chain, http2Connection)
} else {
socket.soTimeout = chain.readTimeoutMillis()
source.timeout().timeout(chain.readTimeoutMillis.toLong(), MILLISECONDS)
sink.timeout().timeout(chain.writeTimeoutMillis.toLong(), MILLISECONDS)
Http1ExchangeCodec(client, this, source, sink)
}
}
代码很简单,判断了是http1还是http2,根据判断new出不同的实例并返回。
到这里,ConnectInterceptor的intercept中的大致流程算是梳理通顺了,但还有些细节需要弄清楚:
- 连接如何拿到的?
- 连接是如何复用的?
- DNS和Socket又是如何连接的?
如何得到连接
看前面FastFallbackExchangeFinder,FastFallbackExchangeFinder两个实现类中的find(),一路看下去都能看到这么一句代码:
routePlanner.plan()
routePlanner是一个RoutePlanner类型实例,RoutePlanner是个接口类,实现类其实是RealRoutePlanner,我们跟到plan()方法中看一下:
/*RealRoutePlanner*/
@Throws(IOException::class)
override fun plan(): Plan {
//1.返回已分配的连接
val reuseCallConnection = planReuseCallConnection()
if (reuseCallConnection != null) return reuseCallConnection
// 2.第一次尝试从连接池中获取连接
val pooled1 = planReusePooledConnection()
if (pooled1 != null) return pooled1
// 3.在新routes前尝试之前推迟连接
if (deferredPlans.isNotEmpty()) return deferredPlans.removeFirst()
// 4.通过routeSelector来获取到新的Route来进行Connection的建立
val connect = planConnect()
// 5.第二次尝试连接池中获取连接,如果获取到直接返回
val pooled2 = planReusePooledConnection(connect, connect.routes)
if (pooled2 != null) return pooled2
return connect
}
从上面流程可以看到,plan()方法做了以下几件事:
- 检查已分配过的Connection是否满足此次请求,如果满足直接返回。
- 检查当前连接池ConnectionPool中是否有满足此次请求的Connection。
- 在使用Route前,检查推迟连接(之前连接竞争中胜出但是未使用的连接)集合中是否有可用连接,尝试使用。
- 检查当前RouteSelector列表中,是否还有可用Route(Route是proxy,IP地址的包装类), 如果没有就发起DNS请求。
- 通过DNS获取到新的Route之后,第二次从ConnectionPool查找有无可复用的Connection,否则就创建新的RealConnection
可以看到plan()返回的是一个Plan类型,这是一个接口类,实际返回的是它的实现类ConnectPlan,`ConnectPlan官方解释是:单次连接服务器的尝试。它包含的步骤有:
- TCP握手
- [可选] 连接隧道(使用 HTTP 代理访问 HTTPS 服务器时我们必须发送“CONNECT”请求,并处理来自代理的授权质询)
- [可选] TLS握手
每个步骤都可能失败。如果可以重试,则会使用下一个Plan创建一个新实例,该将采用不同的配置。
回过头我们再来到FastFallbackExchangeFinder(FastFallbackExchangeFinder类似),当拿到表示连接的 Plan后会被包装成一个ConnectResult,这就是个data类:
/*FastFallbackExchangeFinder*/
private fun launchTcpConnect(): ConnectResult? {
val plan = when {
routePlanner.hasNext() -> {
try {
routePlanner.plan()
} catch (e: Throwable) {
FailedPlan(e)
}
}
else -> return null // Nothing further to try.
}
// Already connected. Return it immediately.
if (plan.isReady) return ConnectResult(plan)
// Already failed? Return it immediately.
if (plan is FailedPlan) return plan.result
// Connect TCP asynchronously.
tcpConnectsInFlight += plan
val taskName = "$okHttpName connect ${routePlanner.address.url.redact()}"
taskRunner.newQueue().schedule(object : Task(taskName) {
override fun runOnce(): Long {
val connectResult = try {
plan.connectTcp()
} catch (e: Throwable) {
ConnectResult(plan, throwable = e)
}
// Only post a result if this hasn't since been canceled.
if (plan in tcpConnectsInFlight) {
connectResults.put(connectResult)
}
return -1L
}
})
return null
}
接着就将ConnectResult返回到find()中,如果你还记得上面FastFallbackExchangeFinder的find()方法的话,里面有这么一段代码:
/*FastFallbackExchangeFinder*/
override fun find(): RealConnection {
...
try {
while (tcpConnectsInFlight.isNotEmpty() || routePlanner.hasNext()) {
...
// 等待正在进行的连接完成或失败
if (connectResult == null) {
connectResult = awaitTcpConnect(awaitTimeoutNanos, TimeUnit.NANOSECONDS) ?: continue
}
if (connectResult.isSuccess) {
//已得到一个连接TCP成功的连接,取消和延迟所有丢失的竞争连接
cancelInFlightConnects()
// 如果是全新的连接需要走到这一步,进行TLS判断连接,如果是连接池中得到的连接则不用
if (!connectResult.plan.isReady) {
connectResult = connectResult.plan.connectTlsEtc()
}
if (connectResult.isSuccess) {
//在这里加入连接池
return connectResult.plan.handleSuccess()
}
}
val throwable = connectResult.throwable
if (throwable != null) {
···
if (nextPlan != null)
//将下一个从竞争中胜出的连接加入到集合中(deferredPlans),下次获取连接时会进行尝试(上面获取连接时的第三步判断)
routePlanner.deferredPlans.addFirst(nextPlan)
···
}
} finally {
cancelInFlightConnects()
}
···
}
经过一系列连接、判断操作,最终执行到connectResult.plan.handleSuccess(),表示连接成功,并且将新建的连接放到连接池。
/*ConnectPlan*/
override fun handleSuccess(): RealConnection {
···
//如果连接池中匹配到相同连接,则合并连接。
val pooled3 = routePlanner.planReusePooledConnection(this, routes)
if (pooled3 != null) return pooled3.connection
//连接池中未匹配到相同连接,将该连接加入连接池
synchronized(connection) {
client.connectionPool.delegate.put(connection)
call.acquireConnectionNoEvents(connection)
}
···
return connection
}
连接复用
从上面的获取连接的步骤可以看到,第二步和第五步对ConnectionPool做了两次复用检查,且拿到Plan后当连接成功将新建的连接放入到ConnectionPool中。
因此这里就是OkHttp的连接复用其实是通过ConnectionPool来实现的,我们追溯ConnectionPool代码会发现它内部拥有个代理对象delegate,如同它的名字RealConnectionPool,这才是真正的连接池。
/*ConnectionPool*/
class ConnectionPool internal constructor(
internal val delegate: RealConnectionPool
)
继续追到里面会发现RealConnectionPool内部有一个connections的ConcurrentLinkedQueue对象就是用来保存缓存的连接。
/*RealConnectionPool*/
private val connections = ConcurrentLinkedQueue<RealConnection>()
DNS 过程
从前面分析的步骤可知,Dns的过程隐藏在了第四步RouteSelector检查中,其实也就是在 planConnect()中:
/*RealRoutePlanner*/
@Throws(IOException::class)
private fun planConnect(): ConnectPlan {
//使用来自前面合并连接的route
val localNextRouteToTry = nextRouteToTry
if (localNextRouteToTry != null) {
nextRouteToTry = null
return planConnectToRoute(localNextRouteToTry)
}
// 使用从现有route选项中获取的route
val existingRouteSelection = routeSelection
if (existingRouteSelection != null && existingRouteSelection.hasNext()) {
return planConnectToRoute(existingRouteSelection.next())
}
// 确定要使用的代理(如果有)。这可能会在 ProxySelector.select() 中阻塞。
var newRouteSelector = routeSelector
if (newRouteSelector == null) {
newRouteSelector = RouteSelector(
address = address,
routeDatabase = call.client.routeDatabase,
call = call,
fastFallback = client.fastFallback,
eventListener = call.eventListener
)
routeSelector = newRouteSelector
}
// 列出当前代理的可用 IP 地址。这可能会在 Dns.lookup() 中阻塞。
if (!newRouteSelector.hasNext()) throw IOException("exhausted all routes")
val newRouteSelection = newRouteSelector.next()
routeSelection = newRouteSelection
if (call.isCanceled()) throw IOException("Canceled")
return planConnectToRoute(newRouteSelection.next(), newRouteSelection.routes)
}
不是很好理解,我们先搞明白RouteSelector, RouteSelection,Route这三个类的关系,就比较容易理解了,下面给出三个类之间的关系图:

根据上图结合源码梳理一下:
RouteSelector在调用next遍历在不同proxy情况下获得下一个Selection封装类,Selection持有一个Route的列表,也就是每个proxy都对应有Route列表。Selection其实就是针对List封装的一个迭代器,通过next()方法获得下一个Route,Route持有proxy、address和inetAddress,可以理解为Route就是针对IP和Proxy配对的一个封装。RouteSelector的next()方法内部调用了nextProxy(),nextProxy()又会调用resetNextInetSocketAddres()方法。resetNextInetSocketAddres通过address.dns.lookup获取InetSocketAddress,也就是IP地址。
通过上面的梳理我们知道,IP地址最终是通过address内的dns获取到的,而这个dns又是怎么构建的呢?
在分析ExchangeFinder时,大家还记得展示的createAddress()的代码嘛?不记得可以返回去看一下,从这段代码我们就能知道在创建address时,将内置的client.dns传递进来,而client.dns是在OkHttpclient的构建过程中传递进来Dns.System,里面的lookup是通过InetAddress.getAllByName 方法获取到对应域名的IP,也就是默认的Dns实现。
至此,整个DNS的过程就真相大白了。
建立Socket连接
通过Dns获得Connectoin之后,就是建立连接的过程了。此时我们又要回到FastFallbackExchangeFinder,如果还有印象,在分析如何得到连接的时候,有带着大家看过这么一个方法launchTcpConnect(),里面当拿到的是一个新连接的时候,会执行这么一段代码:
/*FastFallbackExchangeFinder*/
// Connect TCP asynchronously.
private fun launchTcpConnect(): ConnectResult? {
···
taskRunner.newQueue().schedule(object : Task(taskName) {
override fun runOnce(): Long {
···
//执行ConnectPlan内的connectTcp()进行TCP连接
plan.connectTcp()
···
return -1L
}
})
return null
}
可以看到执行了 plan.connectTcp(),我们跟进去看一下:
/*ConnectPlan*/
override fun connectTcp(): ConnectResult {
check(rawSocket == null) { "TCP already connected" }
var success = false
//告知call有关连接call的信息,以便异步取消工作
call.plansToCancel += this
try {
eventListener.connectStart(call, route.socketAddress, route.proxy)
connectionListener.connectStart(route, call)
//建立socket连接
connectSocket()
success = true
return ConnectResult(plan = this)
} catch (e: IOException) {
eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)
connectionListener.connectFailed(route, call, e)
return ConnectResult(plan = this, throwable = e)
} finally {
call.plansToCancel -= this
if (!success) {
//清理资源
rawSocket?.closeQuietly()
}
}
}
connectTcp方法并不复杂,先做了简单的判断,调用了一些回调代码,然后调用系统方法建立Socket连接。
在FastFallbackExchangeFinder的find()方法能看到,在connectTcp后,还会继续调用一个ConnectPlan中的另一个方法connectTlsEtc(),主要是如果请求是https请求,还需要一个TLS的连接建立。
CallServerInterceptor(请求服务拦截器)
CalllServerInterceptor是最后一个拦截器了,前面的拦截器已经完成了socket连接和tls连接,那么这一步就是传输http的头部和body数据了。
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.exchange!!//上个拦截器传入的exchange
val request = realChain.request
val requestBody = request.body
val sentRequestMillis = System.currentTimeMillis()
var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
var sendRequestException: IOException? = null
try {
//写请求头
exchange.writeRequestHeaders(request)
//含body的请求
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// 若请求头包含 "Expect: 100-continue" , 就会等服务端返回含有 "HTTP/1.1 100 Continue"的响应,然后再发送请求body.
//如果没有收到这个响应(例如收到的响应是4xx),那就不发送body了。
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
//responseBuilder为null说明服务端返回了100,也就是可以继续发送body了
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// 满足了 "Expect: 100-continue" ,写请求body
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
//没有满足 "Expect: 100-continue" ,请求发送结束
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
//请求发送结束
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
} catch (e: IOException) {
if (e is ConnectionShutdownException) {
throw e // No request was sent so there's no response to read.
}
if (!exchange.hasFailure) {
throw e // Don't attempt to read the response; we failed to send the request.
}
sendRequestException = e
}
try {
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
//回调 读响应头开始事件(如果上面没有)
if (invokeStartEvent) {
exchange.responseHeadersStart()
invokeStartEvent = false
}
}
//构建response
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
//这里服务端又返回了个100,就再尝试获取真正的响应()
if (shouldIgnoreAndWaitForRealResponse(code, exchange)) {
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
exchange.responseHeadersStart()
}
response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
code = response.code
}
//回调读响应头结束
exchange.responseHeadersEnd(response)
//这里就是获取响应body了
response = if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response.stripBody()
} else {
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
}
//请求头中Connection是close,表示请求完成后要关闭连接
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
"close".equals(response.header("Connection"), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
//204(无内容)、205(充值内容),body应该是空
if ((code == 204 || code == 205) && response.body.contentLength() > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body.contentLength()}")
}
return response
} catch (e: IOException) {
if (sendRequestException != null) {
sendRequestException.addSuppressed(e)
throw sendRequestException
}
throw e
}
}
private fun shouldIgnoreAndWaitForRealResponse(code: Int, exchange: Exchange): Boolean = when {
// Server sent a 100-continue even though we did not request one. Try again to read the
// actual response status.
code == 100 -> true
// Handle Processing (102) & Early Hints (103) and any new codes without failing
// 100 and 101 are the exceptions with different meanings
// But Early Hints not currently exposed
code in (102 until 200) -> true
else -> false
}
}
整个内容就是前面说的一句话:写入http请求的header和body、读取响应的header和body。这里就不再解释了。
这里我们可以看到,无论写请求还是读响应,都是使用Exchange对应的方法。上面也提到过Exchange理解上是对ExchangeCodec的包装,这写方法内部除了事件回调和一些参数获取外,核心工作都由 ExchangeCodec 对象完成,而 ExchangeCodec实际上利用的是 Okio,而 Okio 实际上还是用的 Socket。
ExchangeCodec的实现类 有对应Http1.1的Http1ExchangeCodec 和 对应Http2.0的Http2ExchangeCodec。其中Http2ExchangeCodec是使用Http2.0中 数据帧 的概念完成请求响应的读写。关于Http1ExchangeCodec、Http2ExchangeCodec具体实现原理涉及okio这不再展开。
CallServerInterceptor的intercept方法中没有调用连接器链Chain的proceed方法,因为这是最后一个拦截器了。
总结
通过本篇的学习,我们知道了ConnectInterceptor负责连接的获取,其中涉及到连接池的概念;CallServerInterceptor是真正的网络IO读写。ConnectInterceptor涉及的内容较多,它是Okhttp的核心。结合上篇,我们我们已经分析完了Okhttp内部所有的拦截器。