前言
上一篇文章我们分析了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内部所有的拦截器。