本文源码为okhttp:4.9.0版本。
implementation("com.squareup.okhttp3:okhttp:4.9.0")
基本用法
创建请求Request后,要用OkHttpClient的newCall()方法创建一个RealCall对象,然后调用execute()发起同步请求或调用enqueue()发起异步请求。
//1.创建请求(包含url,method,headers,body)
val request = Request
.Builder()
.url("https://developer.android.google.cn/")
.build()
//2.创建OkHttpClient (包含调度器、拦截器、DNS等)
val okHttpClient = OkHttpClient.Builder().build()
//3.创建Call(用于调用请求)
val newCall = okHttpClient.newCall(request)
//4.通过异步请求数据
newCall.enqueue(object :Callback{
override fun onFailure(call: Call, e: IOException) {}
override fun onResponse(call: Call, response: Response) {}
})
//4.通过同步请求数据
val response = newCall.execute()
请求调度器
网络请求主要分为同步请求和异步请求,Dispatcher主要用于控制并发的请求,无论是同步请求还是异步请求,都会通过Dispatcher来处理。
// Dispatcher.kt 源码
class Dispatcher constructor() {
// 最大并发执行的请求数
@get:Synchronized var maxRequests = 64
//每个主机的最大任务请求数
@get:Synchronized var maxRequestsPerHost = 5
//执行异步请求的线程池
private var executorServiceOrNull: ExecutorService? = null
@get:Synchronized
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
// 默认创建线程池
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
//准备运行的异步请求队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
//正在运行的异步请求队列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
//正在运行的同步请求队列
private val runningSyncCalls = ArrayDeque<RealCall>()
constructor(executorService: ExecutorService) : this() {
this.executorServiceOrNull = executorService
}
fun executorService(): ExecutorService = executorService
}
请求分发机制
请求操作 Call
RealCall实现了Call接口,也是这个接口唯一的实现类,RealCall是一个OkHttp应用与网络层之间的桥梁。
RealCall
可以理解为同步请求操作,RealCall的内部类
AsyncCall
可以理解异步请求操作。
- 发起同步请求execute()
// RealCall.kt 源码
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
// 超时就关闭socket/流
timeout.enter()
callStart()
try {
// 把当前RealCall 添加到同步请求操作队列
client.dispatcher.executed(this)
// 返回Response
return getResponseWithInterceptorChain()
} finally {
// 把当前RealCall 从同步请求操作队列中删除
client.dispatcher.finished(this)
}
}
// Dispatcher.kt 源码
@Synchronized internal fun executed(call: RealCall) {
runningSyncCalls.add(call)
}
在上面的代码中,execute()方法timeout.enter()这行代码调用AsyncTimeout的enter()方法让AsyncTimeout在请求超时的时候关闭Socket或流。在RealCall的execute()方法调用完enter()方法后,会调用Dispatcher的executed()把请求加入同步请求队列,然后调getResponseWithInterceptorChain()方法获取响应,获取到响应后,就会让Dispatcher把请求从同步请求操作队列中移除。
- 发起异步请求enqueue()
// RealCall.kt 源码
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
// Dispatcher.kt 源码
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
// 把AsyncCall 添加到待执行的异步请求队列中
readyAsyncCalls.add(call)
//不是WebSocket 连接请求
if (!call.call.forWebSocket) {
// 找出与当前请求地址的主机相同 AsyncCall
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
// 找出并执行可执行的请求
promoteAndExecute()
}
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
// 并发执行的请求数超出最大的请求数64
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
// 主机的并发请求数超过最大请求数5
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
// 把asyncCall 加入runningAsyncCalls 中
runningAsyncCalls.add(asyncCall)
}
//
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
// 把asyncCall提交到线程池executorService
asyncCall.executeOn(executorService)
}
return isRunning
}
在上面的代码中,RealCall的enqueue()方法会创建一个异步请求操作AsyncCall,并把它交给Dispatcher处理。AsyncCall实现了Runnable接口,Dispatcher接收到AsyncCall后,会把AsyncCall添加到待执行异步请求队列readyAsyncCalls中,然后调用自己的promoteAndExecute()方法。在这个方法中遍历readyAsyncCalls队列,筛选出符合条件并发执行的请求数要小于最大的请求数64并且主机的并发请求数不能超过最大请求数5的AsyncCall,把AsyncCall加入runningAsyncCalls队列后,将有效的请求筛选出后并保存,立即开始遍历请求,利用调度器Dispatcher里的ExecutorService进行Runnable任务,也就是遍历后加入到线程池中执行这些有效的网络请求。
// RealCall.kt 源码
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
//将当前Runnable放到线程池中运行
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
// 失败的回调
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
// 如果失败,从执行异步请求队列中删除
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
// 判断是否超时
timeout.enter()
try {
//通过拦截器链来得到网络响应
val response = getResponseWithInterceptorChain()
signalledCallback = true
// 成功的回调
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
// 失败的回调
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
// 失败的回调
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
// 把请求从正在运行异步请求队列 runningAsyncCalls 中移除
client.dispatcher.finished(this)
}
}
}
}
AsyncCall类executeOn()方法主要是正在运行异步请求队列放入线程池,由线程池开启线程执行任务;run()方法是线程池中执行的请求任务,通过getResponseWithInterceptorChain()得到网络请求结果response。
异步请求执行流程:
- 拦截器链
// RealCall.kt 源码
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
//创建拦截器集合
val interceptors = mutableListOf<Interceptor>()
//添加用户设置的应用拦截器
interceptors += client.interceptors
//负责重试和重定向
interceptors += RetryAndFollowUpInterceptor(client)
//用于桥接应用层和网络层的请求数据
interceptors += BridgeInterceptor(client.cookieJar)
//用于处理缓存
interceptors += CacheInterceptor(client.cache)
//网络连接拦截器,用于获取一个连接
interceptors += ConnectInterceptor
if (!forWebSocket) {
//添加用户设置的网络拦截器
interceptors += client.networkInterceptors
}
//用于请求网络并获取网络响应
interceptors += CallServerInterceptor(forWebSocket)
//创建职责链
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
//启动职责链
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
getResponseWithInterceptorChain()方法内部实现是通过一个
责任链模式
来完成,将网络请求的各个阶段封装到各个链条中,首先会创建一个interceptors列表,然后添加拦截器到列表中,再用interceptors创建一个拦截器链RealInterceptorChain,然后调用拦截器链的proceed()方法。
// RealInterceptorChain.kt 源码
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
// 调用了RealInterceptorChain的copy方法,
// 其内部会新建一个RealInterceptorChain,
// 通过参数index+1来循环interceptors中的拦截器
val next = copy(index = index + 1, request = request)
// 获取当前要执行的拦截器
val interceptor = interceptors[index]
// 运行当前的拦截器,并设置了下个拦截器。
// 其内部的逻辑通常是:当前拦截器处理完成后会接着执行下个拦截器的proceed方法
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
proceed()方法是责任链模式的核心所在,将请求移交给下一个拦截器。
拦截器 | 作用 |
---|---|
应用拦截器 | 获取到原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等 |
RetryAndFollowUpInterceptor(重试重定向拦截器) | 负责在请求失败时重试和重定向 |
BridgeInterceptor(桥接拦截器) | 应用层和网络层的桥接拦截器,主要工作是为请求添加cookie、添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要进行解压 |
CacheInterceptor(缓存拦截器) | 负责读取和更新缓存,可以配置自定义的缓存拦截器 |
ConnectInterceptor(连接拦截器) | 连接拦截器,内部会维护一个连接池,负责连接复用、创建连接(三次握手等等)、释放连接以及创建连接上的socket流 |
networkInterceptors(网络拦截器) | 用户自定义拦截器,通常用于监控网络层的数据传输 |
CallServerInterceptor(请求服务拦截器) | 拦截器链的最后的拦截器,用于向服务端发送数据并获取响应 |
拦截器
RetryAndFollowUpInterceptor
负责在请求失败时重试和重定向。
RetryAndFollowUpInterceptor的intercept()方法中的代码是放在while中执行的,只有当重试的条件不成立时,请求才会被中断,而且这个拦截器没有设定重试次数的上限,最大重定向次数是写死的20次,如果有特殊需求的话,则要自定义一个重试拦截器和重定向拦截器。
- 重试机制
// RetryAndFollowUpInterceptor.kt 源码
@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) {
// 初始化RealCall 的ExchangeFinder,它的作用是查找可重用的连接。
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) {
//通过路由连接的尝试失败。请求将不会被发送。
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} catch (e: IOException) {
// 试图与服务器通信失败。请求可能已经发送。
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
// 上一个响应
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
// 数据交换器
val exchange = call.interceptorScopedExchange
// followUp重定向请求;followUpRequest()根据不同的code 确定是否需要创建newRequest
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=20
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
上面源码中,在重试与重定向拦截器的intercept()方法中,当请求在后续的拦截器中处理时遇到路线异常(RouteException)或IO异常(IOException时)才会调用recover()方法判断是否要重试,不重试则抛出异常。
// RetryAndFollowUpInterceptor.kt 源码
private fun recover(e: IOException,call: RealCall,
userRequest: Request,requestSendStarted: Boolean
): Boolean {
// 应用层禁止重试;OkHttpClient 的 retryOnConnectionFailure 的值为 false
if (!client.retryOnConnectionFailure) return false
// 不能再次发送请求体;
// 1)请求执行过程中遇到 IO 异常(不包括 Http2Connection 抛出的 ConnectionShutdownException)
// 2)requestIsOneShot() 返回 true,这个方法默认为 false ,除非我们自己重写了这个方法
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
// 致命异常;
// 1)协议异常 ProtocalException
// 2)Socket 超时异常 SocketTimeoutException
// 3)证书验证异常 CertificateExeption
// 4)SSL 对端验证异常 SSLPeerUnverifiedException
if (!isRecoverable(e, requestSendStarted)) return false
// 没有更多路线可重试
// 1) 给 OkHttpClient 设置了代理
// 2) DNS 服务器返回多个 IP 地址
if (!call.retryAfterFailure()) return false
return true
}
- 重定向机制
// RetryAndFollowUpInterceptor.kt 源码
@Throws(IOException::class)
private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
val route = exchange?.connection?.route()
val responseCode = userResponse.code
val method = userResponse.request.method
when (responseCode) {
// 407 HTTP代理验证
HTTP_PROXY_AUTH -> {
val selectedProxy = route!!.proxy
if (selectedProxy.type() != Proxy.Type.HTTP) {
throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
}
return client.proxyAuthenticator.authenticate(route, userResponse)
}
// 401 未经授权的
HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)
//临时重定向 307~308,300~303
HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
// 构建重定向请求
return buildRedirectRequest(userResponse, method)
}
//客服端超时 408
HTTP_CLIENT_TIMEOUT -> {
// 408's are rare in practice, but some servers like HAProxy use this response code. The
// spec says that we may repeat the request without modifications. Modern browsers also
// repeat the request (even non-idempotent ones.)
if (!client.retryOnConnectionFailure) {
// The application layer has directed us not to retry the request.
return null
}
val requestBody = userResponse.request.body
if (requestBody != null && requestBody.isOneShot()) {
return null
}
val priorResponse = userResponse.priorResponse
if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null
}
if (retryAfter(userResponse, 0) > 0) {
return null
}
return userResponse.request
}
// 服务器不可用 503
HTTP_UNAVAILABLE -> {
val priorResponse = userResponse.priorResponse
if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
// We attempted to retry and got another timeout. Give up.
return null
}
if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
// specifically received an instruction to retry without delay
return userResponse.request
}
return null
}
// 421
HTTP_MISDIRECTED_REQUEST -> {
// OkHttp can coalesce HTTP/2 connections even if the domain names are different. See
// RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then
// we can retry on a different connection.
val requestBody = userResponse.request.body
if (requestBody != null && requestBody.isOneShot()) {
return null
}
if (exchange == null || !exchange.isCoalescedConnection) {
return null
}
exchange.connection.noCoalescedConnections()
return userResponse.request
}
else -> return null
}
}
从上面的代码可以看,followUpRequest()方法会根据不同的响应状态码构建不同类型的请求。当状态码407时,并且协议为 HTTP ,则返回一个包含认证挑战的请求;当状态码3XX时,会调用 buildRedirectRequest()构建重定向请求,告诉客户端使用替代位置访问客户端感兴趣的资源,要么提供一个替代的响应而不是资源的内容。
BridgeInterceptor
作为连接应用程序和服务端桥梁的拦截器。
主要在请求的过程中负责给Request添加必要的请求头信息(包括gzip压缩、cookie添加等等),在响应过程中解析Response信息(包括gzip解压、cookie保存)。
// BridgeInterceptor.kt 源码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
// 新建一个请求
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
val body = userRequest.body
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
// 添加contentType(实体主体的媒体类型)
requestBuilder.header("Content-Type", contentType.toString())
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
// 添加Content-Length(内容长度)
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
// 添加Transfer-Encoding(指定报文主体的传输方式)
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}
// 添加 Host(请求资源所在的服务器)
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
// 默认keep-Alive
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")
}
//传输流的压缩方式 默认gzip方式
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
//添加cookie(本地缓存)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
// 添加User-Agent(HTTP 客户端程序的信息)
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
// 返回Response
val networkResponse = chain.proceed(requestBuilder.build())
// 处理header中Set-Cookie内容 将cookie保存
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
//将原始request放入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) {
//封装成GzipSource 重写read方法 解压gzip
val gzipSource = GzipSource(responseBody.source())
//精简header
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
其实BridgeInterceptor的作用就是帮用户处理网络请求,它会帮助用户填写服务器请求所需要的配置信息,如上面所展示的User-Agent、Connection、Host、Accept-Encoding等;同时也会对请求的结果进行相应处理。
BridgeInterceptor的内部实现主要分为以下三步:
- 为用户网络请求设置Content-Type、Content-Length、Host、Connection、Cookie等参数,也就是将一般请求转换为适合服务器解析的格式,以适应服务器端;
- 通过 chain.proceed(requestBuilder.build())方法,将转换后的请求移交给下一个拦截器CacheInterceptor,并接收返回的结果Response;
- 对结果Response也进行gzip、Content-Type转换,以适应应用程序端。
CacheInterceptor
处理网络请求缓存的拦截器。
OkHttp中使用缓存的话,要在创建OkHttpClient初始化时用cache()方法设置缓存
/**
* 网络缓存数据的最大值(字节)
*/
const val MAX_SIZE_NETWORK_CACHE = 50 * 1024 * 1024L
private fun initOkHttpClient() {
val networkCacheDirectory = File(cacheDir?.absolutePath + "networkCache")
if (!networkCacheDirectory.exists()) {
networkCacheDirectory.mkdir()
}
val cache = Cache(networkCacheDirectory, MAX_SIZE_NETWORK_CACHE)
okHttpClient = OkHttpClient.Builder()
// 使用缓存
.cache(cache)
.build()
}
注意:
CacheInterceptor只会缓存GET和HEAD等获取资源的方法的请求,而对于POST和PUT等修改资源的请求和响应数据是不会进行缓存的。
- 得到/保存缓存响应
// CacheInterceptor.kt 源码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
//获取缓存 如果我们配置了缓存 那么会去查找是否存在cache
// 注意:okhttp默认并不会配置缓存,通过OkHttpClient.Builder的cache方法设置
// cacheCandidate是上次与服务器交互缓存的Response
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) {
// 缓存候选不适用。关闭它。
cacheCandidate.body?.closeQuietly()
}
// 不允许使用网络请求 并且当前网络没有缓存,会返回504(网关超时)
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)
}
}
// 不允许使用网络 仅直接使用缓存
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 {
// 直接发起网络请求
networkResponse = chain.proceed(networkRequest)
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
//检查缓存是否可用,如果可用。那么就用当前缓存的Response,关闭网络连接,释放连接。
if (cacheResponse != null) {
// 如果缓存数据不为空并且code为304,表示数据没有变化,继续使用缓存数据;
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()
}
}
// 返回 网络响应
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// 将网络数据保存到缓存中
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
// 不是get方法,移除缓存
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
上面的代码中,调用CacheStrategy类的compute() 方法创建 CacheStrategy 。
- 如果 CacheControl中有onlyIfCached(不重新加载响应)指令,那么CacheStrategy的cacheResponse字段也为空。
- 当请求还是新鲜的(存在时间 age 小于新鲜时间 fresh ),那么 CacheStrategy 的 networkRequest 字段就为空,这时 CacheInterceptor 就会返回缓存中的响应。
- 当请求已经不新鲜时,CacheInterceptor 就会通过 ConnectInterceptor 和 CallServerInterceptor 获取响应
- CacheStrategy 缓存策略
// CacheStrategy.kt 源码
//缓存策略类
class CacheStrategy{
//如果我们需要请求网络 则networkRequest不为null 否则为null
val networkRequest: Request?
//请求的返回或者请求的响应 如果无法使用缓存(一般是过期或者无缓存 则为null)
val cacheResponse: Response?
}
根据networkRequest和cacheResponse这两个变量来选择是否命中缓存。结论:
networkRequest\cacheResponse | cacheResponse is null | cacheResponse is not null |
---|---|---|
networkRequest is null | HTTP_GATEWAY_TIMEOUT 504错误 | 直接使用缓存 |
networkRequest is not null | 进行网络请求 并且缓存新response | 根据code(304) 判断是否需要重新request |
// CacheStrategy.kt 源码
fun compute(): CacheStrategy {
// 根据request的cache-control 和response 的cache-control判断
val candidate = computeCandidate()
// request仅使用缓存
if (candidate.networkRequest != null && request.cacheControl.onlyIfCached) {
return CacheStrategy(null, null)
}
return candidate
}
private fun computeCandidate(): CacheStrategy {
// 如果为null 表示之前没有缓存
if (cacheResponse == null) {
return CacheStrategy(request, null)
}
// 如果缺少tls握手 直接请求网络
if (request.isHttps && cacheResponse.handshake == null) {
return CacheStrategy(request, null)
}
// 根据cacheResponse的code判断是否允许cache
if (!isCacheable(cacheResponse, request)) {
return CacheStrategy(request, null)
}
//request的cacheControl
val requestCaching = request.cacheControl
// request的cacheControl 的noCache
if (requestCaching.noCache || hasConditions(request)) {
return CacheStrategy(request, null)
}
//response的cacheControl
val responseCaching = cacheResponse.cacheControl
val ageMillis = cacheResponseAge()
var freshMillis = computeFreshnessLifetime()
// 在freshMillis时间内是新鲜的,无需向服务器请求资源
if (requestCaching.maxAgeSeconds != -1) {
freshMillis = minOf(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds.toLong()))
}
// 最小刷新时间
var minFreshMillis: Long = 0
if (requestCaching.minFreshSeconds != -1) {
minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds.toLong())
}
// 请求中Cache-Control字段 maxStale:客户端愿意接收一个超过缓存时间的资源
var maxStaleMillis: Long = 0
if (!responseCaching.mustRevalidate && requestCaching.maxStaleSeconds != -1) {
maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds.toLong())
}
if (!responseCaching.noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
val builder = cacheResponse.newBuilder()
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"")
}
val oneDayMillis = 24 * 60 * 60 * 1000L
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"")
}
return CacheStrategy(null, builder.build())
}
// Find a condition to add to the request. If the condition is satisfied, the response body
// will not be transmitted.
val conditionName: String
val conditionValue: String?
when {
etag != null -> {
// If-None-Match:告诉服务器如果时间一致,返回状态码304
conditionName = "If-None-Match"
conditionValue = etag
}
// lastModified:该资源的最后更改时间
lastModified != null -> {
// If-Modified-Since:告诉服务器如果时间一致,返回状态码304
conditionName = "If-Modified-Since"
conditionValue = lastModifiedString
}
servedDate != null -> {
conditionName = "If-Modified-Since"
conditionValue = servedDateString
}
else -> return CacheStrategy(request, null) // No condition! Make a regular request.
}
val conditionalRequestHeaders = request.headers.newBuilder()
conditionalRequestHeaders.addLenient(conditionName, conditionValue!!)
val conditionalRequest = request.newBuilder()
.headers(conditionalRequestHeaders.build())
.build()
//直接使用缓存
return CacheStrategy(conditionalRequest, cacheResponse)
}
companion object {
/** Returns true if [response] can be stored to later serve another request. */
fun isCacheable(response: Response, request: Request): Boolean {
// Always go to network for uncacheable response codes (RFC 7231 section 6.1), This
// implementation doesn't support caching partial content.
when (response.code) {
HTTP_OK,
HTTP_NOT_AUTHORITATIVE,
HTTP_NO_CONTENT,
HTTP_MULT_CHOICE,
HTTP_MOVED_PERM,
HTTP_NOT_FOUND,
HTTP_BAD_METHOD,
HTTP_GONE,
HTTP_REQ_TOO_LONG,
HTTP_NOT_IMPLEMENTED,
StatusLine.HTTP_PERM_REDIRECT -> {
// These codes can be cached unless headers forbid it.
}
HTTP_MOVED_TEMP,
StatusLine.HTTP_TEMP_REDIRECT -> {
// These codes can only be cached with the right response headers.
// http://tools.ietf.org/html/rfc7234#section-3
// s-maxage is not checked because OkHttp is a private cache that should ignore s-maxage.
if (response.header("Expires") == null &&
response.cacheControl.maxAgeSeconds == -1 &&
!response.cacheControl.isPublic &&
!response.cacheControl.isPrivate) {
return false
}
}
else -> {
// All other codes cannot be cached.
return false
}
}
// A 'no-store' directive on request or response prevents the response from being cached.
return !response.cacheControl.noStore && !request.cacheControl.noStore
}
}
computeCandidate()方法 根据request和cacheResponse 的cacheControl计算出缓存策略。当响应的状态码为302(HTTP_MOVED_TEMP)或307(HTTP_TEMP_REDIRECT)时,isCacheable()方法就会根据响应的Expires首部和Cache-Control首部判断是否返回false(不缓存)。
Expires首部
的作用是服务器端可以指定一个绝对的日期,如果已经过了这个日期,就说明文档不“新鲜”了。
ConnectInterceptor
负责和服务器建立连接。流程是从连接池中查找连接;如果不存在,就创建连接并完成TCP、TLS握手。
查找连接
// ConnectInterceptor.kt 源码
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
// exchange是用来和服务端交互的对象封装
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
// RealCall.kt 源码
internal fun initExchange(chain: RealInterceptorChain): Exchange {
...
val exchangeFinder = this.exchangeFinder!!
// codec:编码解码器,确定是用Http1的方式还是以Http2的方式进行请求
val codec = exchangeFinder.find(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
}
从上面的代码来看,RealCall类的initExchange()方法初始化exchange数据交换类,然后根据下面的调用链initExchange()->ExchangeFinder#find()->ExchangeFinder#findHealthyConnection()->ExchangeFinder#findConnection()。
接着,下面是ExchangeFinder的findConnection()方法
// ExchangeFinder.kt 源码
@Throws(IOException::class)
private fun findConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean
): RealConnection {
if (call.isCanceled()) throw IOException("Canceled")
// 尝试重用调用中的连接
val callConnection = call.connection
if (callConnection != null) {
var toClose: Socket? = null
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
// 如果调用的连接没有被释放,则重用它。
// 这里我们没有调用connectionAcquired(),因为我们已经获得了它。
if (call.connection != null) {
check(toClose == null)
return callConnection
}
// 释放 call connection
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
// We need a new connection. Give it fresh stats.
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
// 首先从连接池查找获取
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
// 连接池没有连接,尝试用其他路线从连接池获取连接
val routes: List<Route>?
val route: Route
if (nextRouteToTry != null) {
// 使用来自前面合并连接的路由
routes = null
route = nextRouteToTry!!
nextRouteToTry = null
} else if (routeSelection != null && routeSelection!!.hasNext()) {
// 使用路由选择中 现有的路由
routes = null
route = routeSelection!!.next()
} else {
// 计算一个新的路由选择(阻塞操作)
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")
// 如果有新的路由 继续从连接池中查找试试
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
route = localRouteSelection.next()
}
// 创建新连接
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
try {
// 进行TCP和TLS连接
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
}
indConnection()方法大致做了3件事,首先会尝试复用RealCall已有的连接,没有已有连接的话则尝试从连接池获取连接复用;如果连接池没有可复用连接的话,则创建一个新连接并返回给 CallServerInterceptor 使用。
接着,可以看一下RealConnectionPool类的callAcquirePooledConnection()方法
// RealConnectionPool.kt 源码
fun callAcquirePooledConnection(
address: Address,
call: RealCall,
routes: List<Route>?,
requireMultiplexed: Boolean
): Boolean {
for (connection in connections) {
synchronized(connection) {
// 判断connection是否支持多路复用
if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
// 判断connection的host是否匹配
if (!connection.isEligible(address, routes)) return@synchronized
call.acquireConnectionNoEvents(connection)
return true
}
}
return false
}
上面的方法是从连接池是否可以查找相应的连接。
建立连接
// RealConnection.kt 源码
fun connect(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
call: Call,
eventListener: EventListener
) {
// 判断是否连接过协议
check(protocol == null) { "already connected" }
var routeException: RouteException? = null
val connectionSpecs = route.address.connectionSpecs
val connectionSpecSelector = ConnectionSpecSelector(connectionSpecs)
if (route.address.sslSocketFactory == null) {
if (ConnectionSpec.CLEARTEXT !in connectionSpecs) {
throw RouteException(UnknownServiceException(
"CLEARTEXT communication not enabled for client"))
}
val host = route.address.url.host
if (!Platform.get().isCleartextTrafficPermitted(host)) {
throw RouteException(UnknownServiceException(
"CLEARTEXT communication to $host not permitted by network security policy"))
}
} else {
if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
throw RouteException(UnknownServiceException(
"H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"))
}
}
while (true) {
try {
// 该请求是否使用了Proxy.Type.HTTP代理且目标是Https连接
if (route.requiresTunnel()) {
// 创建代理隧道连接;目的在于利用Http来代理请求Https
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
if (rawSocket == null) {
// We were unable to connect the tunnel but properly closed down our resources.
break
}
} else {
// 连接socket(TCP连接)
connectSocket(connectTimeout, readTimeout, call, eventListener)
}
// 建立请求协议
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
break
} catch (e: IOException) {
...
}
}
if (route.requiresTunnel() && rawSocket == null) {
throw RouteException(ProtocolException(
"Too many tunnel connections attempted: $MAX_TUNNEL_ATTEMPTS"))
}
idleAtNs = System.nanoTime()
}
在 RealConnection 的 connect() 方法中首先会判断当前连接是否已连接,也就是 connect() 方法被调用过没有,如果被调用过的话,则抛出非法状态异常。如果没有连接过的话,则判断请求用的是不是HTTPS方案,是的话则连接隧道,不是的话则调用connectSocket()方法连接Socket。
// RealConnection.kt 源码
@Throws(IOException::class)
private fun establishProtocol(
connectionSpecSelector: ConnectionSpecSelector,
pingIntervalMillis: Int,
call: Call,
eventListener: EventListener
) {
if (route.address.sslSocketFactory == null) {
if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
socket = rawSocket
protocol = Protocol.H2_PRIOR_KNOWLEDGE
startHttp2(pingIntervalMillis)
return
}
socket = rawSocket
protocol = Protocol.HTTP_1_1
return
}
eventListener.secureConnectStart(call)
// TLS连接
connectTls(connectionSpecSelector)
eventListener.secureConnectEnd(call, handshake)
if (protocol === Protocol.HTTP_2) {
startHttp2(pingIntervalMillis)
}
}
判断当前地址是否是HTTPS;如果不是HTTPS,则判断当前协议是否是明文HTTP2,如果是的则调用startHttp2,开始Http2的握手动作,如果是Http/1.1则直接return返回;如果是HTTPS,就开始建立TLS安全协议连接了(connectTls);如果是HTTPS且为HTTP2,除了建立TLS连接外,还会调用startHttp2,开始Http2的握手动作。
@Throws(IOException::class)
private fun connectTls(connectionSpecSelector: ConnectionSpecSelector) {
val address = route.address
val sslSocketFactory = address.sslSocketFactory
var success = false
var sslSocket: SSLSocket? = null
try {
// 利用请求地址host,端口以及TCP socket共同创建sslSocket
sslSocket = sslSocketFactory!!.createSocket(
rawSocket, address.url.host, address.url.port, true /* autoClose */) as SSLSocket
// 为Socket 配置加密算法,TLS版本等
val connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)
if (connectionSpec.supportsTlsExtensions) {
Platform.get().configureTlsExtensions(sslSocket, address.url.host, address.protocols)
}
// 调用startHandshake()进行强制握手
sslSocket.startHandshake()
// block for session establishment
val sslSocketSession = sslSocket.session
val unverifiedHandshake = sslSocketSession.handshake()
//验证服务器证书的合法性.
if (!address.hostnameVerifier!!.verify(address.url.host, sslSocketSession)) {
val peerCertificates = unverifiedHandshake.peerCertificates
if (peerCertificates.isNotEmpty()) {
val cert = peerCertificates[0] as X509Certificate
throw SSLPeerUnverifiedException("""
|Hostname ${address.url.host} not verified:
| certificate: ${CertificatePinner.pin(cert)}
| DN: ${cert.subjectDN.name}
| subjectAltNames: ${OkHostnameVerifier.allSubjectAltNames(cert)}
""".trimMargin())
} else {
throw SSLPeerUnverifiedException(
"Hostname ${address.url.host} not verified (no certificates)")
}
}
val certificatePinner = address.certificatePinner!!
handshake = Handshake(unverifiedHandshake.tlsVersion, unverifiedHandshake.cipherSuite,
unverifiedHandshake.localCertificates) {
certificatePinner.certificateChainCleaner!!.clean(unverifiedHandshake.peerCertificates,
address.url.host)
}
// 利用握手记录进行证书锁定校验
certificatePinner.check(address.url.host) {
handshake!!.peerCertificates.map { it as X509Certificate }
}
// 连接成功则保存握手记录和ALPN协议
val maybeProtocol = if (connectionSpec.supportsTlsExtensions) {
Platform.get().getSelectedProtocol(sslSocket)
} else {
null
}
socket = sslSocket
source = sslSocket.source().buffer()
sink = sslSocket.sink().buffer()
protocol = if (maybeProtocol != null) Protocol.get(maybeProtocol) else Protocol.HTTP_1_1
success = true
} finally {
if (sslSocket != null) {
Platform.get().afterHandshake(sslSocket)
}
if (!success) {
sslSocket?.closeQuietly()
}
}
}
CallServerInterceptor
负责与服务器进行数据交互;负责向服务器发送请求数据、从服务器读取响应数据。
@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()
// 将请求头写入到socket中,底层通过ExchangeCodec协议类
//(对应Http1ExchangeCodec和Http2ExchangeCodec),
// 最终是通过Okio来实现的,具体实现在RealBufferedSink这个类里面
exchange.writeRequestHeaders(request)
var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
// 根据请求方法判断是否有请求体
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
// 如果支持复用 传输request body
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 {
// 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()) {
//Request结束
exchange.finishRequest()
}
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 (code == 100) {
// 100表示 继续
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
}
资料