2019-11-05Okhttp源码解析-拦截器<三>
2019-11-05 本文已影响0人
猫KK
5. CallServerInterceptor
前面分析了前四个拦截器,现在到最后一个,用于请求服务器数据
class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
//获取exchange,exchange 是在ConnectInterceptor拦截器中初始化的
val exchange = realChain.exchange()
//获取请求
val request = realChain.request()
//获取请求数据
val requestBody = request.body
val sentRequestMillis = System.currentTimeMillis()
//向服务器写请求头信息,就是通过ConnectInterceptor 获取的socket的输入输出流向服务器写数据
exchange.writeRequestHeaders(request)
var responseHeadersStarted = false
var responseBuilder: Response.Builder? = null
//判断方法是GET或HEAD,如果是这两种方法,是不需要向服务器写请求数据的
if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
// 判断是否为100-continue 情况
if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseHeadersStarted = true
exchange.responseHeadersStart()
responseBuilder = exchange.readResponseHeaders(true)
}
//不为100-continue 情况,向服务器写请求数据
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 (!responseHeadersStarted) {
exchange.responseHeadersStart()
}
//读服务器返回的头信息
if (responseBuilder == null) {
responseBuilder = exchange.readResponseHeaders(false)!!
}
var response = responseBuilder
.request(request)
.handshake(exchange.connection()!!.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
var code = response.code
//判断状态码为100情况
if (code == 100) {
// server sent a 100-continue even though we did not request one.
// try again to read the actual response
response = exchange.readResponseHeaders(false)!!
.request(request)
.handshake(exchange.connection()!!.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
code = response.code
}
//响应头获取完毕
exchange.responseHeadersEnd(response)
//处理101情况
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 {
//不是101情况,获取服务器的响应数据,赋值到body中
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()
}
//判断204、205情况
if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
throw ProtocolException(
"HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
}
//返回响应数据
return response
}
}
对于向服务请写请求头
@Throws(IOException::class)
fun writeRequestHeaders(request: Request) {
try {
eventListener.requestHeadersStart(call)
//通过前面获取的流来写
codec.writeRequestHeaders(request)
eventListener.requestHeadersEnd(call, request)
} catch (e: IOException) {
eventListener.requestFailed(call, e)
trackFailure(e)
throw e
}
}
codec 是在ConnectInterceptor拦截器中实现的,也就是RealConnection.newCodec()方法
//RealConnection
@Throws(SocketException::class)
internal fun newCodec(client: OkHttpClient, chain: Interceptor.Chain): ExchangeCodec {
val socket = this.socket!!
val source = this.source!!
val sink = this.sink!!
val http2Connection = this.http2Connection
//判断http2 和 http1
return if (http2Connection != null) {
Http2ExchangeCodec(client, this, chain, http2Connection)
} else {
socket.soTimeout = chain.readTimeoutMillis()
source.timeout().timeout(chain.readTimeoutMillis().toLong(), MILLISECONDS)
sink.timeout().timeout(chain.writeTimeoutMillis().toLong(), MILLISECONDS)
Http1ExchangeCodec(client, this, source, sink)
}
}
判断http2 和http1 关于两者的区别,请自行了解,这里来看http1 的情况,所以codec的值为Http1ExchangeCodec,看Http1ExchangeCodec.writeRequestHeaders()方法
override fun writeRequestHeaders(request: Request) {
val requestLine = RequestLine.get(
request, realConnection!!.route().proxy.type())
writeRequest(request.headers, requestLine)
}
fun writeRequest(headers: Headers, requestLine: String) {
check(state == STATE_IDLE) { "state: $state" }
//通过okio 向服务器写数据
sink.writeUtf8(requestLine).writeUtf8("\r\n")
for (i in 0 until headers.size) {
sink.writeUtf8(headers.name(i))
.writeUtf8(": ")
.writeUtf8(headers.value(i))
.writeUtf8("\r\n")
}
sink.writeUtf8("\r\n")
state = STATE_OPEN_REQUEST_BODY
}
循环头信息,不断的写数据,注意,往服务器写数据是规定格式的,所以就会像上面一样又是\r\n的
对于写请求数据,一般来说我们要写表单数据时是这样构建的
var build = FormBody.Builder()
.add("text", "text")
.build()
var request = Request.Builder()
.url("https://wanandroid.com/wxarticle/chapters/json")
.post(build)
.build()
所以拦截器中的requestBody就是FormBody对象,看里面的writeTo方法
@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
writeOrCountBytes(sink, false)
}
private fun writeOrCountBytes(sink: BufferedSink?, countBytes: Boolean): Long {
var byteCount = 0L
//获取buffer
val buffer: Buffer = if (countBytes) Buffer() else sink!!.buffer
//循环不断往服务器写数据
for (i in 0 until encodedNames.size) {
if (i > 0) buffer.writeByte('&'.toInt())
buffer.writeUtf8(encodedNames[i])
buffer.writeByte('='.toInt())
buffer.writeUtf8(encodedValues[i])
}
if (countBytes) {
byteCount = buffer.size
buffer.clear()
}
return byteCount
}
到此,okhttp 的拦截器都已经分析完成。