本文基于OkHttp 4.3.1源码分析
OkHttp - 官方地址
OkHttp - GitHub代码地址
概述
OkHttp整体流程(本文覆盖红色部分)
Interceptor流程
责任链模式
概念
责任链模式(Chain of Responsibility Pattern),包含命令对象和一系列实现了相同接口的处理对象,这些处理对象相互连接成为一条责任链。每一个处理对象能决定它能处理哪些命令对象,对于它不能处理的命令对象,将会传递给该链中的下一个处理对象。负责链模式就是对这种顺序处理事件的行为的抽象,通过接口来定义处理事件的方法,顺序分发/处理事件。
主要解决
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
优点
- 降低耦合度。它将请求的发送者和接收者解耦。
- 简化了对象。使得对象不需要知道链的结构。
- 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
- 增加新的请求处理类很方便。
特点
- 每个责任人实现相同的接口,处理一个事件对象
- 让事件对象责任人之间顺序传递
- 事件的处理结果的返回是逆序的
- 责任链中的每个责任人都可以有权不继续传递事件,以自身为终点处理事件返回结果
OkHttp中应用
在 OkHttp 中,命令对象就是 Request 对象,处理对象就是每一个 Interceptor 对象。每个 interceptor 对 request 进行一些步骤的处理,而将其余的工作交给下一个 interceptor。另外,责任链中的处理对象如果可以全权处理命令对象,则不需要交给下一个处理对象。OkHttp 中的 CacheInterceptor 也是具有全权处理的能力。如果请求的结果已经缓存,则不需要再交给 ConnectInterceptor 等进行连接服务器、发送请求的处理,直接返回已缓存的 response 即可。
源码分析
Interceptor
拦截器,即处理对象类,Chain和Interceptor共同实现顺序调用,最终反响依次返回Response
拦截器一般可以概括三个步骤
- 请求前处理逻辑
- 执行 chain.proceed,顺序调用
- 获取响应结果后逻辑处理
默认拦截器依次包括
- RetryAndFollowUpInterceptor (重试机制)
- BridgeInterceptor (Http连接转换桥)
- CacheInterceptor (缓存策略)
- ConnectInterceptor (连接)
- CallServerInterceptor (响应读取)
// 拦截器,处理对象
interface Interceptor {
// 拦截方法,接受拦截器链(包括命令对象Request及Call、和一系列辅助对象),返回处理结果
fun intercept(chain: Chain): Response
// 链接口
interface Chain {
// 核心方法, 顺序执行 处理方法
fun proceed(request: Request): Response
...
}
}
RealInterceptorChain 拦截器链
完成责任链模式核心类,拦截器顺序执行、反向返回及中途中断核心实现
前一篇文章知道,命令对象RealCall,执行RealInterceptorChain的proceed方法,该方法最后会按照拦截器的加入顺序,循环构建Chain对象,然后执行拦截器的拦截方法,执导最后一个拦截器或者是中途拦截器直接返回了。
class RealInterceptorChain(
private val interceptors: List<Interceptor>, // 拦截器,处理对象
private val transmitter: Transmitter, // 发射机
private val exchange: Exchange?, // 交换器 请求和响应
private val index: Int,
private val request: Request, // 请求
private val call: Call, // 一次请求和返回封装
private val connectTimeout: Int,
private val readTimeout: Int,
private val writeTimeout: Int
) : Interceptor.Chain {
...
override fun proceed(request: Request): Response {
return proceed(request, transmitter, exchange)
}
// 顺序调用方法
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
...
calls++
...
// 构造一个 RealInterceptorChain
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
// 根据index 依次取拦截器
val interceptor = interceptors[index]
// 所有拦截器顺序执行,拦截器方法中执行 chain.proceed完成reponse返回,将命令对象交给下一个拦截器进行处理,如果不执行chain.proceed则在该拦截器进行终结。
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
...
return response
}
}
RetryAndFollowUpInterceptor
重试拦截器
RetryAndFollowUpInterceptor.intercept
- 请求连接准备工作
- 执行chain.proceed顺序执行,获取response
- 若出现异常,则判断是否需要进行重试机制
- 根据服务端返回的状态码,进行重定向请求生成
- 进行重试,直到不需要重试或者成功,最多20次
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val realChain = chain as RealInterceptorChain
val transmitter = realChain.transmitter()
var followUpCount = 0
var priorResponse: Response? = null
while (true) { // 循环
// 发射机 执行连接准备工作
transmitter.prepareToConnect(request)
if (transmitter.isCanceled) { // 处理取消的请求
throw IOException("Canceled")
}
var response: Response
var success = false
try {
// 执行 proceed顺序执行流程,最终获取response
response = realChain.proceed(request, transmitter, null)
success = true
} catch (e: RouteException) {
// 若不满足重试条件,则抛出异常
if (!recover(e.lastConnectException, transmitter, false, request)) {
throw e.firstConnectException
}
continue // 满足,则重试
} catch (e: IOException) {
// 若不满足重试条件,则抛出异常
val requestSendStarted = e !is ConnectionShutdownException
if (!recover(e, transmitter, requestSendStarted, request)) throw e
continue // 满足,则重试
} finally {
// 异常抛出,则success=false,执行释放资源
if (!success) {
transmitter.exchangeDoneDueToException()
}
}
if (priorResponse != null) { // 赋值上一次的response
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = response.exchange
val route = exchange?.connection()?.route()
// 获取进一步重试的请求
val followUp = followUpRequest(response, route)
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
transmitter.timeoutEarlyExit() // 无重试,停止timeout并退出
}
return response
}
val followUpBody = followUp.body
// 如果期望执行一次,则结束
if (followUpBody != null && followUpBody.isOneShot()) {
return response
}
response.body?.closeQuietly()
if (transmitter.hasExchange()) {
exchange?.detachWithViolence()
}
// 重试不超过20此
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
// 修改下次的请求
request = followUp
// 记录上一次的response
priorResponse = response
}
}
RetryAndFollowUpInterceptor.followUpRequest
主要针对重定向的几个状态码进行特殊里,从Header中取Location字段,构造重定向request
private fun followUpRequest(userResponse: Response, route: Route?): Request? {
val responseCode = userResponse.code
val method = userResponse.request.method
when (responseCode) {
HTTP_PROXY_AUTH -> { // 407
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)
}
// 身份认证
HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)
// 针对Get、Head请求 执行构建重定向请求
HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT -> { // 307 、308
if (method != "GET" && method != "HEAD") {
return null
}
return buildRedirectRequest(userResponse, method)
}
// 针对300、301、302、303 执行构建重定向请求
HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
return buildRedirectRequest(userResponse, method)
}
HTTP_CLIENT_TIMEOUT -> {
// 408 ,说明需要重新发送一次相同的请求
... // 判断是否有必要重新发送
return userResponse.request
}
HTTP_UNAVAILABLE -> { // 503
return null
}
else -> return null
}
}
private fun buildRedirectRequest(userResponse: Response, method: String): Request? {
// 设置不需要执行重定向
if (!client.followRedirects) return null
// location header 是否支持
val location = userResponse.header("Location") ?: return null
// Don't follow redirects to unsupported protocols.
val url = userResponse.request.url.resolve(location) ?: return null
// 如果不允许SSL和Non-SSL之间的重定向,则返回null
val sameScheme = url.scheme == userResponse.request.url.scheme
if (!sameScheme && !client.followSslRedirects) return null
// Most redirects don't include a request body.
val requestBuilder = userResponse.request.newBuilder()
if (HttpMethod.permitsRequestBody(method)) {
val maintainBody = HttpMethod.redirectsWithBody(method)
if (HttpMethod.redirectsToGet(method)) {
requestBuilder.method("GET", null)
} else {
val requestBody = if (maintainBody) userResponse.request.body else null
requestBuilder.method(method, requestBody)
}
if (!maintainBody) {
requestBuilder.removeHeader("Transfer-Encoding")
requestBuilder.removeHeader("Content-Length")
requestBuilder.removeHeader("Content-Type")
}
}
// When redirecting across hosts, drop all authentication headers. This
// is potentially annoying to the application layer since they have no
// way to retain them.
if (!userResponse.request.url.canReuseConnectionFor(url)) {
requestBuilder.removeHeader("Authorization")
}
// 构建 新的请求
return requestBuilder.url(url).build()
}
BridgeInterceptor
Bridges from application code to network code.
连接应用层和网络层的桥梁
- 主要针对请求Header进行处理,如gzip、cookie及其他Header字段等
- 响应则进行gzip解压
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
// 将userRequest中的属性设置到request中
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")
}
// 如果没有设置Accept-Encoding,则自动设置为gzip
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
// 将userRequest中的cookie解析出来并设置到Request中的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)
}
// 顺序执行处理方法
val networkResponse = chain.proceed(requestBuilder.build())
// 处理响应的headers,并缓存cookie
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
// 设置了Gzip,且响应结果支持gzip,则进行Gzip解析
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()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
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)
}
}
}