4.OkHttp的请求拦截链

OkHttp请求的核心处理就是这一系列的拦截链

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //建立一个完整的拦截器堆栈。
    List<Interceptor> interceptors = new ArrayList<>();
    //OkHttp初始化添加的拦截器
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }

这里面看到 ,第一个add的是client里面自己定义的拦截链集合和如果不是webSocket的话能添加客户端网络的拦截链,然后后面依次添加的拦截器有

RetryAndFollowUpInterceptor 重试和跟进拦截器
BridgeInterceptor           桥拦截器
CacheInterceptor            缓存拦截器
ConnectInterceptor          链接拦截器
CallServerInterceptor       呼叫服务拦截器
RealInterceptorChain        实际拦截链,其中携带整个拦截器链:所有应用拦截器,OkHttp核心,所有网络拦截器,最后是网络呼叫者。

这种链式调用在设计模式里有个叫责任链模式,这个的话我们进去看下源码印证一下,因为他这个集合是一个带有泛型的,所以直接看他的泛型

/**
 * Observes, modifies, and potentially short-circuits requests going out and the corresponding
 * responses coming back in. Typically interceptors add, remove, or transform headers on the request
 * or response.
 * 观察,修改和潜在地短路请求以及相应的响应。通常,拦截器会在请求或响应中添加,删除或变换头文件。
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

然后再看RealInterceptorChain的代码因为后面调用的是他的proceed方法,可以知道他肯定是实现了Interceptor的Chain,我们先来看他的初始化的过程

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest);

直接看他的构造函数

 public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

为null的先不管,看那些不为null的, 传过来了一个请求链,index是0,还有request 接着调用了RealInterceptorChain的proceed方法

 @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    //如果我们已经有一个流,请确认传入的请求将使用它
    if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    //如果我们已经有一个流,确认这是唯一的call在chain.proceed()中
    if (this.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    // 调用链中的下一个拦截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    //  确认下一个拦截器将其所需的调用链接到chain.proceed()
    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }

这里面可以看到最后调用的是proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection)

首先是几个判断,流程如下

  1. httpCodec不为空并且connection的url不支持这个请求的Url,那么抛出异常
  2. httpCodec不为空且当前的请求数大于1,抛出异常
  3. 开吃调用拦截链的逻辑
  4. httpCodec不为空并且当前索引加1小于请求链的长度,下一个请求的call不为1 ,抛出异常。
  5. response为空,抛出异常
  6. return response

这块逻辑 会不停的去调用下一个

  // Call the next interceptor in the chain.
    // 调用链中的下一个拦截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

第一次运行到这里的时候,调用的是RetryAndFollowUpInterceptor这个拦截器的intercept方法

RetryAndFollowUpInterceptor 重试和跟进拦截器

之前有简单介绍过这个拦截器,所以我们直接看他的intercept方法

@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();

    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
      try {
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), false, request)) {
          throw e.getLastConnectException();
        }
        releaseConnection = false;
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, requestSendStarted, request)) throw e;
        releaseConnection = false;
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Request followUp = followUpRequest(response);

      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }

      closeQuietly(response.body());

      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      if (followUp.body() instanceof UnrepeatableRequestBody) {
        streamAllocation.release();
        throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
      }

      if (!sameConnection(response, followUp.url())) {
        streamAllocation.release();
        streamAllocation = new StreamAllocation(
            client.connectionPool(), createAddress(followUp.url()), callStackTrace);
      } else if (streamAllocation.codec() != null) {
        throw new IllegalStateException("Closing the body of " + response
            + " didn't close its backing stream. Bad interceptor?");
      }

      request = followUp;
      priorResponse = response;
    }
  }

这边的流程屡一下

  1. 获取request
  2. new 一个StreamAllocation
  3. 然后再去调用当前传过来的chain的proceed方法
  4. 然后返回response
  5. 组装response和一些善后处理

敏锐的我们看到了streamAllocation 按照字面的意思是 流分配 传入的值是连接池,url,和一个callStackTrace

SteamAllocation上面的描述我们看下,直接中文版翻译过来:

 *  该类协调三个实体之间的关系
 *
 * Connections:   到远程服务器的物理套接字连接。 这些建立可能很慢,所以有必要能够取消当前连接的连接。
 * Streams:       在连接上分层的逻辑HTTP请求/响应对。 每个连接都有自己的分配限制,它定义了连接可以携带多少个并发流。
 *                 HTTP / 1.x连接一次可以携带1个流,HTTP / 2通常携带多个。
 * Calls:         流的逻辑顺序,通常是初始请求及其后续请求。 我们更喜欢将单个Call的所有流保留在同一个连接上,以获得更好的行为和位置。
 *
 * 该类的实例代表呼叫,在一个或多个连接上使用一个或多个流。 该类有API可以释放以上每种资源:
 *          {@link #noNewStreams()} 将阻止连接被用于新的流。在{@code Connection: close}header之后或当连接可能不一致时使用此选项。
 *          {@link #streamFinished streamFinished()}从此分配中释放活动流。
 *                                  请注意,在给定时间只有一个流可能处于活动状态,因此在使用{@link #newStream newStream()}
 *                                  创建后续流之前,有必要调用{@link #streamFinished streamFinished()}。
 *         {@link #release()}删除连接上的呼叫保持。 请注意,这不会立即释放连接,如果有一个流仍然挥之不去。
 *                              这种情况发生在通话完成但其响应体尚未完全消耗的情况下。
 *
 * 此类支持{@linkplain # asynchronous canceling}。
 *                            这是为了具有最小的爆炸半径。
 *                            如果HTTP / 2流处于活动状态,则取消将取消该流,但不会将其他流分享其连接。
 *                            但是如果TLS握手仍在进行中,则取消可能会破坏整个连接。

这个我们大致就知道了这个类是来干嘛的了,在RetryAndFollowUpInterceptor里面他只进行了 realease和streamFailed操作 ,推测他的调用应该在下一个拦截链里面,我们根据上面的拦截器集合知道 下一个调用的是BridgeInterceptor拦截器

BridgeInterceptor

这个拦截器主要的职责是

从应用程序代码到网络代码的桥梁。 首先,它根据用户请求构建网络请求。 然后它继续呼叫网络。 最后,它从网络响应中构建用户响应。 主要构件的是网络请求的header的部分和组装返回来的body

(昨天睡的晚,今天好困啊。。。。)

这个类不大,直接放上来讲

public final class BridgeInterceptor implements Interceptor {
  private final CookieJar cookieJar;

  //放进去构建的时候的CookieJar
  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    //老套路,拿到chain的request
    Request userRequest = chain.request();
    //利用request的Builder新建一个request
    Request.Builder requestBuilder = userRequest.newBuilder();
    //初始化requestBody
    RequestBody body = userRequest.body();
    
    //获取contentType 因为是在TestMain里面设置的body,用的FormBody,他的CONTENT_TYPE是    MediaType.parse("application/x-www-form-urlencoded");
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }
    
      //设置Content-Length
      long contentLength = body.contentLength();
      if (contentLength != -1) {
        requestBuilder.header("Content-Length", Long.toString(contentLength));
        requestBuilder.removeHeader("Transfer-Encoding");
      } else {
        requestBuilder.header("Transfer-Encoding", "chunked");
        requestBuilder.removeHeader("Content-Length");
      }
    }
    //设置Host
    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    // 如果我们添加了一个“Accept-Encoding:gzip”头域,我们也负责解压缩传输流。
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }
    
    //头部加入cookie
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }
    //头部加入UA头
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }

    /**
    *----------------------------------------------调用下一个链条的proceed方法  责任链模式---------------------------------
    **/
    Response networkResponse = chain.proceed(requestBuilder.build());
            
    //后面的是请求回来的
    
    //保存cookiejar
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    
    //构建response
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    //处理gzip和encode
    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }
    //返回response
    return responseBuilder.build();
  }

  /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
  private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
      if (i > 0) {
        cookieHeader.append("; ");
      }
      Cookie cookie = cookies.get(i);
      cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
  }
}

这里截止到调用下个拦截链的时候,做了以下几件事情:

  1. 构建请求体
  2. 设置content-type和content-length
  3. 设置Host
  4. 设置Connection
  5. 处理gzip
  6. 设置cookie
  7. 设置User-Agent
  8. 调用下一个拦截链

按照上面的顺序,下一个拦截链是CacheInterceptor,CacheInterceptor是个比较复杂的拦截链,如果我们没设置的话里面的判断都是不进去的,然后调用下一个chain,下一个是ConnectInterceptor

ConnectInterceptor

这个拦截器做的事情看起来简单,实际上是比较复杂的,他的主要职责是:

打开与目标服务器的连接,并进入下一个拦截器

代码量很少

public final class ConnectInterceptor implements Interceptor {
  public final OkHttpClient client;

  public ConnectInterceptor(OkHttpClient client) {
    this.client = client;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    //如果是get请求 不做健康检查
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
}

这里重要的就是streamAllocation这个类,这边调用了他的newStream()方法和connection()方法 ,我们点进去分析这两个方法

//在retryAndFollowUp里面已经初始化过 我们这边就直接看他的方法里面

  public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
    int connectTimeout = client.connectTimeoutMillis();
    int readTimeout = client.readTimeoutMillis();
    int writeTimeout = client.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    //从连接池里寻找一个健康的connection
    try {
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
      HttpCodec resultCodec = resultConnection.newCodec(client, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }
    } catch (IOException e) {
      throw new RouteException(e);
    }
  }

这里先初始化了几个时间 ,然后去find了一个健康的connection 返回RealConnection

我们看这个小蝌蚪找妈妈的方法

/**
   * Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated until a healthy connection is found.
   *
   * 找到一个健康的连接并返回它。 如果不健康,则重复该过程直到发现健康的连接。
   */
  private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          connectionRetryEnabled);

      // If this is a brand new connection, we can skip the extensive health checks.
      //如果这是一个全新的联系,我们可以跳过广泛的健康检查。
      synchronized (connectionPool) {
        if (candidate.successCount == 0) {
          return candidate;
        }
      }

      // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
      // isn't, take it out of the pool and start again.
      //做一个(潜在的慢)检查,以确认汇集的连接是否仍然良好。 如果不是,请将其从池中取出并重新开始。
      if (!candidate.isHealthy(doExtensiveHealthChecks)) {
        noNewStreams();
        continue;
      }

      return candidate;
    }
  }

这个方法的参数有必要说明一下

doExtensiveHealthChecks = true //是否做健康检查 get的话不做
connectTimeout = 10000 //链接超时时间
readTimeout = 10000 //读取超时时间
writeTimeout = 10000 //写入超时时间
connectionRetryEnabled = true //连接重试启用

这个方法首先就是进入了一个while循环,里面又调用了一个findConnection的方法去寻找RealConnection,又转到findConnection,这个方法比较长,慢慢来看

/**
   * Returns a connection to host a new stream. This prefers the existing connection if it exists,
   * then the pool, finally building a new connection.
   *
   * 返回一个连接以托管新流。最好是现有的连接,如果它存在,然后是池,最后建立一个新的连接。
   */
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
    Route selectedRoute;
    //二话不说先上锁,避免线程问题
    synchronized (connectionPool) {
      //  确保状态的正常
      if (released) throw new IllegalStateException("released");
      if (codec != null) throw new IllegalStateException("codec != null");
      if (canceled) throw new IOException("Canceled");

      //  Attempt to use an already-allocated connection.
      //  尝试使用已分配的连接。如果之前没有请求的话就是null,如果有的话就用 没有的话往下走
      RealConnection allocatedConnection = this.connection;
      if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
        return allocatedConnection;
      }

      // Attempt to get a connection from the pool.
      // 尝试从池中获取连接。
      Internal.instance.get(connectionPool, address, this, null);
      if (connection != null) {
        return connection;
      }

      selectedRoute = route;
    }

    // If we need a route, make one. This is a blocking operation.
    //如果我们需要一条路线,做一个路线。这是一个阻止操作。
    if (selectedRoute == null) {
      selectedRoute = routeSelector.next();
    }
    //真正的请求
    RealConnection result;
    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");

      // Now that we have an IP address, make another attempt at getting a connection from the pool.
      // This could match due to connection coalescing.
      //现在我们有一个IP地址,再尝试从池中获取连接。
      //这可能由于连接聚结而匹配。
      Internal.instance.get(connectionPool, address, this, selectedRoute);
      if (connection != null) return connection;

      // Create a connection and assign it to this allocation immediately. This makes it possible
      // for an asynchronous cancel() to interrupt the handshake we're about to do.
      //
      //创建连接并立即将其分配给此分配。这使得异步cancel()可以中断我们即将做的握手。
      //
      route = selectedRoute;
      refusedStreamCount = 0;
      //到这里发现没有,新建一个RealConnection
      result = new RealConnection(connectionPool, selectedRoute);
      //添加计数
      acquire(result);
    }

    // Do TCP + TLS handshakes. This is a blocking operation.
    //做TCP + TLS握手。 这是一个阻止操作。这一步进行的是sokcet的初始化和链接
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    //假如这个请求之前有失败过 ,从失败队列里移除
    routeDatabase().connected(result.route());

    Socket socket = null;
    synchronized (connectionPool) {
      // Pool the connection.
      // 加入连接池。
      Internal.instance.put(connectionPool, result);

      // If another multiplexed connection to the same address was created concurrently, then
      // release this connection and acquire that one.
      // 如果同时创建了与同一地址的多路复用连接,则释放此连接并获取该连接。
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);

    return result;
  }

这边寻找到一个连接后,讲这个链接赋值个一个RealConnection,然后去调用下一个链条的proceed方法,就是上面的

return realChain.proceed(request, streamAllocation, httpCodec, connection);

根据上面的集合 下一个链条是CallServerInterceptor,我们来进入CallServerInterceptor

CallServerInterceptor

这是链中的最后一个拦截器。 它对服务器进行网络呼叫

是不是感觉不想看了?头晕眼皮重想睡觉?

**稳住 我们能赢!! **

public final class CallServerInterceptor implements Interceptor {
  private final boolean forWebSocket;   
    
 //RealCall 初始化  是否是webSocket
  public CallServerInterceptor(boolean forWebSocket) {
    this.forWebSocket = forWebSocket;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    //同样的配方 同样的味道
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //在上个链条里初始化的HttpCodec  一般ed是HttpCodec1
    HttpCodec httpCodec = realChain.httpStream();
    //从上一个里面获取streamAllocation
    StreamAllocation streamAllocation = realChain.streamAllocation();
    //获取当前链接
    RealConnection connection = (RealConnection) realChain.connection();
    //获取请求
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();
    ///这应该更新HTTP引擎的sentRequestMillis字段
    httpCodec.writeRequestHeaders(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.
      //如果请求中有一个“Expect:100-continue”头,请等待“HTTP / 1.1 100 Continue”响应,
      // 然后再发送请求主体。如果我们没有这样做,
      // 请返回我们所获得的(例如4xx响应),而不会传输请求体。
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        //如果满足“Expect:100-continue”期望,请写请求正文
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } 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) {
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

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

    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;
  }
}

上面获取到一系列的变量之后,然后调用了

  httpCodec.writeRequestHeaders(request);

这个方法里面是这样的

 /** Returns bytes of a request header for sending on an HTTP transport. */
  /** 返回用于在HTTP传输上发送的请求标头的字节*/
  public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }

这个就是组装了一个头然后请求过去,然后接着判断当前请求的方法和body是否合法,然后往下走,判断一下如果请求中有一个“Expect:100-continue”头,等待“HTTP / 1.1 100 Continue”响应, 平常也没有 ,然后往下走,判断responseBuilder 如果为空的话,调用httpCodec的createRequestBody,它里面的经过重重调用之后,最后是调用的这个方法

private long writeOrCountBytes(BufferedSink sink, boolean countBytes) {
    long byteCount = 0L;

    Buffer buffer;
    if (countBytes) {
      buffer = new Buffer();
    } else {
      buffer = sink.buffer();
    }

    for (int i = 0, size = encodedNames.size(); i < size; i++) {
      if (i > 0) buffer.writeByte('&');
      buffer.writeUtf8(encodedNames.get(i));
      buffer.writeByte('=');
      buffer.writeUtf8(encodedValues.get(i));
    }

    if (countBytes) {
      byteCount = buffer.size();
      buffer.clear();
    }

    return byteCount;
  }

从CallServerInterceptor 调用过来的这个方法的countBytes是false, 用的是sink的buff 这个sink的buff现在就是socket的流操作 ,把请求提写入到socket里面,完成请求,具体的整个sokcet请求的流程我会在教程里讲,这里就不多叙述。

接着上面的说,请求完之后 responseBuilder 是通过httpcodec的readResponseHeaders来创建,里面有请求回来的请求码, 状态和头部,然后根据responseBuilder构架response,把当前的请求,握手,发送请求的时间,服务器返回的时间放到response里面,此处忽略101先,然后看到response又通过builder去放入返回的body,如果是20X的话就抛出异常,然后返回response。 此处又该往回走,在RealInterceptorChain 里面后半部分,处理了一下结尾,然后返回Response,然后RealCall里面的getResponseWithInterceptorChain()返回Response,然后在execute或者AsyncCall的execute里面返回Response,如果是AsuncCall的话返回到回调里,这个流程就完成了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 简介 目前在HTTP协议请求库中,OKHttp应当是非常火的,使用也非常的简单。网上有很多文章写了关于OkHttp...
    第八区阅读 1,368评论 1 5
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,144评论 1 23
  • 关于okhttp是一款优秀的网络请求框架,关于它的源码分析文章有很多,这里分享我在学习过程中读到的感觉比较好的文章...
    蕉下孤客阅读 3,588评论 2 38