okhttp学习系列之CallServerInterceptor拦截器

这个拦截器是干嘛的

前面我们已经通过ConnectInterceptor拦截器建立了连接(参见ConnectInterceptor拦截器), 那CallServerInterceptor就是发起真正的网络请求, 他是最后一个拦截器

val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
    
    
  interceptors += client.networkInterceptors
}
//偶没有骗你, 他真的是最后一个拦截器
interceptors += CallServerInterceptor(forWebSocket)

主要流程

流程如下, 注意, 我们基于http1.1来进行讨论, 请求头里没有100-continue, 一切都以最简单的来
在这里插入图片描述

走读下代码

override fun intercept(chain: Interceptor.Chain): Response {
    
    
 ... 这里不用看了
 try {
    
    
	// 写入http header
   exchange.writeRequestHeaders(request)
   if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
    
    
   	// 只有非GET和Head的请求, 才会写body
     if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
    
    
       ... 这里流程忽略, 这里是给100-continue用的, 先不看吧
     }
     if (responseBuilder == null) {
    
    
       if (requestBody.isDuplex()) {
    
    
         ... 这里也不看, 应该是给http2用的(存疑)
       } else {
    
    
         // 写request body
         val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
         requestBody.writeTo(bufferedRequestBody)
         bufferedRequestBody.close()
       }
     } else {
    
    
     // 对于GET请求, 根本就没有body好吧
       exchange.noRequestBody()
       ...
     }
   } else {
    
    
     exchange.noRequestBody()
   }
   ...
 //上面write 完毕, 开始read
 try {
    
    
   if (responseBuilder == null) {
    
    
   // 先读取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 source进行一些封装
     response.newBuilder()
         .body(exchange.openResponseBody(response))
         .build()
   return response
 } catch (e: IOException) {
    
    
 ...
 }
}

主流程就是上面那个, 还是有一些特别有意思的逻辑

request header是怎么写的?

看一下写header的代码

override fun writeRequestHeaders(request: Request) {
    
    
  val requestLine = RequestLine.get(request, connection.route().proxy.type())
  writeRequest(request.headers, requestLine)
}

将命令搞成一个字符串 requestLine

fun get(request: Request, proxyType: Proxy.Type) = buildString {
    
    
  append(request.method)
  append(' ')
  if (includeAuthorityInRequestLine(request, proxyType)) {
    
    
    append(request.url)
  } else {
    
    
    append(requestPath(request.url))
  }
  append(" HTTP/1.1")
}

开始正式写

fun writeRequest(headers: Headers, requestLine: String) {
    
    
  check(state == STATE_IDLE) {
    
     "state: $state" }
  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
}

这里的sink是哪里来的?
他其实就是来自我们ConnectInterceptor拦截器中的connectSocket

//来自connectSocket方法
try {
    
    
  source = rawSocket.source().buffer()
  sink = rawSocket.sink().buffer()
} catch (npe: NullPointerException) {
    
    
  if (npe.message == NPE_THROW_WITH_NULL) {
    
    
    throw IOException(npe)
  }
}

为什么要封装response body, 不直接读出来

这里我们不应该忘记okhttp的基本用法

OkHttpClient client = new OkHttpClient();
  //创建一个Request
  Request request = new Request.Builder()
          .get()
          .url(url)
          .build();
  //通过client发起请求
  client.newCall(request).enqueue(new Callback() {
    
    
      @Override
      public void onFailure(Call call, IOException e) {
    
    
      }
      @Override
      public void onResponse(Call call, Response response) throws IOException {
    
    
          if (response.isSuccessful()) {
    
    
          // String str = response.body().string();
          }
      }
  });

返回的结果还是要在response中读出来的

如果一个response还没有读取内容, 那怎么保证对应的connect不被复用, 不被分配给别人

我们在ConnectInterceptor中知道, 连接是复用的, 在发起Call的时候, 会优先从连接池中拿连接, 如果没有, 采取新建一个连接.
那怎么保证正在使用的连接,或者说已经有response返回了, 但是还没有被read的connect, 不被分配给别的call呢?

因为我们每次用完response中的outputStream之后, 都对其进行了close操作, close操作之后, 会释放掉RealCall中的RealConnection.

所以我们一定要进行close操作啊.

代码看下
在Exchange的openResponseBody中, 可以看到, 我们拿到的ResponseBody是RealResponseBody的实例, 其中, 关键的BufferedSource是ResponseBodySource的实例

@Throws(IOException::class)
fun openResponseBody(response: Response): ResponseBody {
    
    
 try {
    
    
 ...
 //注意这里, 是ResponseBodySource
   val source = ResponseBodySource(rawSource, contentLength)
   return RealResponseBody(contentType, contentLength, source.buffer())
 } catch (e: IOException) {
    
    
   eventListener.responseFailed(call, e)
   trackFailure(e)
   throw e
 }
}

当我们调用了close方法之后, 实际上代码流程会这么走
在这里插入图片描述
最终会释放RealConnection中的calls.
而我们在ConnectInterceptor中看过下面的代码, 在申请连接的时候, 如果calls不为空, 那么就不选取这个连接

internal fun isEligible(address: Address, routes: List<Route>?): Boolean {
    
    
  assertThreadHoldsLock()

  // If this connection is not accepting new exchanges, we're done.
  // 如果判断一个realConnection是否可以用,主要还是靠noNewExchange来的
  if (calls.size >= allocationLimit || noNewExchanges) return false
  ...
  }

猜你喜欢

转载自blog.csdn.net/weixin_43662090/article/details/114871057
今日推荐