CallServerInterceptor
請求服務(wù)攔截器,這是OKHttp最后一個默認(rèn)的攔截器,當(dāng)與服務(wù)建立好連接之后,就可以進(jìn)行真正的網(wǎng)絡(luò)請求了朝群。利用exchange
發(fā)出請求到服務(wù)器并且解析生成Response
@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 there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
// Continue" response before transmitting the request body. If we don't get that, return
// what we did get (such as a 4xx response) without ever transmitting the request body.
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.connection.isMultiplexed) {
// If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
// from being reused. Otherwise we're still obligated to transmit the request body to
// leave the connection in a consistent state.
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
if (requestBody == null || !requestBody.isDuplex()) {
exchange.finishRequest()
}
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) {
// Server sent a 100-continue even though we did not request one. Try again to read the actual
// response status.
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
}
首先會調(diào)用exchange.writeRequestHeaders(request)
將請求頭寫入到緩存中(這時候還沒有發(fā)送給服務(wù)器),然后立即判斷請求頭中的參數(shù)Expect: 100-continue
:
var responseBuilder: Response.Builder? = null
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.
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// 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()
}
這個請求頭代表了在發(fā)送請求體之前需要和服務(wù)器確定是否愿意接受客戶端發(fā)送的請求體中符。所以 permitsRequestBody
判斷為是否會攜帶請求體的方式(POST)潜圃,如果命中if,則會先給服務(wù)器發(fā)起一次查詢是否愿意接收請求體舟茶,這時候如果服務(wù)器愿意會響應(yīng)100(沒有響應(yīng)體,responseBuilder
即為null)堵第。這時候才能夠繼續(xù)發(fā)送剩余請求數(shù)據(jù)吧凉。
但是如果服務(wù)器不同意接受請求體,那么我們就需要標(biāo)記該連接不能再被復(fù)用踏志,調(diào)用noNewExchangesOnConnection()
關(guān)閉相關(guān)的Socket阀捅。最終調(diào)用exchange.flushRequest()
將請求發(fā)送到服務(wù)器。
接下來
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()
這時 responseBuilder 的情況即為:
POST方式請求针余,請求頭中包含 Expect 饲鄙,服務(wù)器允許接受請求體,并且已經(jīng)發(fā)出了請求體圆雁, responseBuilder 為null;
POST方式請求忍级,請求頭中包含 Expect ,服務(wù)器不允許接受請求體伪朽, responseBuilder 不為null
POST方式請求轴咱,未包含 Expect ,直接發(fā)出請求體烈涮, responseBuilder 為null;
POST方式請求朴肺,沒有請求體, responseBuilder 為null;
GET方式請求坚洽, responseBuilder 為null;
對應(yīng)上面的5種情況戈稿,讀取響應(yīng)頭并且組成響應(yīng) Response ,注意:此 Response 沒有響應(yīng)體讶舰。同時需要注意的是鞍盗,如果服務(wù)器接受 Expect: 100-continue 這是不是意味著我們發(fā)起了兩次 Request 需了?那此時的響應(yīng)頭是第一次查詢服務(wù)器是否支持接受請求體的,而不是真正的請求對應(yīng)的結(jié)果響應(yīng)橡疼。所以緊接著:
var code = response.code
if (code == 100) {
// Server sent a 100-continue even though we did not request one. Try again to read the actual
// response status.
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
如果響應(yīng)是100援所,這代表了是請求 Expect: 100-continue 成功的響應(yīng),需要馬上再次讀取一份響應(yīng)頭欣除,這才是真正的請求對應(yīng)結(jié)果響應(yīng)頭住拭。接下來創(chuàng)建response
并返回結(jié)果:
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()
}
// 如果沒有響應(yīng)體,但Content-Length請求頭大于0历帚,拋出異常
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
}
return response
forWebSocket
代表websocket
的請求滔岳,我們直接進(jìn)入else,這里就是讀取響應(yīng)體數(shù)據(jù)挽牢。然后判斷請求和服務(wù)器是不是都希望長連接谱煤,一旦有一方指明close
,那么就需要關(guān)閉socket
禽拔。而如果服務(wù)器返回204/205
刘离,一般情況而言不會存在這些返回碼,但是一旦出現(xiàn)這意味著沒有響應(yīng)體睹栖,但是解析到的響應(yīng)頭中包含Content-Lenght
且不為0硫惕,這表響應(yīng)體的數(shù)據(jù)字節(jié)長度。此時出現(xiàn)了沖突野来,直接拋出協(xié)議異常恼除!
總結(jié)
在這個攔截器中就是完成HTTP協(xié)議報(bào)文的封裝與解析。現(xiàn)在OKHttp的源碼基本就完了
整個OkHttp功能的實(shí)現(xiàn)就在這五個默認(rèn)的攔截器中曼氛,所以先理解攔截器模式的工作機(jī)制是先決條件豁辉。這五個攔截器分別為: 重試攔截器、橋接攔截器舀患、緩存攔截器徽级、連接攔截器、請求服務(wù)攔截器聊浅。每一個攔截器負(fù)責(zé)的工作不一樣灰追,就好像工廠流水線,最終經(jīng)過這五道工序狗超,就完成了最終的產(chǎn)品弹澎。
但是與流水線不同的是,OkHttp中的攔截器每次發(fā)起請求都會在交給下一個攔截器之前干一些事情努咐,在獲得了結(jié)果之后又干一些事情苦蒿。整個過程在請求向是順序的,而響應(yīng)向則是逆序渗稍。
當(dāng)用戶發(fā)起一個請求后佩迟,會由任務(wù)分發(fā)起 Dispatcher 將請求包裝并交給重試攔截器處理团滥。
重試攔截器在交出(交給下一個攔截器)之前,負(fù)責(zé)判斷用戶是否取消了請求报强;在獲得了結(jié)果之后灸姊,會根據(jù)響應(yīng)碼判斷是否需要重定向,如果滿足條件那么就會重啟執(zhí)行所有攔截器秉溉。
橋接攔截器在交出之前力惯,負(fù)責(zé)將HTTP協(xié)議必備的請求頭加入其中(如:Host)并添加一些默認(rèn)的行為(如:GZIP壓縮);在獲得了結(jié)果后召嘶,調(diào)用保存cookie接口并解析GZIP數(shù)據(jù)父晶。
緩存攔截器顧名思義,交出之前讀取并判斷是否使用緩存弄跌;獲得結(jié)果后判斷是否緩存甲喝。
連接攔截器在交出之前,負(fù)責(zé)找到或者新建一個連接铛只,并獲得對應(yīng)的socket流埠胖;在獲得結(jié)果后不進(jìn)行額外的處理。
請求服務(wù)器攔截器進(jìn)行真正的與服務(wù)器的通信淳玩,向服務(wù)器發(fā)送數(shù)據(jù)直撤,解析讀取的響應(yīng)數(shù)據(jù)。在經(jīng)過了這一系列的流程后凯肋,就完成了一次HTTP請求。