OkHttp深入理解(5)CallServerInterceptor

来源:互联网 发布:w3school sql 编辑:程序博客网 时间:2024/06/07 00:17

这是整个拦截器链中的最后一个拦截器,经过了前面拦截器进行的准备工作,在CallServerInterceptor中会真正向服务器发起请求。intercept方法的代码不多:

 @Override public Response intercept(Chain chain) throws IOException {    RealInterceptorChain realChain = (RealInterceptorChain) chain;    HttpCodec httpCodec = realChain.httpStream();    StreamAllocation streamAllocation = realChain.streamAllocation();    RealConnection connection = (RealConnection) realChain.connection();    Request request = realChain.request();    long sentRequestMillis = System.currentTimeMillis();    realChain.eventListener().requestHeadersStart(realChain.call());    httpCodec.writeRequestHeaders(request);    realChain.eventListener().requestHeadersEnd(realChain.call(), request);    Response.Builder responseBuilder = null;    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != 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".equalsIgnoreCase(request.header("Expect"))) {        httpCodec.flushRequest();        realChain.eventListener().responseHeadersStart(realChain.call());        responseBuilder = httpCodec.readResponseHeaders(true);      }      if (responseBuilder == null) {        // Write the request body if the "Expect: 100-continue" expectation was met.        realChain.eventListener().requestBodyStart(realChain.call());        long contentLength = request.body().contentLength();        CountingSink requestBodyOut =            new CountingSink(httpCodec.createRequestBody(request, contentLength));        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);        request.body().writeTo(bufferedRequestBody);        bufferedRequestBody.close();        realChain.eventListener()            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);      } else if (!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.        streamAllocation.noNewStreams();      }    }    httpCodec.finishRequest();    if (responseBuilder == null) {      realChain.eventListener().responseHeadersStart(realChain.call());      responseBuilder = httpCodec.readResponseHeaders(false);    }    Response response = responseBuilder        .request(request)        .handshake(streamAllocation.connection().handshake())        .sentRequestAtMillis(sentRequestMillis)        .receivedResponseAtMillis(System.currentTimeMillis())        .build();    realChain.eventListener()        .responseHeadersEnd(realChain.call(), response);    int code = response.code();    if (forWebSocket && code == 101) {      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.      response = response.newBuilder()          .body(Util.EMPTY_RESPONSE)          .build();    } else {      response = response.newBuilder()          .body(httpCodec.openResponseBody(response))          .build();    }    if ("close".equalsIgnoreCase(response.request().header("Connection"))        || "close".equalsIgnoreCase(response.header("Connection"))) {      streamAllocation.noNewStreams();    }    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {      throw new ProtocolException(          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());    }    return response;  }

大致流程如下:
1. 写入请求头
2. 判断呢是否需要发送RequestBody,如果不需要,直接跳转到步骤5
3. 如果2满足,继续判断header中Expect域是否为100-continue,这个请求头字段的作用是在发送RequestBody前向服务器确认是否接受RequestBody,如果服务器不接受也就没有发送的必要了。有这个字段,相当于一次简单的握手操作,会等待拿到服务器返回的ResponseHeaders之后再继续,如果服务器接收RequestBody,会返回null。
4. 根据3的判断,如果RequestBuilder为null,说明Expect不为100-continue或者服务器同意接收RequestBody。这时就开始向流中写入RequestBody。
5. 读取响应头信息
6. 构建Response,写入原请求、握手情况、请求时间、得到结果的时间等
7. 构建响应体
8. 如果请求或者响应头要求close连接,进行断开
9. 针对204/205状态码处理
10. 返回Response

针对3中的Expect字段详解可以看看这段:

  1、http 100-continue用于客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据。在现实应用中,通过在POST大数据时,才会使用100-continue协议。
  2、客户端策略。
    1)如果客户端有POST数据要上传,可以考虑使用100-continue协议。加入头{“Expect”:”100-continue”}
    2)如果没有POST数据,不能使用100-continue协议,因为这会让服务端造成误解。
    3)并不是所有的Server都会正确实现100-continue协议,如果Client发送Expect:100-continue消息后,在timeout时间内无响应,Client需要立马上传POST数据。
    4)有些Server会错误实现100-continue协议,在不需要此协议时返回100,此时客户端应该忽略。
  3、服务端策略。
    1)正确情况下,收到请求后,返回100或错误码。
    2)如果在发送100-continue前收到了POST数据(客户端提前发送POST数据),则不发送100响应码(略去)。


CallServerInterceptor的整体逻辑不难。复杂的细节被封装到了HttpCodec实现类里,它有针对HTTP 1.1/HTTP2的两个实现类,而里面进行具体的流的写入/读取操作,又被封装到了okio库的BufferedSink等相关的类中,要深入下去工作量还很多。暂时就这样了。
至此针对OkHttp的拦截器的笔记就写完了,不容易唉。接下来有时间会继续记录一些诸如路由寻址、缓存具体实现等的细节。