美文网首页
源码分析->撕开OkHttp(7)拦截器CallServerIn

源码分析->撕开OkHttp(7)拦截器CallServerIn

作者: 杨0612 | 来源:发表于2020-11-10 11:11 被阅读0次
源码分析基于 3.14.4
关键字:拦截器CallServerInterceptor

https://www.jianshu.com/p/4b0fde7f6b4c上一篇,讲了ConnectInterceptor拦截器的作用。

这次分析CallServerInterceptor

访问服务器拦截器,这是真正处理请求以及响应的地方。

还是看CallServerInterceptor.intercept

(1)exchange.writeRequestHeaders(request),发送request(请求行以及Heads),本质上就是把request请求行以及Heads信息拼接成字符串写入通信管道,拼接的字符串如下;

        /***
         * GET /user HTTP/1.1             -------------------请求行
         * Host:api.github.com            -------------------Heads
         * Content-Type:text/plain        -------------------Heads
         * Content-Length:400             -------------------Heads
         */

(2)接下来以GET请求为例
因为GET请求没有body,所以HttpMethod.permitsRequestBody(request.method()) && request.body() != null条件不成立,则执行exchange.noRequestBody(),把上面拼接的字符串flush出去;
(3)exchange.readResponseHeaders(false),读取响应头;
(4)responseBuilder.build(),构建response;
(5)exchange.openResponseBody(response),读取解析响应体body,解析后的body只是一个Buffer,连字符串都不是;
(16)如果请求头或者响应头带有Connection:closed信息,那么 exchange.noNewExchangesOnConnection()标记这个Connection可以被回收了;如果客户端希望keep-alive,但是服务器不希望,Connection也是要被标记的;
(7)如果响应码是204或者205,但是响应体body有内容,也就是(code == 204 || code == 205) && response.body().contentLength() > 0成立,则抛出ProtocolException异常;

Tips:204、205类似交互信息,主要是服务端通知客户端做什么,不会带有body的,例如204,告知客户端数据没有发生变化,可以用原来的数据;

(8)CallServerInterceptor没有下一个拦截器也没有后置处理,所以最后把response返回给ConnectInterceptor;

  @Override public Response intercept(Chain chain) throws IOException {
    ......
    exchange.writeRequestHeaders(request);
    ......
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
       ......
    } else {
      exchange.noRequestBody();
    }
    ......
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(false);
    }

    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
       ......
      response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build();

    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      exchange.noNewExchangesOnConnection();
    }

    if ((code == 204 || code == 205) && response.body().contentLength() > 0) {
      throw new ProtocolException(
          "HTTP " + code + " had non-zero Content-Length: " + response.body().contentLength());
    }

    return response;
  }
使用代理会有不同的请求行

前面提到exchange.writeRequestHeaders(request),这个方法是把请求行以及请求头拼接成字符串发给服务器,它内部会调到Http1ExchangeCodec.writeRequestHeaders

Http1ExchangeCodec是处理HTTP 1.1的,Http2ExchangeCodec是处理HTTP 2.0的;
Http1ExchangeCodec.writeRequestHeaders

主要看RequestLine.get方法;

  @Override public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, realConnection.route().proxy().type());
    writeRequest(request.headers(), requestLine);
  }
RequestLine.get

如果使用了代理并且是HTTP请求,那么请求行path路径是包含host(request.url()),否则path路径是不包含host的(requestPath(request.url()));

  public static String get(Request request, Proxy.Type proxyType) {
  ......
    if (includeAuthorityInRequestLine(request, proxyType)) {
      result.append(request.url());
    } else {
      result.append(requestPath(request.url()));
    }
    ......
  }

//普通请求行
GET /user HTTP/1.1 
//使用代理并且是HTTP的请求行
GET www.baidu.com/user HTTP/1.1 
如果请求带Expect:100-continue如何处理?

Expect:100-continue,表示客户端期望发送body,如果响应码为100,表示可以发,否则不可以发。
(1)调用exchange.writeRequestHeaders(request)发送请求以后,如果是POST请求并且带Body,那么HttpMethod.permitsRequestBody(request.method()) && request.body() != null条件成立,进入if逻辑;
(2)请求带Expect:100-continue,那么"100-continue".equalsIgnoreCase(request.header("Expect"))条件成立, responseBuilder = exchange.readResponseHeaders(true)读取响应头;
(3)如果服务器返回响应码100,表示可以接收数据,那么上面获取的responseBuilder 就是null,调用request.body().writeTo(bufferedRequestBody)把body发送出去;
(4)如果服务器返回响应码不是100,表示不可以接收数据,那么后续读取响应信息就可以了;

exchange.writeRequestHeaders(request);

    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        exchange.flushRequest();
        ......
        responseBuilder = exchange.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        ......
        request.body().writeTo(bufferedRequestBody);
      } else {
        ......
      }
    } else {
      exchange.noRequestBody();
    }
总结

(1)CallServerInterceptor,访问服务器拦截器,这是真正处理请求以及响应的地方;
(2)GET或POST不带body请求访问流程,发送请求Request(拼接请求行以及请求头)、读取响应头、解析响应体;
(3)POST带body访问流程,发送请求Request(拼接请求行以及请求头),如果请求头带有Expect:100-continue,那么读取响应头,响应码为100那就body,否则读取响应头、解析响应体;如果不带Expect:100-continue,那就发送请求体、读取响应头、解析响应体;

以上分析有不对的地方,请指出,互相学习,谢谢哦!

相关文章

网友评论

      本文标题:源码分析->撕开OkHttp(7)拦截器CallServerIn

      本文链接:https://www.haomeiwen.com/subject/lsncbktx.html