对于OKHttp的源码解析,我一共分了三章
这一章我们主要来了解一下拦截器
拦截器
按照拦截器的添加顺序来看:
- client.interceptors():这是由开发者设置的,会在所有的拦截器处理之前进行最早的拦截器处理,可用于添加一些公共参数,如 自定义header、自定义log、参数判断等。
- RetryAndFollowUpInterceptor:重试与重定向拦截器。会对连接做一些初始化工作,以及请求失败的重试工作,重定向的后续请求操作
- BridgeInterceptor:桥接拦截器。主要是帮我们生成HTTP请求头的内容。
- CacheInterceptor:缓存拦截器。在真正向服务器发起请求之前,先判断是否命中缓存。缓存如果命中,则直接使用缓存的内容,不用再向服务器发起请求。从而减小网络访问的开销。
- ConnectInterceptor:负责建立连接。
- client.networkInterceptors():开发者自己设置的,这个拦截器一般做网络调试用,用户自定义,插在连接拦截器和请求服务拦截器之间。
- CallServerInterceptor():向服务器发起请求并读取服务器的响应。
RetryAndFollowUpInterceptor 重试与重定向拦截器
主要处理一下几个问题
- 1.异常,或者协议重试(408客户端超时,权限问题,503服务暂时不处理,retry-after为0)
- 2.重定向
- 3.重试的次数不能超过20次。
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
var request = chain.request
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
var newExchangeFinder = true
var recoveredFailures = listOf<IOException>()
while (true) {
//这里会新建一个ExchangeFinder,ConnectInterceptor会使用到
call.enterNetworkInterceptorExchange(request, newExchangeFinder)
var response: Response
var closeActiveExchange = true
try {
if (call.isCanceled()) {
throw IOException("Canceled")
}
try {
response = realChain.proceed(request)
newExchangeFinder = true
} catch (e: RouteException) {
// 尝试通过路由连接失败,该请求将不会被发送
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} catch (e: IOException) {
//尝试与服务器通信失败。该请求可能已发送。
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
// 尝试关联上一个response,注意:body是为null
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = call.interceptorScopedExchange
// 会根据 responseCode 来判断,构建一个新的request并返回来重试或者重定向
val followUp = followUpRequest(response, exchange)
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
call.timeoutEarlyExit()
}
closeActiveExchange = false
return response
}
// 如果请求体是一次性的, 不需要再次重试
val followUpBody = followUp.body
if (followUpBody != null && followUpBody.isOneShot()) {
closeActiveExchange = false
return response
}
response.body?.closeQuietly()
// 最大重试次数,不同的浏览器是不同的
// MAX_FOLLOW_UPS Chrome 21次; Firefox 20次; Safari 16次; HTTP1.0 建议5次。
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
// 判断是否需要重连, false->不尝试重连 true->尝试重连
private fun recover(
e: IOException,
call: RealCall,
userRequest: Request,
requestSendStarted: Boolean
): Boolean {
// 客户端禁止重连
// The application layer has forbidden retries.
if (!client.retryOnConnectionFailure) return false
// 不能再次发送该请求体
// We can't send the request body again.
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
// 发生的异常是致命的,无法恢复的
// This exception is fatal.
if (!isRecoverable(e, requestSendStarted)) return false
// 没有更多的路由尝试重连
// No more routes to attempt.
if (!call.retryAfterFailure()) return false
// 对于故障恢复,将相同的路由选择器与新连接一起使用
// For failure recovery, use the same route selector with a new connection.
return true
}
...省略代码...
}
�
- 调用RealCall的enterNetworkInterceptorExchange方法实例化一个ExchangeFinder在RealCall对象中。
- 执行RealCall的proceed 方法,进入下一个拦截器,进行下一步的请求处理。
- 如果出现路由异常,则通过recover方法校验,当前的连接是否可以重试,不能重试则抛出异常,离开当前的循环。
BridgeInterceptor 桥接拦截器
主要处理了如下几个问题:
- 主要将Content-Type、Content-Length、Host等一些数据添加到头部。
- 拿到数据之后对数据进行处理,判断是否为gzip,进行对数据数据解压。
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
// 获取原始的请求数据
val userRequest = chain.request()
// 在原始的request的参数基础上创建一个新的 builder
val requestBuilder = userRequest.newBuilder()
// 获取原始请求的body
// 重新构建请求 添加一些必要的请求头信息
val body = userRequest.body
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")
}
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
// 添加cookie
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
// 添加 user-Agent
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
// 使用新的builder构建一个新的request,然后执行下一个拦截器来处理请求
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
// 创建一个新的responseBuilder,目的是将原始请求数据构建到response中
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
// 修改response header信息,移除Content-Encoding,Content-Length信息
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
// 修改response body信息
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
/** Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`. */
private fun cookieHeader(cookies: List<Cookie>): String = buildString {
cookies.forEachIndexed { index, cookie ->
if (index > 0) append("; ")
append(cookie.name).append('=').append(cookie.value)
}
}
}
- 设置头部的Content-Type.说明内容类型是什么
- 如果contentLength大于等于0,则设置头部的Content-Length(说明内容大小是多少);否则设置头部的Transfer-Encoding为chunked(说明传输编码为分块传输)
- 如果Host不存在,设置头部的Host(在Http 1.1之后出现,可以通过同一个URL访问到不同主机,从而实现服务器虚拟服务器的负载均衡。如果1.1之后不设置就会返回404)。
- 如果Connection不存在,设置头部的Connection为Keep-Alive(代表连接状态需要保持活跃)
- 如果Accept-Encoding且Range为空,则强制设置Accept-Encoding为gzip(说明请求将会以gzip方式压缩)
- 从CookieJar的缓存中取出cookie设置到头部的Cookie
- 如果User-Agent为空,则设置User-Agent到头部
CacheInterceptor 缓存拦截器
用户通过OkHttpClient.cache来配置缓存,缓存拦截器通过CacheStrategy来判断是使用网络还是缓存来构建response
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
// 通过request从OkHttpClient.cache中获取缓存
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
// 创建缓存策略
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
// 为空表示不使用网络,反之,则表示使用网络
val networkRequest = strategy.networkRequest
// 为空表示不使用缓存,反之,则表示使用缓存
val cacheResponse = strategy.cacheResponse
// 追踪网络与缓存的使用情况
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
// 有缓存 但是不适用,关闭它
if (cacheCandidate != null && cacheResponse == null) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body?.closeQuietly()
}
// 如果网络被禁止,但是缓存又是空的,构建一个code为504的response,并返回
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
// 如果我们禁用了网络不使用网络,且有缓存,直接根据缓存内容构建并返回response
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
// 为缓存添加监听
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = null
try {
//责任链往下处理,从服务器返回response 赋值给 networkResponse
networkResponse = chain.proceed(networkRequest)
} finally {
// 捕获I/O或其他异常,请求失败,networkResponse为空,且有缓存的时候,不暴露缓存内容
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
// 否则关闭缓存响应体
cacheCandidate.body?.closeQuietly()
}
}
// 如果有缓存
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
// 且网络返回response code为304的时候,使用缓存内容新构建一个Response返回。
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
networkResponse.body!!.close()
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
// 否则关闭缓存响应体
cacheResponse.body?.closeQuietly()
}
}
// 构建网络请求的response
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
// 如果cache不为null,即用户在OkHttpClient中配置了缓存,则将上一步新构建的网络请求response存到cache中
if (cache != null) {
// 根据response的code,header以及CacheControl.noStore来判断是否可以缓存
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// 将该response存入缓存
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
// 根据请求方法来判断缓存是否有效,只对Get请求进行缓存,其它方法的请求则移除
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
// 缓存无效,将该请求缓存从client缓存配置中移除
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
}
网络请求前:
- 首先根据request从OkHttpClient.cache中获取缓存,通过CacheStrategy获取本次请求的请求体及缓存的响应体。
- 如果 请求体networkRequest和响应体cacheResponse都为空的话,则返回错误码为 504
- 如果 请求体networkRequest为空 响应体cacheResponse不为空的话,则将该响应体返回
- 如果请求体networkRequest不为空的话,则进入下一个拦截器。
网络请求后:
- 如果当前cacheResponse不为空,且 networkResponse状态码为304, 则代表数据没有变化,那么就会根据 cacheResponse 构建一个新的 response,根据当前时间更新到缓存当中,并返回到上一拦截器中
- 如果networkResponse状态码不为304,则判断是否进行缓存,最后返回到上一拦截器中
ConnectInterceptor 连接拦截器
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
// 初始化一个 exchange对象
val exchange = realChain.call.initExchange(chain)
// 根据这个exchange对象 来复制创建一个新的连接责任链
val connectedChain = realChain.copy(exchange = exchange)
// 执行该连接责任链
return connectedChain.proceed(realChain.request)
}
}
这里核心的一个东西是 exchange,让我们来看一下 initExchange这个方法
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
// 这里的 exchangeFinder 就是在 RetryAndFollowUpInterceptor中创建的
val exchangeFinder = this.exchangeFinder!!
// 返回一个ExchangeCodec,
// ExchangeCodec 是一个编解码器,对request进行编码 并对Response进行解码
val codec = exchangeFinder.find(client, chain)
// 根据exchangeFinder与codec新构建一个Exchange对象,并返回
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()
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
// 查找一个合格可用的连接,返回一个 RealConnection 对象
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
// 根据连接,创建并返回一个请求响应编码器
// Http1ExchangeCodec 或者 Http2ExchangeCodec,分别对应Http1协议与Http2协议
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
findHealthyConnection()
// 查找连接,如果连接正常,则返回该连接。如果它不正常,则重复该过程,直到找到正常的连接。
private fun findHealthyConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
doExtensiveHealthChecks: Boolean
): RealConnection {
while (true) {
// 查找连接
val candidate = findConnection(
connectTimeout = connectTimeout,
readTimeout = readTimeout,
writeTimeout = writeTimeout,
pingIntervalMillis = pingIntervalMillis,
connectionRetryEnabled = connectionRetryEnabled
)
// 确认连接是否可用,可用则直接返回
// Confirm that the connection is good.
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate
}
// 如果不合格,则标记为不可用,从连接池中移除
// If it isn't, take it out of the pool.
candidate.noNewExchanges()
// Make sure we have some routes left to try. One example where we may exhaust all the routes
// would happen if we made a new connection and it immediately is detected as unhealthy.
if (nextRouteToTry != null) continue
val routesLeft = routeSelection?.hasNext() ?: true
if (routesLeft) continue
val routesSelectionLeft = routeSelector?.hasNext() ?: true
if (routesSelectionLeft) continue
throw IOException("exhausted all routes")
}
}
findConnection()
private fun findConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean
): RealConnection {
if (call.isCanceled()) throw IOException("Canceled")
// 先重试重连 call 中的connection,不需要重新去获取连接
// Attempt to reuse the connection from the call.
val callConnection = call.connection // This may be mutated by releaseConnectionNoEvents()!
if (callConnection != null) {
var toClose: Socket? = null
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
// 如果 call中的 connection没有被释放,则重用它
// If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here
// because we already acquired it.
if (call.connection != null) {
check(toClose == null)
return callConnection
}
// 如果 call 中的 connection 已经被释放了,则关闭 socket
// The call's connection was released.
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
// 需要一个新的连接,所以重置一些状态
// We need a new connection. Give it fresh stats.
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
// 尝试从连接池中获取一个连接
// Attempt to get a connection from the pool.
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
// 如果连接池中是空的,准备下次尝试连接的路由
// Nothing in the pool. Figure out what route we'll try next.
val routes: List<Route>?
val route: Route
if (nextRouteToTry != null) {
// Use a route from a preceding coalesced connection.
routes = null
route = nextRouteToTry!!
nextRouteToTry = null
} else if (routeSelection != null && routeSelection!!.hasNext()) {
// Use a route from an existing route selection.
routes = null
route = routeSelection!!.next()
} else {
// Compute a new route selection. This is a blocking operation!
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes
if (call.isCanceled()) throw IOException("Canceled")
// 第三次,再次尝试从连接池中获取一个连接,带路由,不带多路复用
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. We have a better chance of matching thanks to connection coalescing.
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
route = localRouteSelection.next()
}
// 第四次,手动创建一个新连接
// Connect. Tell the call about the connecting call so async cancels work.
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
try {
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
// 第五次,再次尝试从连接池中获取一个连接,带路由,带多路复用
// 这一步主要是为了校验一下,比如已经有了一条连接了,就可以直接复用,而不用使用手动创建的连接
// If we raced another call connecting to this host, coalesce the connections. This makes for 3
// different lookups in the connection pool!
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
val result = call.connection!!
nextRouteToTry = route
newConnection.socket().closeQuietly()
eventListener.connectionAcquired(call, result)
return result
}
synchronized(newConnection) {
// 将手动创建的新连接放入连接池
connectionPool.put(newConnection)
call.acquireConnectionNoEvents(newConnection)
}
eventListener.connectionAcquired(call, newConnection)
return newConnection
}
- 第一次,尝试重连 call 中的 connection,不需要去重新获取连接。
- 第二次,尝试从连接池中获取一个连接,不带路由,不带多路复用。
- 第三次,再次尝试从连接池中获取一个连接,带路由,不带多路复用。
- 第四次,手动创建一个新连接。
- 第五次,再次尝试从连接池中获取一个连接,带路由,带多路复用。
到这里,就算建立了连接
CallServerInterceptor
将请求头和请求体发送给服务器,并解析服务器返回的response
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!!
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)
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// 当请求头为"Expect: 100-continue"时,在发送请求体之前需要等待服务器返回"HTTP/1.1 100 Continue" 的response,如果没有等到该response,就不发送请求体。
// POST请求,先发送请求头,在获取到100继续状态后继续发送请求体
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
// 刷新请求,即发送请求头
exchange.flushRequest()
// 解析响应头
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
// 写入请求体
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"响应,写入请求体
// Write the request body if the "Expect: 100-continue" expectation was met.
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
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
if (shouldIgnoreAndWaitForRealResponse(code)) {
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)
response = if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
}
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
"close".equals(response.header("Connection"), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 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): 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
}
}
写入请求头,然后根据条件是否写入发送请求体,请求结束 解析服务器返回的响应头,然后构建一个新的Response,并返回 这里是拦截器责任链的最后一个拦截器,所以不会再调用chain.proceed 方法,而是直接将这个response往上传递。
以上就是所有的拦截器的源码解析。