序、慢慢来才是最快的方法。
背景
OkHttp
是一套处理HTTP
网络请求的依赖库,由Square
公司设计研发并开源,目前可以在Java
和Kotlin
中使用。对于Android App
来说,OkHttp
现在几乎已经占据了所有的网络请求操作。
OKHttp源码官网
版本
java
api 'com.squareup.okhttp3:okhttp:4.10.0'
发起网络请求示例代码
Kotlin
val okHttpClient = OkHttpClient()
val request :Request = Request.Builder()
.url("")
.build()
val newCall = okHttpClient.newCall(request)
//同步请求
newCall.execute()
//异步请求
newCall.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
TODO("Not yet implemented")
}
override fun onResponse(call: Call, response: Response) {
TODO("Not yet implemented")
}
})
核心类
OkHttpClient 、Request 、Call 、RealCall 、Dispatcher 、Deque 、AsyncCall 、Response
- OkHttpClient:
Okhttp
用于请求的执行客户端 - Request: 通过Bulider设计模式,构建的一个请求对象
- Call: 是通过
client.newCall
生成的请求执行对象,当执行了execute
之后才会真正的开始执行网络请求 - Response: 是通过网络请求后,从服务器返回的信息都在里面。内含返回的状态码,以及代表响应消息正文的
ResponseBody
- interceptor 用户定义的拦截器,在重试拦截器之前执行
- retryAndFollowUpInterceptor 重试拦截器
- BridgeInterceptor 建立网络桥梁的拦截器,主要是为了给网络请求时候,添加各种各种必要参数。如Cookie,Content-type
- CacheInterceptor 缓存拦截器,主要是为了在网络请求时候,根据返回码处理缓存。
- ConnectInterceptor 连接拦截器,主要是为了从连接池子中查找可以复用的socket连接。
- networkInterceptors 用户定义的网络拦截器,在CallServerInterceptor(执行网络请求拦截器)之前运行。
- CallServerInterceptor 真正执行网络请求的逻辑。
1.网路请求流程
OkHttpClient 网络配置层
Kotlin
class Builder constructor() {
//Okhttp 请求分发器,是整个OkhttpClient的执行核心
internal var dispatcher: Dispatcher = Dispatcher()
//Okhttp连接池,不过会把任务委托给RealConnectionPool处理
internal var connectionPool: ConnectionPool = ConnectionPool()
//用户定义的拦截器,在重试拦截器之前执行
internal val interceptors: MutableList<Interceptor> = mutableListOf()
//用户定义的网络拦截器,在CallServerInterceptor(执行网络请求拦截器)之前运行。
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
//流程监听器
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
//连接失败时是否重连
internal var retryOnConnectionFailure = true
//服务器认证设置
internal var authenticator: Authenticator = Authenticator.NONE
//是否重定向
internal var followRedirects = true
//是否重定向到https
internal var followSslRedirects = true
//cookie持久化的设置
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
//缓存设置
internal var cache: Cache? = null
//DNS设置
internal var dns: Dns = Dns.SYSTEM
//代理设置
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
//默认的socket连接池
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
//用于https的socket连接池
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
//用于信任Https证书的对象
internal var x509TrustManagerOrNull: X509TrustManager? = null
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
//http协议集合
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
//https对host的检验
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
internal var certificateChainCleaner: CertificateChainCleaner? = null
//请求超时
internal var callTimeout = 0
//连接超时
internal var connectTimeout = 10_000
//读取超时
internal var readTimeout = 10_000
//写入超时
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
internal var routeDatabase: RouteDatabase? = null
}
client.newCall(request):
Kotlin
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
在这里生成一个RealCall对象,这里第三个参数是否为websocket,默认是false。 在拿到RealCall对象之后,这里有两种方式起发送网络请求:
- execute() : 这种方式很少用
- enqueue() : 这种方式是将每个请求放在队列中,按照顺序逐个去进行消费。
RealCall.enqueue()
java
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
private fun callStart() {
this.callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()")
eventListener.callStart(this)
}
这里主要做了一下几步
- 首先回调eventListener的callStart()方法,
- 然后把创建AsyncCall对象将responseCallback传进去。
- 最后Dispatcher的enqueue()方法.
Dispatcher.enqueue()
Kotlin
class Dispatcher constructor() {
......
//按运行顺序准备异步调用的队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
//正在运行的异步请求队列, 包含取消但是还未finish的AsyncCall
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
//正在运行的同步请求队列, 包含取消但是还未finish的RealCall
private val runningSyncCalls = ArrayDeque<RealCall>()
......
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
private fun findExistingCallWithHost(host: String): AsyncCall? {
for (existingCall in runningAsyncCalls) {
if (existingCall.host == host) return existingCall
}
for (existingCall in readyAsyncCalls) {
if (existingCall.host == host) return existingCall
}
return null
}
- 首先将AsyncCall加入readyAsyncCalls队列中.
- 然后通过findExistingCallWithHost查找在runningAsyncCalls和readyAsyncCalls是否存在相同host的AsyncCall,如果存在则调用call.reuseCallsPerHostFrom()进行复用
- 最后调用 promoteAndExecute() 通过线程池执行队列中的AsyncCall对象
Dispatcher.promoteAndExecute()
Kotlin
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
//判断是否有请求正在执行
val isRunning: Boolean
//加锁,保证线程安全
synchronized(this) {
//遍历 readyAsyncCalls 队列
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
//runningAsyncCalls的数量不能大于最大并发请求数 64
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
//同一Host的最大数是5
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
//从readyAsyncCalls队列中移除并加入到executableCalls和runningAsyncCalls中
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
//遍历executableCalls 执行asyncCall
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
在这里遍历readyAsyncCalls队列,判断runningAsyncCalls的数量是否大于最大并发请求数64, 判断同一Host的请求是否大于5,然后将AsyncCall从readyAsyncCalls队列中移除,并加入到executableCalls和runningAsyncCalls中,遍历executableCalls 执行asyncCall.
RealCall.AsyncCall.exceuteOn()
Kotlin
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
......
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
//执行AsyncCall 的run方法
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 {
//执行OkHttp的拦截器 获取response对象
val response = getResponseWithInterceptorChain()
signalledCallback = true
//通过该方法将response对象回调出去
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
//遇到IO异常 回调失败方法
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 {
client.dispatcher.finished(this)
}
}
}
}
这里可以看到AsyncCall就是一个Runable对象,线程执行就会调用该对象的run方法,而executeOn方法就是执行runable对象. 在run方法中主要执行了以下几步:
- 调用getResponseWithInterceptorChain()执行OkHttp拦截器,获取response对象
- 调用responseCallback的onResponse方法将Response对象回调出去
- 如果遇见IOException异常则调用responseCallback的onFailure方法将异常回调出去
- 如果遇到其他异常,调用cancel()方法取消请求,调用responseCallback的onFailure方法将异常回调出去
- 调用Dispatcher的finished方法结束执行
RealCall.getResponseWithInterceptorChain()
java
@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
//如果不是sockect 添加newtwork拦截器
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 {
//执行拦截器责任链获取Response
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)
}
}
}
在这里主要执行了以下几步操作
- 首先构建一个可变interceptor集合,将所有拦截器添加进去,这里如果是websocket则不添加networkInterceptor拦截器,这个interceptor集合的添加顺序也就是OkHttp拦截器的执行顺序
- 构建一个RealInterceptorChain对象,将所有的拦截器包裹
- 调用RealInterceptorChain的proceed的方法,获得Response对象
简单的总结一下:这里才用了责任链设计模式,构建RealInterceptorChain对象,然后执行proceed方法获取response对象
2.Interceptor
Kotlin
fun interface Interceptor {
//拦截方法
@Throws(IOException::class)
fun intercept(chain: Chain): Response
companion object {
inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =
Interceptor { block(it) }
}
interface Chain {
//获取Request对象
fun request(): Request
//处理请求获取Reponse
@Throws(IOException::class)
fun proceed(request: Request): Response
......
}
}
Kotlin
class RealInterceptorChain(
internal val call: RealCall,
private val interceptors: List<Interceptor>,
private val index: Int,
internal val exchange: Exchange?,
internal val request: Request,
internal val connectTimeoutMillis: Int,
internal val readTimeoutMillis: Int,
internal val writeTimeoutMillis: Int
) : Interceptor.Chain {
internal fun copy(
index: Int = this.index,
exchange: Exchange? = this.exchange,
request: Request = this.request,
connectTimeoutMillis: Int = this.connectTimeoutMillis,
readTimeoutMillis: Int = this.readTimeoutMillis,
writeTimeoutMillis: Int = this.writeTimeoutMillis
) = RealInterceptorChain(call, interceptors, index, exchange, request, connectTimeoutMillis,
readTimeoutMillis, writeTimeoutMillis)
......
override fun call(): Call = call
override fun request(): Request = request
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
......
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
......
return response
}
}
这里看一看到copy()方法就是创建了一个RealInterceptorChain()对象,不过需要注意的是index在创建对象时是index = index + 1,这样就会执行index对应下标的拦截器,不断的调用下一个拦截器,直到有response对象返回,也就是chain.proceed(originalRequest)结束。
RetryAndFollowUpInterceptor(错误重定向拦截器)
主要处理了如下几个方向的问题:
- 1.异常,或者协议重试(408客户端超时,权限问题,503服务暂时不处理,retry-after为0)
- 2.重定向
- 3.重试的次数不能超过20次。
RetryAndFollowUpInterceptor.intercept()
Kotlin
@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) {
//尝试通过路由连接失败。该请求将不会被发送。
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
}
//尝试关联上一个response,注意:body是为null
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()
//最大重试次数,不同的浏览器是不同的,比如:Chrome为21,Safari则是16
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
-
1.调用RealCall的enterNetworkInterceptorExchange方法实例化一个
ExchangeFinder
在RealCall对象中。 -
2.执行RealCall的proceed 方法,进入下一个拦截器,进行下一步的请求处理。
-
3.如果出现路由异常,则通过recover方法校验,当前的连接是否可以重试,不能重试则抛出异常,离开当前的循环。
Kotlin
private fun recover(
e: IOException,
call: RealCall,
userRequest: Request,
requestSendStarted: Boolean
): Boolean {
//禁止重连
if (!client.retryOnConnectionFailure) return false
// 不能再次发送请求体
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
// 致命异常
if (!isRecoverable(e, requestSendStarted)) return false
// 没有更多线路可以重连
if (!call.retryAfterFailure()) return false
// 对于故障恢复,将相同的路由选择器与新连接一起使用
return true
}
BridgeInterceptor(应用层和网络层的桥接拦截器)
主要处理了如下几个问题:
- 主要将Content-Type、Content-Length、Host等一些数据添加到头部。
- 拿到数据之后对数据进行处理,判断是否为gzip,进行对数据数据解压。
BridgeInterceptor.intercept()
Kotlin
@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) {
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")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
//执行下一个拦截器
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()
}
- 设置头部的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
。
CacheInterceptor.intercept()
Kotlin
@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) {
cacheCandidate.body?.closeQuietly()
}
//如果网络被禁止,但是缓存又是空的,构建一个code为504的response,并返回
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 (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 {
//捕获I/O或其他异常,请求失败,networkResponse为空,且有缓存的时候,不暴露缓存内容
if (networkResponse == null && cacheCandidate != null) {
//否则关闭缓存响应体
cacheCandidate.body?.closeQuietly()
}
}
//如果有缓存
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()
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) {
listener.cacheMiss(call)
}
}
}
//根据请求方法来判断缓存是否有效,只对Get请求进行缓存,其它方法的请求则移除
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
//缓存无效,将该请求缓存从client缓存配置中移除
cache.remove(networkRequest)
} catch (_: IOException) {
}
}
}
return response
}
网络请求前:
- 首先根据request从OkHttpClient.cache中获取缓存,通过
CacheStrategy
获取本次请求的请求体及缓存的响应体。 - 如果 请求体
networkRequest
和响应体cacheResponse
都为空的话,则返回错误码为 504 - 如果 请求体
networkRequest
为空 响应体cacheResponse
不为空的话,则将该响应体返回 - 如果请求体
networkRequest
不为空的话,则进入下一个拦截器。
网络请求后:
- 如果当前
cacheResponse
不为空,且networkResponse
状态码为304, 则代表数据没有变化,那么就会根据cacheResponse
构建一个新的response
,根据当前时间更新到缓存当中,并返回到上一拦截器中 - 如果
networkResponse
状态码不为304,则判断是否进行缓存,最后返回到上一拦截器中
从LruCache中获取缓存
Kotlin
val cacheCandidate = cache?.get(chain.request())
Kotlin
internal fun get(request: Request): Response? {
val key = key(request.url)
val snapshot: DiskLruCache.Snapshot = try {
cache[key] ?: return null
} catch (_: IOException) {
return null // Give up because the cache cannot be read.
}
val entry: Entry = try {
Entry(snapshot.getSource(ENTRY_METADATA))
} catch (_: IOException) {
snapshot.closeQuietly()
return null
}
val response = entry.response(snapshot)
if (!entry.matches(request, response)) {
response.body?.closeQuietly()
return null
}
return response
}
Kotlin
@JvmStatic
fun key(url: HttpUrl): String = url.toString().encodeUtf8().md5().hex()
- 首先将url转化为urf-8,并且通过md5拿到摘要,再调用hex获取16进制的字符串,该字符串就是LruCache的key;
- 通过key获取到
DiskLruCache.Snapshot
对象(这里在DiskLruCache
中重写了get方法),根据DiskLruCache.Snapshot
对象获取到okio 的source。
DiskLruCache:
Kotlin
@Synchronized @Throws(IOException::class)
operator fun get(key: String): Snapshot? {
initialize()
checkNotClosed()
validateKey(key)
val entry = lruEntries[key] ?: return null
val snapshot = entry.snapshot() ?: return null
redundantOpCount++
journalWriter!!.writeUtf8(READ)
.writeByte(' '.toInt())
.writeUtf8(key)
.writeByte('\n'.toInt())
if (journalRebuildRequired()) {
cleanupQueue.schedule(cleanupTask)
}
return snapshot
}
ConnectInterceptot(链接拦截器)
Kotlin
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
- 首先初始化一个可交换连接的对象
- 拷贝一个全新的
RealInterceptorChain
对象,并且调用该对象的proceed
方法执行下一拦截器
这个拦截器主要操作都在initExchange(chain)
当中
initExchange()
Kotlin
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
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
}
exchangeFinder
Kotlin
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
- 通过
findHealthyConnection
找到一个健康的连接resultConnection
,也就是一个活跃的连接, - 调用
resultConnection.newCodec(client, chain)
获取到ExchangeCodec
进行返回
findHealthyConnection
Kotlin
@Throws(IOException::class)
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
)
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate
}
candidate.noNewExchanges()
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
从OkHttp的连接池中找到对应的RealConnection
进行返回,如果没有的话,则创建一个candidate.isHealthy(doExtensiveHealthChecks)
检查该连接是否活跃可用- 如果当前连接是不健康,则调用
candidate.noNewExchanges()
将noNewExchanges
设置为true
,表示该连接存在问题 if (routesSelectionLeft) continue
判断是否还有其他路由需要尝试,如果有的话则返回true
,进入下一循环。
findConnection()
Kotlin
@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()
}
}
if (call.connection != null) {
check(toClose == null)
return callConnection
}
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
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 {
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
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
首先从RealCall
对象中获取RealConnection
。 -
在获取到
RealConnection
对象时,存在三种情况RealConnection
对象不为空,但是host和port不匹配RealConnection
对象不为空,完全匹配RealConnection
对象为空
-
判断
RealConnection
对象是否为空, 如果不为空则检查一下host和port是否匹配。- 如果不匹配则调用
releaseConnectionNoEvents()
,把RealConnection
绑定的RealCall
队列中对应的RealCall
移除,并从ConnectionPool
中移除该RealConnection
,当前RealCall
中绑定的RealConnection
设置为空, 并获取当前缓存RealConnection
的socket
对象,并关闭该socket
。 - 如果
noNewExchanges
为false
,并且host
和port
匹配,则返回该callConnection
对象 - 如果
RealConnection
对象为空,则会通过connectionPool
和route
生成一个新的RealConnection
对象
- 如果不匹配则调用
我们继续向下看
Kotlin
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
callAcquirePooledConnection
Kotlin
fun callAcquirePooledConnection(
address: Address,
call: RealCall,
routes: List<Route>?,
requireMultiplexed: Boolean
): Boolean {
for (connection in connections) {
synchronized(connection) {
if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
if (!connection.isEligible(address, routes)) return@synchronized
call.acquireConnectionNoEvents(connection)
return true
}
}
return false
}
这里我们主要看connection.isEligible是否符合条件
Kotlin
internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
assertThreadHoldsLock()
//如果当前RealConnection复用的RealCall队列的大小大于allocationLimit 限制大小,或者noNewExchanges 为true
if (calls.size >= allocationLimit || noNewExchanges) return false
//如果RealConnection 中的route和当前传递进来的地址的address不一致,直接返回false即可
if (!this.route.address.equalsNonHost(address)) return false
//如果RealConnection 中的route 和传递进来的host一致了,那么说明address和host都一致就是一个资源路径可以返回true。
if (address.url.host == this.route().address.url.host) {
return true
}
// 如果host 不一致,且http2Connection 为空,也就不是http 2.0协议,那么就不可能做到不同的资源路径进行复用的情况直接返回
if (http2Connection == null) return false
//此时就是必须要符合http 2.0的协议才能进行连接的复用,也就是路由可以共享。如果传进来的routes是空 或者通过routeMatchesAny 查找只要出现socket的地址一致且是直接连接的地址,则返回true,返回false一半就是代理的服务。此时就会直接返回
if (routes == null || !routeMatchesAny(routes)) return false
//想要进一步匹配,那么整个网络请求的HostnameVerifier 校验服务器主机名的必须为OkHostnameVerifier
if (address.hostnameVerifier !== OkHostnameVerifier) return false
//其次匹配HttpUrl和RealConnection的route 能否匹配。如果port 端口不匹配则直接返回false,如果host 匹配则直接返回true。否则就必须要保证noCoalescedConnections 为true (noCoalescedConnections 这个标志位为true,则说明该连接可以共享连接,但是不共享主机.),handshake不为空(说明已经经过了三次握手),且本次校验可以通过主机服务器名的校验。
if (!supportsUrl(address.url)) return false
try {
address.certificatePinner!!.check(address.url.host, handshake()!!.peerCertificates)
} catch (_: SSLPeerUnverifiedException) {
return false
}
return true
}
RouteSelector 生成
Kotlin
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
Kotlin
class RouteSelector(
private val address: Address,
private val routeDatabase: RouteDatabase,
private val call: Call,
private val eventListener: EventListener
) {
......
init {
resetNextProxy(address.url, address.proxy)
}
......
}
Kotlin
private fun resetNextProxy(url: HttpUrl, proxy: Proxy?) {
fun selectProxies(): List<Proxy> {
if (proxy != null) return listOf(proxy)
val uri = url.toUri()
if (uri.host == null) return immutableListOf(Proxy.NO_PROXY)
val proxiesOrNull = address.proxySelector.select(uri)
if (proxiesOrNull.isNullOrEmpty()) return immutableListOf(Proxy.NO_PROXY)
return proxiesOrNull.toImmutableList()
}
eventListener.proxySelectStart(call, url)
proxies = selectProxies()
nextProxyIndex = 0
eventListener.proxySelectEnd(call, url, proxies)
}
- 如果
proxy
不为空,则创建一个含有proxy的集合返回 - 如果
host
为空,则创建一个Proxy.NO_PROXY
的集合返回 - 否则调用address 的proxySelector 的select方法获取代理集合返回
如果有需要进行代理可以在OkhttpClientBuilder的addProxy中为不同的uri设置自己的代理规则。
接着往下看,这里是获取route,下面创建RealConnection
对象需要使用
Kotlin
val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes
if (call.isCanceled()) throw IOException("Canceled")
......
route = localRouteSelection.next()
val newConnection = RealConnection(connectionPool, route)
- 首先调用next获取
Selection
对象,通过调用next方法获取route,生成RealConnection
对象。下面逐步分析。
Kotlin
@Throws(IOException::class)
operator fun next(): Selection {
if (!hasNext()) throw NoSuchElementException()
val routes = mutableListOf<Route>()
while (hasNextProxy()) {
val proxy = nextProxy()
for (inetSocketAddress in inetSocketAddresses) {
val route = Route(address, proxy, inetSocketAddress)
if (routeDatabase.shouldPostpone(route)) {
postponedRoutes += route
} else {
routes += route
}
}
if (routes.isNotEmpty()) {
break
}
}
if (routes.isEmpty()) {
routes += postponedRoutes
postponedRoutes.clear()
}
return Selection(routes)
}
@Throws(IOException::class)
private fun nextProxy(): Proxy {
if (!hasNextProxy()) {
throw SocketException(
"No route to ${address.url.host}; exhausted proxy configurations:$proxies")
}
val result = proxies[nextProxyIndex++]
resetNextInetSocketAddress(result)
return result
}
nextProxy
不断的遍历获取所有的proxy
,调用resetNextInetSocketAddress
,创建route
添加到集合当中
Kotlin
@Throws(IOException::class)
private fun resetNextInetSocketAddress(proxy: Proxy) {
// Clear the addresses. Necessary if getAllByName() below throws!
val mutableInetSocketAddresses = mutableListOf<InetSocketAddress>()
inetSocketAddresses = mutableInetSocketAddresses
val socketHost: String
val socketPort: Int
if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
socketHost = address.url.host
socketPort = address.url.port
} else {
val proxyAddress = proxy.address()
require(proxyAddress is InetSocketAddress) {
"Proxy.address() is not an InetSocketAddress: ${proxyAddress.javaClass}"
}
socketHost = proxyAddress.socketHost
socketPort = proxyAddress.port
}
if (socketPort !in 1..65535) {
throw SocketException("No route to $socketHost:$socketPort; port is out of range")
}
if (proxy.type() == Proxy.Type.SOCKS) {
mutableInetSocketAddresses += InetSocketAddress.createUnresolved(socketHost, socketPort)
} else {
eventListener.dnsStart(call, socketHost)
// Try each address for best behavior in mixed IPv4/IPv6 environments.
val addresses = address.dns.lookup(socketHost)
if (addresses.isEmpty()) {
throw UnknownHostException("${address.dns} returned no addresses for $socketHost")
}
eventListener.dnsEnd(call, socketHost, addresses)
for (inetAddress in addresses) {
mutableInetSocketAddresses += InetSocketAddress(inetAddress, socketPort)
}
}
}
- 如果代理类型是
Proxy.Type.SOCKS
,则先获取address.url
的host和port,并调用InetSocketAddress.createUnresolved
解析,并添加到mutableInetSocketAddresses
集合中。 - 如果代理类型是
Proxy.Type.DIRECT
,则先获取address.url
的host和port,然后调用address.dns
的lookup
方法获取地址,并通过改地址和port生成InetSocketAddress
添加到mutableInetSocketAddresses
集合中。 - 如果代理类型是
Proxy.Type.HTTP
,则获取传递进来的Proxy中的host和port,调用address.dns
的lookup
方法获取地址,并通过改地址和port生成InetSocketAddress
添加到mutableInetSocketAddresses
集合中。
newConnection.connect()
Kotlin
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 {
if (route.requiresTunnel()) {
connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
if (rawSocket == null) {
break
}
} else {
connectSocket(connectTimeout, readTimeout, call, eventListener)
}
establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
break
} catch (e: IOException) {
socket?.closeQuietly()
rawSocket?.closeQuietly()
socket = null
rawSocket = null
source = null
sink = null
handshake = null
protocol = null
http2Connection = null
allocationLimit = 1
eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)
if (routeException == null) {
routeException = RouteException(e)
} else {
routeException.addConnectException(e)
}
if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
throw routeException
}
}
}
if (route.requiresTunnel() && rawSocket == null) {
throw RouteException(ProtocolException(
"Too many tunnel connections attempted: $MAX_TUNNEL_ATTEMPTS"))
}
idleAtNs = System.nanoTime()
}
-
这里分为两种情况
- requiresTunnel判断
sslSocketFactory
不为空,并且代理模式为Proxy.Type.HTTP
时,会调用connectTunnel
连接socket,其实最终还是调用connectSocket
方法。 - requiresTunnel为false时,则直接调用connectSocket连接socket
- requiresTunnel判断
-
完成上面的操作之后调用
establishProtocol
方法
Kotlin
@Throws(IOException::class)
private fun connectSocket(
connectTimeout: Int,
readTimeout: Int,
call: Call,
eventListener: EventListener
) {
val proxy = route.proxy
val address = route.address
val rawSocket = when (proxy.type()) {
Proxy.Type.DIRECT, Proxy.Type.HTTP -> address.socketFactory.createSocket()!!
else -> Socket(proxy)
}
this.rawSocket = rawSocket
eventListener.connectStart(call, route.socketAddress, proxy)
rawSocket.soTimeout = readTimeout
try {
Platform.get().connectSocket(rawSocket, route.socketAddress, connectTimeout)
} catch (e: ConnectException) {
throw ConnectException("Failed to connect to ${route.socketAddress}").apply {
initCause(e)
}
}
try {
source = rawSocket.source().buffer()
sink = rawSocket.sink().buffer()
} catch (npe: NullPointerException) {
if (npe.message == NPE_THROW_WITH_NULL) {
throw IOException(npe)
}
}
}
- 如果代理模式是
Proxy.Type.DIRECT, Proxy.Type.HTTP
则通过SocketFactory创建socket对象。 - 如果代理模式是
Proxy.Type.SOCKS
,则直接new 一个socekt对象。 - 通过
connectSocket
方法,调用socekt的connect
方法建立连接。 - 通过Okio分别获取source 写入流和sink 输出流缓存在RealConnection中。
connectTunnel
Kotlin
@Throws(IOException::class)
private fun connectTunnel(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
call: Call,
eventListener: EventListener
) {
var tunnelRequest: Request = createTunnelRequest()
val url = tunnelRequest.url
for (i in 0 until MAX_TUNNEL_ATTEMPTS) {//MAX_TUNNEL_ATTEMPTS=21
connectSocket(connectTimeout, readTimeout, call, eventListener)
tunnelRequest = createTunnel(readTimeout, writeTimeout, tunnelRequest, url) ?: break
rawSocket?.closeQuietly()
rawSocket = null
sink = null
source = null
eventListener.connectEnd(call, route.socketAddress, route.proxy, null)
}
}
- 调用createTunnelRequest创建一个通道确认的请求
- 调用connectSocket创建socket连接
- createTunnel创建一个隧道,在 21 次重试范围内,进行 socket 和 tunnel 的连接。如果
createTunnel
返回是null
,说明隧道建立成功。
Kotlin
@Throws(IOException::class)
private fun createTunnelRequest(): Request {
val proxyConnectRequest = Request.Builder()
.url(route.address.url)
.method("CONNECT", null)
.header("Host", route.address.url.toHostHeader(includeDefaultPort = true))
.header("Proxy-Connection", "Keep-Alive") // For HTTP/1.0 proxies like Squid.
.header("User-Agent", userAgent)
.build()
val fakeAuthChallengeResponse = Response.Builder()
.request(proxyConnectRequest)
.protocol(Protocol.HTTP_1_1)
.code(HTTP_PROXY_AUTH)
.message("Preemptive Authenticate")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(-1L)
.header("Proxy-Authenticate", "OkHttp-Preemptive")
.build()
val authenticatedRequest = route.address.proxyAuthenticator
.authenticate(route, fakeAuthChallengeResponse)
return authenticatedRequest ?: proxyConnectRequest
}
这个过程构建了构建了一个代理用的proxyConnectRequest 连接请求对象,以及一个虚假的响应,这个响应会包含proxyConnectRequest。然后通过设置的proxyAuthenticator 进行权限校验。
Kotlin
@Throws(IOException::class)
private fun createTunnel(
readTimeout: Int,
writeTimeout: Int,
tunnelRequest: Request,
url: HttpUrl
): Request? {
var nextRequest = tunnelRequest
// 拼接CONNECT命令
val requestLine = "CONNECT ${url.toHostHeader(includeDefaultPort = true)} HTTP/1.1"
while (true) {
val source = this.source!!
val sink = this.sink!!
//对应http/1.1 编码HTTP请求并解码HTTP响应
val tunnelCodec = Http1ExchangeCodec(null, this, source, sink)
source.timeout().timeout(readTimeout.toLong(), MILLISECONDS)
sink.timeout().timeout(writeTimeout.toLong(), MILLISECONDS)
//发送CONNECT,请求打开隧道连接,
tunnelCodec.writeRequest(nextRequest.headers, requestLine)
//完成连接
tunnelCodec.finishRequest()
//构建response,操控的是inputStream流
val response = tunnelCodec.readResponseHeaders(false)!!
.request(nextRequest)
.build()
tunnelCodec.skipConnectBody(response)
when (response.code) {
HTTP_OK -> {
if (!source.buffer.exhausted() || !sink.buffer.exhausted()) {
throw IOException("TLS tunnel buffered too many bytes!")
}
return null
}
HTTP_PROXY_AUTH -> {
nextRequest = route.address.proxyAuthenticator.authenticate(route, response)
?: throw IOException("Failed to authenticate with proxy")
if ("close".equals(response.header("Connection"), ignoreCase = true)) {
return nextRequest
}
}
else -> throw IOException("Unexpected response code for CONNECT: ${response.code}")
}
}
}
establishProtocol
Kotlin
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)
connectTls(connectionSpecSelector)
eventListener.secureConnectEnd(call, handshake)
if (protocol === Protocol.HTTP_2) {
startHttp2(pingIntervalMillis)
}
}
- 判断
route.address.sslSocketFactory == null
,如果为true的话,则说明该请求为http请求- 如果http请求里包涵了"h2_prior_knowledge"协议,代表是一个支持明文的http2请求,所以仍然开启的是http2的连接
- 如果http请求里不包涵了"h2_prior_knowledge"协议,则正常建立http连接
- 如果
route.address.sslSocketFactory == null
,如果为false的话,则说明该请求为https请求- 建立TLS连接
- 建立http2连接
connectTls
Kotlin
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
Address address = route.address();
SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
boolean success = false;
SSLSocket sslSocket = null;
try {
sslSocket = (SSLSocket) sslSocketFactory.createSocket(
rawSocket, address.url().host(), address.url().port(), true);
ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket)
if (connectionSpec.supportsTlsExtensions()) {
Platform.get().configureTlsExtensions(
sslSocket, address.url().host(), address.protocols());
}
sslSocket.startHandshake();
SSLSession sslSocketSession = sslSocket.getSession();
Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
List<Certificate> peerCertificates = unverifiedHandshake.peerCertificates();
if (!peerCertificates.isEmpty()) {
X509Certificate cert = (X509Certificate) peerCertificates.get(0);
throw new SSLPeerUnverifiedException(
"Hostname " + address.url().host() + " not verified:"
+ "\n certificate: " + CertificatePinner.pin(cert)
+ "\n DN: " + cert.getSubjectDN().getName()
+ "\n subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
} else {
throw new SSLPeerUnverifiedException(
"Hostname " + address.url().host() + " not verified (no certificates)");
}
}
address.certificatePinner().check(address.url().host(),
unverifiedHandshake.peerCertificates());
String maybeProtocol = connectionSpec.supportsTlsExtensions()
? Platform.get().getSelectedProtocol(sslSocket)
: null;
socket = sslSocket;
source = Okio.buffer(Okio.source(socket));
sink = Okio.buffer(Okio.sink(socket));
handshake = unverifiedHandshake;
protocol = maybeProtocol != null
? Protocol.get(maybeProtocol)
: Protocol.HTTP_1_1;
success = true;
} catch (AssertionError e) {
...
} finally {
...
}
}
- 将刚刚得到的socket通过sslSocketFactory进行包裹,得到一个新的sslSocket
- 调用configureSecureSocket对sslSocket进行配置协议。
- 通过supportsTlsExtensions方法查看是否支持TLS拓展
- 调用sslSocket.startHandshake()方法,开始握手协议
- 判断(!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)对sslSocket的地址与主机地址进行校验,确保一致可用。
- 开始 证书校验
- 将刚才完成握手和协议校验的sslSocket保存起来,并且获得用于IO传输的source、sink
Kotlin
@Throws(IOException::class)
private fun startHttp2(pingIntervalMillis: Int) {
val socket = this.socket!!
val source = this.source!!
val sink = this.sink!!
socket.soTimeout = 0
val http2Connection = Http2Connection.Builder(client = true, taskRunner = TaskRunner.INSTANCE)
.socket(socket, route.address.url.host, source, sink)
.listener(this)
.pingIntervalMillis(pingIntervalMillis)
.build()
this.http2Connection = http2Connection
this.allocationLimit = Http2Connection.DEFAULT_SETTINGS.getMaxConcurrentStreams()
http2Connection.start()
}
这个拦截器的主要作用是获取一个活跃可用连接,
- 首先从RealCall对象中获取,如果找到直接返回,
- 如果从RealCall对象没有获取到, 则再从连接池中查找,如果找到也是直接返回,
- 如果从连接池中没有找到,则通过路由再次在连接池中查询,如果找到也是直接返回
- 如果从连接池中再次没有找到,那么就创建一个
RealConnection
对象,然后创建Socket连接,封装地址信息,并将该连接添加到连接池中,最后进行返回
NetWorkInterceptor(网络连接器)
CallServerInterceptor(请求拦截器)
Kotlin
@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()
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) {
if (requestBody.isDuplex()) {
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
if (invokeStartEvent) {
exchange.responseHeadersStart()
invokeStartEvent = false
}
}
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
if (code == 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) {
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
}
exchange.writeRequestHeaders(request)
写入请求头信息- 判断请求方式是不是
GET
或HEAD
,如果不是则需要传入请求体,接着判断请求头中的Expect
值是否为100-continue
,如果是的话,则会读取响应体的头部信息,如果读出的Response.Builder
为空,接着判断requestBody.isDuplex()
,如果为true的话,则刷新缓冲区,通过exchange.createRequestBody(request, true).buffer()
创建bufferedRequestBody
,往请求的requestBody
写入数据,如果为false的话,,通过exchange.createRequestBody(request, true).buffer()
创建bufferedRequestBody
,写入输出流中发送数据。 - 如果请求方式是
GET
或HEAD
的话,则没有请求体。 - 如果
requestBody
为空,也就是没有请求体,或者requestBody.isDuplex()
为false的话,则结束请求。 - 如果responseBuilder为空的话,则调用
exchange.readResponseHeaders
方法 - 获取响应体
- 判断响应体的code是否为100,如果响应体为100 则是说后面还有数据需要传输,则会重新调用
exchange.readResponseHeaders
方法,再次生成响应体 - 判断code是否为101 并且 为websocekt请求,如果是的话,则生成一个空的response,否则就会通过
exchange.openResponseBody(response)
读取response中的数据生成一个响应体。 - 最后判断code是否为204、205并且响应体的body为空的话则抛出异常,否则正常返回
。。。。。。。省略
参考:
OKHTTP 源码分析(1)调用流程梳理_okhttpclient().newbuilder().build();-CSDN博客