OkHttp 源码阅读笔记(五)
第一篇文章中介绍了 OkHttp
的同步调用和异步调用,Dispatcher
的任务调度器工作方式和 RealInterceptorChain
拦截器链的工作方式:OkHttp 源码阅读笔记(一)。
第二篇文章中介绍了 OkHttp
如何从缓存中获取链接,如何创建链接以及 ConnectionPool
的工作原理:OkHttp 源码阅读笔记(二)。
第三篇文章中介绍了 OkHttp
中的系统拦截器 RetryAndFollowUpInterceptor
和 BridgeInterceptor
:OkHttp 源码阅读笔记(三)。
第四篇文章中介绍了 OkHttp
中的系统拦截器 CacheInterceptor
:OkHttp 源码阅读笔记(四)。
本篇文章是系列文章的第五篇,也是系列文章中的最后一篇,介绍最后一个系统拦截器 CallServerInterceptor
。
CallServerInterceptor
在 CallServerInterceptor
的前面的系统拦截器,已经处理好了 Request
,后续要返回的 Response
处理逻辑也准备就绪。网络链接相关的工作也都已经处理好了。所以 CallServerInterceptor
的工作就是将 Request
中的数据通过网络链接传输到服务器,同时通过网络链接接收服务器发送过来的 Response
的相关数据,然后返回上一层拦截器。同样的以 intercept()
方法作为分析该流程的入口函数:
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()
var invokeStartEvent = true
var responseBuilder: Response.Builder? = null
var sendRequestException: IOException? = null
try {
// 写入 Request Header
exchange.writeRequestHeaders(request)
// 判断是否能够写入 Request Header,只有 PUT 和 GET 不允许写入。
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
// 如果 Reqeust Header 中有 "Expect: 100-continue",表示不能够写入 Request Body,需要等待 Response 返回 100+。后面会看到这部分逻辑。
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
// 直接读 Response Header
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
// 以下是 Http2 相关逻辑跳过
if (requestBody.isDuplex()) {
// ...
} else {
// Http 1.x 相关逻辑
// Write the request body if the "Expect: 100-continue" expectation was met.
// 写入 Request Body。
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
} catch (e: IOException) {
if (e is ConnectionShutdownException) {
throw e // No request was sent so there's no response to read.
}
if (!exchange.hasFailure) {
throw e // Don't attempt to read the response; we failed to send the request.
}
sendRequestException = e
}
try {
if (responseBuilder == null) {
// 读取 Response Header
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
// 判断 Response Code 是不是 100+,如果是的话还需要再读取一次 Response Header。
if (shouldIgnoreAndWaitForRealResponse(code)) {
// 再次读取 Response Header。
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.
// WebSocket 逻辑,没有 Response Body。
response.newBuilder()
.body(EMPTY_RESPONSE)
.build()
} else {
response.newBuilder()
// 构建 Response Body,注意这里的 Response Body 并没有读取到内存中,需要库的使用者真正使用时才会去读取,后续会分析这部分代码。
.body(exchange.openResponseBody(response))
.build()
}
// 如果 Request Header 或者 Response Header 中有 "Connection: close" 就表示后续不能够再使用这个链接,然后会把这个链接标记为不可用。
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
"close".equals(response.header("Connection"), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
}
// 返回最后的结果
return response
} catch (e: IOException) {
if (sendRequestException != null) {
sendRequestException.addSuppressed(e)
throw sendRequestException
}
throw e
}
}
我们先来理一下普通逻辑:
- 调用
Exchange#writeRequestHeaders()
方法写入Request Header
,后续再分析这个方法。 - 写入
Request Body
。- 除
GET
和HEAD
以外的请求,同时Request Body
不为空才能够写入。 - 如果
Request Header
中有Expect: 100-continue
不能写入Request Body
,表示需要等待Response
中返回100+
才做后续操作。 - 通过
Exchange#createRequestBody()
(后续详细分析) 方法创建一个对应的Sink
(可以理解为OutputStream
),然后将Request Body
写入上面的Sink
。
- 除
- 通过
Exchange#readResponseHeaders()
(后续详细分析)方法读取Response Header
。 - 通过
Exchange#openResponseBody()
(后续详细分析)方法打开Response Body
,注意这里并没有读取Body
到内存中,只库的使用者真正使用时才会去读取。 - 判断
Request Header
或者Response Heander
中是否有Connection: close
,这表示该链接在使用完后不能够再重用,通过调用Exchange#noNewExchangesOnConnection()
来标记,最终会设置RealConnection#noNewExchanges
属性为true
,如果有看这个系列的第二篇文章,应该还对这个参数有印象。
再简单说明一下 Response Code
为 100+
的情况:
- 如果
Request Header
中有Expect: 100-continue
,就表示它需要等待Response Code
为100+
才继续后续操作。 - 如果
Response Header
中返回的是100+
,就还需要再次读取Response Header
。
具体 Repsonse Code
为 100+
和 Expect: 100-continue
这种情况,不熟悉的话建议去网上找找 Http
协议的相关描述。
Exchange#writeRequestHeaders()
我这里先给一个 Request Header
的例子,以帮助大家阅读后面的源码:
text
GET /docs/tutorials/linux/shellscripts/howto.html HTTP/1.1
Host: Linode.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.8) Gecko/20091102 Firefox/3.5.5
Accept: text/html,application/xhtml+xml,
Accept-Language: en-us
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8
Cache-Control: no-cache
然后看看 Exhcnage#writeRequestHeaders()
方法的源码:
Kotlin
@Throws(IOException::class)
fun writeRequestHeaders(request: Request) {
try {
// 通知 eventListener 写 RequestHeader 开始
eventListener.requestHeadersStart(call)
// 调用 Codec#writeRequestHeaders() 方法写入 Requst Header
codec.writeRequestHeaders(request)
// 通知 eventListener 写 RequestHeader 结束
eventListener.requestHeadersEnd(call, request)
} catch (e: IOException) {
eventListener.requestFailed(call, e)
trackFailure(e)
throw e
}
}
上面的 Codec
有两个实现类 Http1ExchangeCodec
和 Http2ExchangeCodec
分别表示 Http 1
和 Http 2
协议的写入逻辑,我们后续的分析也都是针对 Http1ExchangeCodec
。
我们继续看看 Http1ExchangeCodec#writeRequestHeaders()
方法的实现:
Kotlin
override fun writeRequestHeaders(request: Request) {
// 获取 Request 方法,协议,路径等等字符串,例如:GET /docs/tutorials/linux/shellscripts/howto.html HTTP/1.1
val requestLine = RequestLine.get(request, connection.route().proxy.type())
writeRequest(request.headers, requestLine)
}
/** Returns bytes of a request header for sending on an HTTP transport. */
fun writeRequest(headers: Headers, requestLine: String) {
check(state == STATE_IDLE) { "state: $state" }
// 写入协议行
sink.writeUtf8(requestLine).writeUtf8("\r\n")
// 写入 Rquest Header,单个 Header 以换行符结束。
for (i in 0 until headers.size) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n")
}
// 再写入一个空的行,用来标记 Header 已经写完了,后续就是写 Request Body。
sink.writeUtf8("\r\n")
state = STATE_OPEN_REQUEST_BODY
}
最开始的行是协议相关的行,格式就是 请求方法
+
+ 请求路径
+
+ 协议
,例如:GET /docs/tutorials/linux/shellscripts/howto.html HTTP/1.1
。后续就是写入 Request Header
,格式是 Key
+ :
+ Value
,例如 Content-Type: application/json
。当 Header
写完了后,还会写一个空行,表示写完了,后续就是 Request Body
的写入。(如果有的话)这里的 Sink
对象是在构建链接成功后就会创建,它是相当于 OutputStream
,也就是对应写,对应读的是 Source
,相当于 InputStream
。他们都是 OkIo
中的实现。前面介绍 OkHttp
链接相关的时候有介绍。
Exchange#createRequestBody()
这里再看看 Exchange#createRequestBody()
的实现:
Kotlin
@Throws(IOException::class)
fun createRequestBody(request: Request, duplex: Boolean): Sink {
this.isDuplex = duplex
// 获取 Request Body 的长度
val contentLength = request.body!!.contentLength()
// 通知 eventListener 开始写 Request Body
eventListener.requestBodyStart(call)
val rawRequestBody = codec.createRequestBody(request, contentLength)
return RequestBodySink(rawRequestBody, contentLength)
}
上面代码比较简单主要是通过 Codec#createRequestBody()
构建一个 Sink
对象,然后再创建一个 RequestBodySink
对象。我们先看看 Http1ExchangeCodec#createRequestBody()
方法的实现:
Kotlin
override fun createRequestBody(request: Request, contentLength: Long): Sink {
return when {
request.body != null && request.body.isDuplex() -> throw ProtocolException(
"Duplex connections are not supported for HTTP/1")
request.isChunked -> newChunkedSink() // Stream a request body of unknown length.
contentLength != -1L -> newKnownLengthSink() // Stream a request body of a known length.
else -> // Stream a request body of a known length.
throw IllegalStateException(
"Cannot stream a request body without chunked encoding or a known content length!")
}
}
如果有 Content-Length
,会调用 newKnownLengthSink()
方法创建 Sink
,反之调用 newChunkedSink()
方法,我们以 newKnownLengthSink()
为例子,看后续的源码:
Kotlin
private fun newKnownLengthSink(): Sink {
check(state == STATE_OPEN_REQUEST_BODY) { "state: $state" }
state = STATE_WRITING_REQUEST_BODY
return KnownLengthSink()
}
private inner class KnownLengthSink : Sink {
// 写超时对象
private val timeout = ForwardingTimeout(sink.timeout())
private var closed: Boolean = false
override fun timeout(): Timeout = timeout
override fun write(source: Buffer, byteCount: Long) {
check(!closed) { "closed" }
// 检查 byteCount 是否已经超过了 Buffer 的最大值
checkOffsetAndCount(source.size, 0, byteCount)
// 直接写入到链接的 Sink 中
sink.write(source, byteCount)
}
override fun flush() {
if (closed) return // Don't throw; this stream might have been closed on the caller's behalf.
sink.flush()
}
override fun close() {
if (closed) return
closed = true
// 关闭超时
detachTimeout(timeout)
state = STATE_READ_RESPONSE_HEADERS
}
}
KnownLengthSink
对象是 Http1ExchangeCodec
中的内部类,它的处理方式很简单,只有一些简单的状态判断,写完时还会把写超时的计时器关闭,最终写入链接创建成功时的 Socket
的 Sink
中。
我们再来看看 RequestBodySink
中的实现:
Kotlin
private inner class RequestBodySink(
// 这个就是上面分析的 KnownLengthSink。
delegate: Sink,
/** The exact number of bytes to be written, or -1L if that is unknown. */
private val contentLength: Long
) : ForwardingSink(delegate) {
private var completed = false
private var bytesReceived = 0L
private var closed = false
@Throws(IOException::class)
override fun write(source: Buffer, byteCount: Long) {
check(!closed) { "closed" }
// 检查写的 Request Body 是否超过了设置的长度
if (contentLength != -1L && bytesReceived + byteCount > contentLength) {
throw ProtocolException(
"expected $contentLength bytes but received ${bytesReceived + byteCount}")
}
try {
super.write(source, byteCount)
this.bytesReceived += byteCount
} catch (e: IOException) {
throw complete(e)
}
}
@Throws(IOException::class)
override fun flush() {
try {
super.flush()
} catch (e: IOException) {
throw complete(e)
}
}
@Throws(IOException::class)
override fun close() {
if (closed) return
closed = true
// 检查写的长度
if (contentLength != -1L && bytesReceived != contentLength) {
throw ProtocolException("unexpected end of stream")
}
try {
super.close()
// 通知写完成
complete(null)
} catch (e: IOException) {
throw complete(e)
}
}
private fun <E : IOException?> complete(e: E): E {
if (completed) return e
completed = true
// 这个方法非常重要,最终会通知 RealCall,写 Request Body 已经完成,这个方法也会通知读 Response Body 已经完成。
return bodyComplete(bytesReceived, responseDone = false, requestDone = true, e = e)
}
}
RequestBodySink
是 Exchange
中的内部类,它的处理方式也非常简单,简单的判断了写入的 Request Body
的长度,如果长度错误会抛出异常,然后还有一个非常重要的地方,写入完成后会调用 bodyComplete()
方法,这个方法最终会通知到 RealCall
对象中去,Response Body
读完成,也是调用这个方法,后面会单独分析。
Exchange#readResponseHeaders()
接着看看读取 Response Header
的实现:
Kotlin
@Throws(IOException::class)
fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
try {
val result = codec.readResponseHeaders(expectContinue)
result?.initExchange(this)
return result
} catch (e: IOException) {
// 通知 eventListener 读取 header 失败
eventListener.responseFailed(call, e)
trackFailure(e)
throw e
}
}
上面的代码很简单,我们继续看 Http1ExchangeCodec#readResponseHeaders()
的实现:
为了更好的理解源码这里贴一个 Response
的例子:
text
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache/2.2.14 (Win32)
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
Content-Length: 88
Content-Type: text/html
Connection: Closed
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
Kotlin
override fun readResponseHeaders(expectContinue: Boolean): Response.Builder? {
// 状态检查
check(state == STATE_OPEN_REQUEST_BODY ||
state == STATE_WRITING_REQUEST_BODY ||
state == STATE_READ_RESPONSE_HEADERS) {
"state: $state"
}
try {
// 读取 Response 的状态行,例如:HTTP/1.1 200 OK
val statusLine = StatusLine.parse(headersReader.readLine())
val responseBuilder = Response.Builder()
.protocol(statusLine.protocol)
.code(statusLine.code)
.message(statusLine.message)
// 读取 header
.headers(headersReader.readHeaders())
return when {
expectContinue && statusLine.code == HTTP_CONTINUE -> {
null
}
statusLine.code == HTTP_CONTINUE -> {
state = STATE_READ_RESPONSE_HEADERS
responseBuilder
}
statusLine.code in (102 until 200) -> {
// Processing and Early Hints will mean a second headers are coming.
// Treat others the same for now
state = STATE_READ_RESPONSE_HEADERS
responseBuilder
}
else -> {
state = STATE_OPEN_RESPONSE_BODY
responseBuilder
}
}
} catch (e: EOFException) {
// Provide more context if the server ends the stream before sending a response.
val address = connection.route().address.url.redact()
throw IOException("unexpected end of stream on $address", e)
}
}
首先读取状态行:协议
+
+ Response Code
+
+ Response Message
,例如:HTTP/1.1 200 OK
,最后的 Response Header
的读取,会调用 HeadersReader#readHeaders()
方法。我们来看看它的实现:
Kotlin
/** Reads headers or trailers. */
fun readHeaders(): Headers {
// 构建 header builder 对象
val result = Headers.Builder()
while (true) {
// 读取行
val line = readLine()
// 如果读取到的行为空,表示 header 读取已经结束
if (line.isEmpty()) break
// 通过方法解析 Response Header
result.addLenient(line)
}
// 构建 Headers 对象。
return result.build()
}
上面的方法也非常简单,一行字符串就表示一个 Response Header
,通过 Headers.Builder#addLenient()
方法来解析这个字符串,如果字符串为空就表示 Response Header
已经读完了,这个和 Request Header
也是类似的,后面的内容就是对应的 Response Body
。
Exchange#openResponseBody()
现在继续看看 Response Body
的处理方法:
Kotlin
@Throws(IOException::class)
fun openResponseBody(response: Response): ResponseBody {
try {
val contentType = response.header("Content-Type")
val contentLength = codec.reportedContentLength(response)
// 构建 Response Body 的 Source
val rawSource = codec.openResponseBodySource(response)
// 再构建一个新的 Source
val source = ResponseBodySource(rawSource, contentLength)
// 构建新的 Response Body。
return RealResponseBody(contentType, contentLength, source.buffer())
} catch (e: IOException) {
eventListener.responseFailed(call, e)
trackFailure(e)
throw e
}
}
继续看看 Http1ExchangeCodec#openResponseBodySource()
方法的实现:
Kotlin
override fun openResponseBodySource(response: Response): Source {
return when {
!response.promisesBody() -> newFixedLengthSource(0)
response.isChunked -> newChunkedSource(response.request.url)
else -> {
val contentLength = response.headersContentLength()
if (contentLength != -1L) {
// 有固定长度的 Response Body
newFixedLengthSource(contentLength)
} else {
// 无固定长度的 Response Body
newUnknownLengthSource()
}
}
}
}
和 Request Body
一样,有固定长度的和没有固定长度的两种,我们以固定长度的为例子来看看 newFixedLengthSource()
方法的实现:
Kotlin
private fun newFixedLengthSource(length: Long): Source {
check(state == STATE_OPEN_RESPONSE_BODY) { "state: $state" }
state = STATE_READING_RESPONSE_BODY
return FixedLengthSource(length)
}
这里构建了一个 FixedLengthSource
对象,我们来看看它的实现:
Kotlin
private inner class FixedLengthSource(private var bytesRemaining: Long) :
AbstractSource() {
init {
if (bytesRemaining == 0L) {
// 如果长度为 0,直接通知读 Response Body 已经完成
responseBodyComplete()
}
}
override fun read(sink: Buffer, byteCount: Long): Long {
// 读的数量判断
require(byteCount >= 0L) { "byteCount < 0: $byteCount" }
// 状态判断
check(!closed) { "closed" }
if (bytesRemaining == 0L) return -1
// 读 Sink
val read = super.read(sink, minOf(bytesRemaining, byteCount))
if (read == -1L) {
connection.noNewExchanges() // The server didn't supply the promised content length.
val e = ProtocolException("unexpected end of stream")
responseBodyComplete()
throw e
}
bytesRemaining -= read
if (bytesRemaining == 0L) {
// 已经读取完毕
responseBodyComplete()
}
return read
}
override fun close() {
if (closed) return
if (bytesRemaining != 0L &&
!discard(ExchangeCodec.DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) {
connection.noNewExchanges() // Unread bytes remain on the stream.
// 读取完毕
responseBodyComplete()
}
closed = true
}
}
上面的代码也是比较简单,只是简单的判断了读取的大小状态和流的开关状态。
我们再来简单看看 ResponseBodySource
的实现:
Kotlin
internal inner class ResponseBodySource(
// 上面的 FixedLengthSource 对象
delegate: Source,
private val contentLength: Long
) : ForwardingSource(delegate) {
private var bytesReceived = 0L
private var invokeStartEvent = true
private var completed = false
private var closed = false
init {
// 如果长度为 0 就直接完成
if (contentLength == 0L) {
complete(null)
}
}
@Throws(IOException::class)
override fun read(sink: Buffer, byteCount: Long): Long {
// 关状态判断
check(!closed) { "closed" }
try {
// 真正的读
val read = delegate.read(sink, byteCount)
if (invokeStartEvent) {
invokeStartEvent = false
// 通知 eventListener Response 读开始
eventListener.responseBodyStart(call)
}
if (read == -1L) {
// 已经读取完毕
complete(null)
return -1L
}
val newBytesReceived = bytesReceived + read
if (contentLength != -1L && newBytesReceived > contentLength) {
throw ProtocolException("expected $contentLength bytes but received $newBytesReceived")
}
bytesReceived = newBytesReceived
if (newBytesReceived == contentLength) {
// 读取完毕
complete(null)
}
return read
} catch (e: IOException) {
throw complete(e)
}
}
@Throws(IOException::class)
override fun close() {
if (closed) return
closed = true
try {
super.close()
// 读取完毕
complete(null)
} catch (e: IOException) {
throw complete(e)
}
}
fun <E : IOException?> complete(e: E): E {
if (completed) return e
completed = true
// If the body is closed without reading any bytes send a responseBodyStart() now.
if (e == null && invokeStartEvent) {
invokeStartEvent = false
eventListener.responseBodyStart(call)
}
// 通知 RealCall Response Body 读取完毕
return bodyComplete(bytesReceived, responseDone = true, requestDone = false, e = e)
}
}
ResponseBodySource
中也是做了简单的状态判断,这里很重要的一个方法是在读取完成后,提前关闭后和出现异常后会调用 complete()
方法,然后在它之中还会调用 bodyComplete()
方法来通知 RealCall
,这里和 RequestBodySink
是类似的,不过这里要注意 ResponseBodySource
它是只有在库的调用地方真正使用这些数据时才会读,在 Response
返回的时候并没有读取这里的数据,这个和 CachedInterceptor
返回的缓存 Response Body
也是一样的。
一次 Http 请求结束的标志
在前面的文章中分析网络链接创建的过程中,有说到,在网络链接创建完成后 RealConnection
会被添加到 RealCall
中,对应的 RealCall
引用也会被添加到 RealConnection
中去。那什么时候才会把 RealConnection
中的 RealCall
引用移除呢???我们貌似没有看到这部分代码,返回 Response
时也没有发现这部分代码。
其实就是在一次 RealCall
真正的完成时就会清除 RealConnection
中的引用,而 Response
返回时,RealCall
并没有真正的完成,而是当请求的 Request Body
写完成和 Response Body
读完成时才标志请求完成了,在上面一节我们分析了他们的读写,完成后最终都会调用 Exchange#bodyComplete()
方法,我们再来看看它的代码实现:
Kotlin
fun <E : IOException?> bodyComplete(
bytesRead: Long,
responseDone: Boolean,
requestDone: Boolean,
e: E
): E {
if (e != null) {
// 记录失败
trackFailure(e)
}
if (requestDone) {
if (e != null) {
// 通知 eventListener 请求 Request 失败
eventListener.requestFailed(call, e)
} else {
// 通知 eventListener 请求 Request Body 结束
eventListener.requestBodyEnd(call, bytesRead)
}
}
if (responseDone) {
if (e != null) {
// 通知 eventListener 请求 Response 失败
eventListener.responseFailed(call, e)
} else {
// 通知 eventListener 请求 Response Body 结束
eventListener.responseBodyEnd(call, bytesRead)
}
}
// 通知 RealCall
return call.messageDone(this, requestDone, responseDone, e)
}
上面的代码只是简单通知 eventListener
各种请求状态,其主要是调用了 RealCall#messageDone()
方法,我们来看看它的实现:
Kotlin
internal fun <E : IOException?> messageDone(
exchange: Exchange,
requestDone: Boolean,
responseDone: Boolean,
e: E
): E {
if (exchange != this.exchange) return e // This exchange was detached violently!
var bothStreamsDone = false
var callDone = false
synchronized(this) {
// 各种状态的更新
if (requestDone && requestBodyOpen || responseDone && responseBodyOpen) {
if (requestDone) requestBodyOpen = false
if (responseDone) responseBodyOpen = false
bothStreamsDone = !requestBodyOpen && !responseBodyOpen
callDone = !requestBodyOpen && !responseBodyOpen && !expectMoreExchanges
}
}
if (bothStreamsDone) {
this.exchange = null
// 增加成功次数
this.connection?.incrementSuccessCount()
}
if (callDone) {
// 完成
return callDone(e)
}
return e
}
如果请求完成后会接着调用 callDone()
方法,我们继续看:
Kotlin
private fun <E : IOException?> callDone(e: E): E {
assertThreadDoesntHoldLock()
val connection = this.connection
if (connection != null) {
connection.assertThreadDoesntHoldLock()
val socket = synchronized(connection) {
// 检查 Connection 状态,如果返回 Socket 不为空,就表示要手动释放。
releaseConnectionNoEvents() // Sets this.connection to null.
}
if (this.connection == null) {
socket?.closeQuietly()
// 通知 eventListener 链接已经释放
eventListener.connectionReleased(this, connection)
} else {
check(socket == null) // If we still have a connection we shouldn't be closing any sockets.
}
}
val result = timeoutExit(e)
if (e != null) {
// 通知 eventListener 请求失败
eventListener.callFailed(this, result!!)
} else {
// 通知 event Listener 请求结束
eventListener.callEnd(this)
}
return result
}
上面代码会调用 releaseConnectionNoEvents()
方法来释放 RealCall
对 Connection
的引用,如果返回的 Socket
不为空就表示链接需要手动释放。我们再来看看它的源码实现:
Kotlin
internal fun releaseConnectionNoEvents(): Socket? {
val connection = this.connection!!
connection.assertThreadHoldsLock()
val calls = connection.calls
val index = calls.indexOfFirst { it.get() == this@RealCall }
check(index != -1)
// 将当前 RealCall 从 RealConnection 的引用中移除
calls.removeAt(index)
// 当前 RealCall 停止对 Connection 引用
this.connection = null
// 如果 RealConnection 中的引用 RealCall 变成了空,就表示它已经闲置了
if (calls.isEmpty()) {
connection.idleAtNs = System.nanoTime()
// 通知 ConnectionPool,当前 connection 已经闲置。
if (connectionPool.connectionBecameIdle(connection)) {
// 需要把当前链接真正关闭。
return connection.socket()
}
}
// 不需要真正关闭链接
return null
}
首先将 RealConnection#calls
中的引用移除,然后判断 RealConnection#calls
是否为空,如果已经为空了,就表示当前 RealConnection
已经闲置了,然后通知 ConnectionPool#connectionBecameIdle()
,如果这个方法返回的 Socket
不为空就表示需要释放。这个和 Request Header
与 Response Header
有关,如果他们指定了 Connection: close
,就表示不复用链接,然后 RealConnection#noNewExchanges
对象就会设置为 true
(这部分可以看看 CallServerInterceptor
),然后上面的 Socket
对象就不会为空。
我们接着看看 RealConnectionPool#connectionBecameIdle()
方法:
Kotlin
fun connectionBecameIdle(connection: RealConnection): Boolean {
connection.assertThreadHoldsLock()
return if (connection.noNewExchanges || maxIdleConnections == 0) {
// 需要关闭链接
connection.noNewExchanges = true
connections.remove(connection)
if (connections.isEmpty()) cleanupQueue.cancelAll()
true
} else {
// 不需要关闭,开启一个新的清除任务 Task。
cleanupQueue.schedule(cleanupTask)
false
}
}
上面的方法我在第二篇文章中也分析过,也比较简单。
这里的话请求结束的相关逻辑也就分析完了,可以简单的做一个总结:当请求的 Response
成功返回时请求并没有完成,需要等待 Response Body
处理完成(读取完成或者出现异常),如果你不需要 Response Body
里面的数据可以手动把它关闭。通过以上的操作就可以把 RealCall
从 RealConnection
中的引用移除。如果没有做上面的操作就可能出现 RealCall
引用泄漏的问题,如果出现引用的泄漏问题,OkHttp
也能够探测到,RealConnectionPool
中会有一个探测闲置 Connection
的 cleanupTask
(第二篇文章中有详细分析),他会调用以下的方法:
Kotlin
private fun pruneAndGetAllocationCount(connection: RealConnection, now: Long): Int {
connection.assertThreadHoldsLock()
val references = connection.calls
var i = 0
while (i < references.size) {
val reference = references[i]
// 引用不为空就表示没有泄漏
if (reference.get() != null) {
i++
continue
}
// 后续就表示泄漏了
// We've discovered a leaked call. This is an application bug.
val callReference = reference as CallReference
val message = "A connection to ${connection.route().address.url} was leaked. " +
"Did you forget to close a response body?"
Platform.get().logCloseableLeak(message, callReference.callStackTrace)
// 主动移除泄漏的 RealCall
references.removeAt(i)
connection.noNewExchanges = true
// If this was the last allocation, the connection is eligible for immediate eviction.
if (references.isEmpty()) {
connection.idleAtNs = now - keepAliveDurationNs
return 0
}
}
return references.size
}
通过上面的方法就能够探测到泄漏问题。
最后
通过五篇文章终于讲完了所有的 OkHttp
,如果你有耐心能够看完的话,相信你对 OkHttp
有一个全新的理解。