美文网首页
Okhttp之CallServerInterceptor拦截器分

Okhttp之CallServerInterceptor拦截器分

作者: Android程序员老鸦 | 来源:发表于2021-07-01 16:08 被阅读0次

Okhttp的浅层架构分析
Okhttp的责任链模式和拦截器分析
Okhttp之RetryAndFollowUpInterceptor拦截器分析
Okhttp之BridgeInterceptor拦截器分析
Okhttp之CacheInterceptor拦截器分析
Okhttp之ConnectInterceptor拦截器分析
Okhttp之网络连接相关三大类RealConnection、ConnectionPool、StreamAllocation
Okhttp之CallServerInterceptor拦截器分析
浅析okio的架构和源码实现

CallServerInterceptor,访问服务器的拦截器,是最后一个拦截器了,要做的事情就是跟服务器交互,把请求发送给服务器,同时拿到服务器返回的response:

public final class CallServerInterceptor implements Interceptor 
    @Override
    public Response intercept(Chain chain) throws IOException {
        //这些对象在前面的Interceptor都已经创建完毕
        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());
        //1. 写入请求头
        //这个方法分析一下,这个httpCodec我们走http2.0分支的
        httpCodec.writeRequestHeaders(request);
        realChain.eventListener().requestHeadersEnd(realChain.call(), request);

        Response.Builder responseBuilder = null;
        //检测一下是否是允许的请求方式,body不为空
        if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
             //一个小知识点,关于"Expect: 100-continue"的
             //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响应码(略去)。
            //如果请求中存在“Expect:100-continue”标头,请在发送请求主体之前等待“HTTP / 1.1 100 Continue”响应。
            // 如果我们没有得到,请返回我们得到的内容(例如4xx响应),不继续发送请求主体。
            if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
                //flush请求头
                httpCodec.flushRequest();
                
                realChain.eventListener().responseHeadersStart(realChain.call());
              //这个方法会进入等待,当后台返回100应答的时候responseBuilder 为空
                //点进去就是调用了stream的takeHeaders()方法获取读到的header
                responseBuilder = httpCodec.readResponseHeaders(true);
            }
            //2 写入请求体
            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();
            }
        }
        //关闭request输出
        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();
        //4 读取响应体
        int 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
            responseBuilder = httpCodec.readResponseHeaders(false);

            response = responseBuilder
                    .request(request)
                    .handshake(streamAllocation.connection().handshake())
                    .sentRequestAtMillis(sentRequestMillis)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();

            code = response.code();
        }

        realChain.eventListener()
                .responseHeadersEnd(realChain.call(), response);
        //空的返回
        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 {
            //组织body
            response = response.newBuilder()
                    .body(httpCodec.openResponseBody(response))
                    .build();
        }
        //标记noNewStreams
        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;
    }
}

//Http2Codec的writeRequestHeaders()方法
 @Override public void writeRequestHeaders(Request request) throws IOException {
    if (stream != null) return;
  
    boolean hasRequestBody = request.body() != null;
    //组装一下header
    List<Header> requestHeaders = http2HeadersList(request);
    //newStream方法是Http2Connection的,继续追踪下去
    stream = connection.newStream(requestHeaders, hasRequestBody);
    stream.readTimeout().timeout(chain.readTimeoutMillis(), TimeUnit.MILLISECONDS);
    stream.writeTimeout().timeout(chain.writeTimeoutMillis(), TimeUnit.MILLISECONDS);
  }

  /**
   * Returns a new locally-initiated stream.
   * @param out true to create an output stream that we can use to send data to the remote peer.
   * Corresponds to {@code FLAG_FIN}.
   */
//Http2Connection的newStream()方法
  public Http2Stream newStream(List<Header> requestHeaders, boolean out) throws IOException {
    return newStream(0, requestHeaders, out);
  }

  private Http2Stream newStream(
      int associatedStreamId, List<Header> requestHeaders, boolean out) throws IOException {
    boolean outFinished = !out;
    boolean inFinished = false;
    boolean flushHeaders;
    Http2Stream stream;
    int streamId;

    synchronized (writer) {
      synchronized (this) {
        if (nextStreamId > Integer.MAX_VALUE / 2) {
          shutdown(REFUSED_STREAM);
        }
        if (shutdown) {
          throw new ConnectionShutdownException();
        }
        streamId = nextStreamId;
        nextStreamId += 2;
        stream = new Http2Stream(streamId, this, outFinished, inFinished, null);
        flushHeaders = !out || bytesLeftInWriteWindow == 0L || stream.bytesLeftInWriteWindow == 0L;
        if (stream.isOpen()) {
          //这个new出来的stream也保存在streams里,是个map
          //同时会返回赋值给Http2Codec的成员变量stream
          //每次读到数据都会调用Http2Connection的readers()方法把数据存到相应的stream
          streams.put(streamId, stream);
        }
      }
      if (associatedStreamId == 0) {
        //走着里,完成写header操作
        writer.synStream(outFinished, streamId, associatedStreamId, requestHeaders);
      } else if (client) {
        throw new IllegalArgumentException("client streams shouldn't have associated stream IDs");
      } else { // HTTP/2 has a PUSH_PROMISE frame.
        writer.pushPromise(associatedStreamId, streamId, requestHeaders);
      }
    }

    if (flushHeaders) {
      writer.flush();
    }

    return stream;
  }

至此OKhttp源码分析完毕,此次分析借鉴了很多之前的大神分析的成果,在这里表示感谢,本来以为分析了几个拦截器后就行了的,但是还是没忍住去看真正的socket和读写交互的代码,当然也只是了解了大概走向,详细的还没去细读,但也是收获很大了,很多以前堵着的思绪都通了不少。

相关文章

网友评论

      本文标题:Okhttp之CallServerInterceptor拦截器分

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