4.OkHttp的請(qǐng)求攔截鏈

OkHttp請(qǐng)求的核心處理就是這一系列的攔截鏈

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    //建立一個(gè)完整的攔截器堆棧。
    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);
  }

這里面看到 ,第一個(gè)add的是client里面自己定義的攔截鏈集合和如果不是webSocket的話能添加客戶端網(wǎng)絡(luò)的攔截鏈,然后后面依次添加的攔截器有

RetryAndFollowUpInterceptor 重試和跟進(jìn)攔截器
BridgeInterceptor           橋攔截器
CacheInterceptor            緩存攔截器
ConnectInterceptor          鏈接攔截器
CallServerInterceptor       呼叫服務(wù)攔截器
RealInterceptorChain        實(shí)際攔截鏈,其中攜帶整個(gè)攔截器鏈:所有應(yīng)用攔截器,OkHttp核心,所有網(wǎng)絡(luò)攔截器,最后是網(wǎng)絡(luò)呼叫者。

這種鏈?zhǔn)秸{(diào)用在設(shè)計(jì)模式里有個(gè)叫責(zé)任鏈模式,這個(gè)的話我們進(jìn)去看下源碼印證一下,因?yàn)樗@個(gè)集合是一個(gè)帶有泛型的,所以直接看他的泛型

/**
 * 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.
 * 觀察,修改和潛在地短路請(qǐng)求以及相應(yīng)的響應(yīng)。通常,攔截器會(huì)在請(qǐng)求或響應(yīng)中添加,刪除或變換頭文件。
 */
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

然后再看RealInterceptorChain的代碼因?yàn)楹竺嬲{(diào)用的是他的proceed方法,可以知道他肯定是實(shí)現(xiàn)了Interceptor的Chain,我們先來看他的初始化的過程

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

直接看他的構(gòu)造函數(shù)

 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的, 傳過來了一個(gè)請(qǐng)求鏈,index是0,還有request 接著調(diào)用了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.
    //如果我們已經(jīng)有一個(gè)流,請(qǐng)確認(rèn)傳入的請(qǐng)求將使用它
    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().
    //如果我們已經(jīng)有一個(gè)流,確認(rèn)這是唯一的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.
    // 調(diào)用鏈中的下一個(gè)攔截器
    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().
    //  確認(rèn)下一個(gè)攔截器將其所需的調(diào)用鏈接到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;
  }

這里面可以看到最后調(diào)用的是proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection)

首先是幾個(gè)判斷,流程如下

  1. httpCodec不為空并且connection的url不支持這個(gè)請(qǐng)求的Url,那么拋出異常
  2. httpCodec不為空且當(dāng)前的請(qǐng)求數(shù)大于1,拋出異常
  3. 開吃調(diào)用攔截鏈的邏輯
  4. httpCodec不為空并且當(dāng)前索引加1小于請(qǐng)求鏈的長度,下一個(gè)請(qǐng)求的call不為1 ,拋出異常。
  5. response為空,拋出異常
  6. return response

這塊邏輯 會(huì)不停的去調(diào)用下一個(gè)

  // Call the next interceptor in the chain.
    // 調(diào)用鏈中的下一個(gè)攔截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

第一次運(yùn)行到這里的時(shí)候,調(diào)用的是RetryAndFollowUpInterceptor這個(gè)攔截器的intercept方法

RetryAndFollowUpInterceptor 重試和跟進(jìn)攔截器

之前有簡單介紹過這個(gè)攔截器,所以我們直接看他的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 一個(gè)StreamAllocation
  3. 然后再去調(diào)用當(dāng)前傳過來的chain的proceed方法
  4. 然后返回response
  5. 組裝response和一些善后處理

敏銳的我們看到了streamAllocation 按照字面的意思是 流分配 傳入的值是連接池,url,和一個(gè)callStackTrace

SteamAllocation上面的描述我們看下,直接中文版翻譯過來:

 *  該類協(xié)調(diào)三個(gè)實(shí)體之間的關(guān)系
 *
 * Connections:   到遠(yuǎn)程服務(wù)器的物理套接字連接。 這些建立可能很慢,所以有必要能夠取消當(dāng)前連接的連接。
 * Streams:       在連接上分層的邏輯HTTP請(qǐng)求/響應(yīng)對(duì)。 每個(gè)連接都有自己的分配限制,它定義了連接可以攜帶多少個(gè)并發(fā)流。
 *                 HTTP / 1.x連接一次可以攜帶1個(gè)流,HTTP / 2通常攜帶多個(gè)。
 * Calls:         流的邏輯順序,通常是初始請(qǐng)求及其后續(xù)請(qǐng)求。 我們更喜歡將單個(gè)Call的所有流保留在同一個(gè)連接上,以獲得更好的行為和位置。
 *
 * 該類的實(shí)例代表呼叫,在一個(gè)或多個(gè)連接上使用一個(gè)或多個(gè)流。 該類有API可以釋放以上每種資源:
 *          {@link #noNewStreams()} 將阻止連接被用于新的流。在{@code Connection: close}header之后或當(dāng)連接可能不一致時(shí)使用此選項(xiàng)。
 *          {@link #streamFinished streamFinished()}從此分配中釋放活動(dòng)流。
 *                                  請(qǐng)注意,在給定時(shí)間只有一個(gè)流可能處于活動(dòng)狀態(tài),因此在使用{@link #newStream newStream()}
 *                                  創(chuàng)建后續(xù)流之前,有必要調(diào)用{@link #streamFinished streamFinished()}。
 *         {@link #release()}刪除連接上的呼叫保持。 請(qǐng)注意,這不會(huì)立即釋放連接,如果有一個(gè)流仍然揮之不去。
 *                              這種情況發(fā)生在通話完成但其響應(yīng)體尚未完全消耗的情況下。
 *
 * 此類支持{@linkplain # asynchronous canceling}。
 *                            這是為了具有最小的爆炸半徑。
 *                            如果HTTP / 2流處于活動(dòng)狀態(tài),則取消將取消該流,但不會(huì)將其他流分享其連接。
 *                            但是如果TLS握手仍在進(jìn)行中,則取消可能會(huì)破壞整個(gè)連接。

這個(gè)我們大致就知道了這個(gè)類是來干嘛的了,在RetryAndFollowUpInterceptor里面他只進(jìn)行了 realease和streamFailed操作 ,推測(cè)他的調(diào)用應(yīng)該在下一個(gè)攔截鏈里面,我們根據(jù)上面的攔截器集合知道 下一個(gè)調(diào)用的是BridgeInterceptor攔截器

BridgeInterceptor

這個(gè)攔截器主要的職責(zé)是

從應(yīng)用程序代碼到網(wǎng)絡(luò)代碼的橋梁。 首先,它根據(jù)用戶請(qǐng)求構(gòu)建網(wǎng)絡(luò)請(qǐng)求。 然后它繼續(xù)呼叫網(wǎng)絡(luò)。 最后,它從網(wǎng)絡(luò)響應(yīng)中構(gòu)建用戶響應(yīng)。 主要構(gòu)件的是網(wǎng)絡(luò)請(qǐng)求的header的部分和組裝返回來的body

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

這個(gè)類不大,直接放上來講

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

  //放進(jìn)去構(gòu)建的時(shí)候的CookieJar
  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    //老套路,拿到chain的request
    Request userRequest = chain.request();
    //利用request的Builder新建一個(gè)request
    Request.Builder requestBuilder = userRequest.newBuilder();
    //初始化requestBody
    RequestBody body = userRequest.body();
    
    //獲取contentType 因?yàn)槭窃赥estMain里面設(shè)置的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());
      }
    
      //設(shè)置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");
      }
    }
    //設(shè)置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.
    // 如果我們添加了一個(gè)“Accept-Encoding:gzip”頭域,我們也負(fù)責(zé)解壓縮傳輸流。
    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));
    }
    //頭部加入U(xiǎn)A頭
    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }

    /**
    *----------------------------------------------調(diào)用下一個(gè)鏈條的proceed方法  責(zé)任鏈模式---------------------------------
    **/
    Response networkResponse = chain.proceed(requestBuilder.build());
            
    //后面的是請(qǐng)求回來的
    
    //保存cookiejar
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    
    //構(gòu)建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();
  }
}

這里截止到調(diào)用下個(gè)攔截鏈的時(shí)候,做了以下幾件事情:

  1. 構(gòu)建請(qǐng)求體
  2. 設(shè)置content-type和content-length
  3. 設(shè)置Host
  4. 設(shè)置Connection
  5. 處理gzip
  6. 設(shè)置cookie
  7. 設(shè)置User-Agent
  8. 調(diào)用下一個(gè)攔截鏈

按照上面的順序,下一個(gè)攔截鏈?zhǔn)荂acheInterceptor,CacheInterceptor是個(gè)比較復(fù)雜的攔截鏈,如果我們沒設(shè)置的話里面的判斷都是不進(jìn)去的,然后調(diào)用下一個(gè)chain,下一個(gè)是ConnectInterceptor

ConnectInterceptor

這個(gè)攔截器做的事情看起來簡單,實(shí)際上是比較復(fù)雜的,他的主要職責(zé)是:

打開與目標(biāo)服務(wù)器的連接,并進(jìn)入下一個(gè)攔截器

代碼量很少

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請(qǐng)求 不做健康檢查
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    RealConnection connection = streamAllocation.connection();

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

這里重要的就是streamAllocation這個(gè)類,這邊調(diào)用了他的newStream()方法和connection()方法 ,我們點(diǎn)進(jìn)去分析這兩個(gè)方法

//在retryAndFollowUp里面已經(jīng)初始化過 我們這邊就直接看他的方法里面

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

    //從連接池里尋找一個(gè)健康的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);
    }
  }

這里先初始化了幾個(gè)時(shí)間 ,然后去find了一個(gè)健康的connection 返回RealConnection

我們看這個(gè)小蝌蚪找媽媽的方法

/**
   * Finds a connection and returns it if it is healthy. If it is unhealthy the process is repeated until a healthy connection is found.
   *
   * 找到一個(gè)健康的連接并返回它。 如果不健康,則重復(fù)該過程直到發(fā)現(xiàn)健康的連接。
   */
  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.
      //如果這是一個(gè)全新的聯(lián)系,我們可以跳過廣泛的健康檢查。
      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.
      //做一個(gè)(潛在的慢)檢查,以確認(rèn)匯集的連接是否仍然良好。 如果不是,請(qǐng)將其從池中取出并重新開始。
      if (!candidate.isHealthy(doExtensiveHealthChecks)) {
        noNewStreams();
        continue;
      }

      return candidate;
    }
  }

這個(gè)方法的參數(shù)有必要說明一下

doExtensiveHealthChecks = true //是否做健康檢查 get的話不做
connectTimeout = 10000 //鏈接超時(shí)時(shí)間
readTimeout = 10000 //讀取超時(shí)時(shí)間
writeTimeout = 10000 //寫入超時(shí)時(shí)間
connectionRetryEnabled = true //連接重試啟用

這個(gè)方法首先就是進(jìn)入了一個(gè)while循環(huán),里面又調(diào)用了一個(gè)findConnection的方法去尋找RealConnection,又轉(zhuǎn)到findConnection,這個(gè)方法比較長,慢慢來看

/**
   * Returns a connection to host a new stream. This prefers the existing connection if it exists,
   * then the pool, finally building a new connection.
   *
   * 返回一個(gè)連接以托管新流。最好是現(xiàn)有的連接,如果它存在,然后是池,最后建立一個(gè)新的連接。
   */
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
    Route selectedRoute;
    //二話不說先上鎖,避免線程問題
    synchronized (connectionPool) {
      //  確保狀態(tài)的正常
      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.
      //  嘗試使用已分配的連接。如果之前沒有請(qǐng)求的話就是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.
    //如果我們需要一條路線,做一個(gè)路線。這是一個(gè)阻止操作。
    if (selectedRoute == null) {
      selectedRoute = routeSelector.next();
    }
    //真正的請(qǐng)求
    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.
      //現(xiàn)在我們有一個(gè)IP地址,再嘗試從池中獲取連接。
      //這可能由于連接聚結(jié)而匹配。
      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.
      //
      //創(chuàng)建連接并立即將其分配給此分配。這使得異步cancel()可以中斷我們即將做的握手。
      //
      route = selectedRoute;
      refusedStreamCount = 0;
      //到這里發(fā)現(xiàn)沒有,新建一個(gè)RealConnection
      result = new RealConnection(connectionPool, selectedRoute);
      //添加計(jì)數(shù)
      acquire(result);
    }

    // Do TCP + TLS handshakes. This is a blocking operation.
    //做TCP + TLS握手。 這是一個(gè)阻止操作。這一步進(jìn)行的是sokcet的初始化和鏈接
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    //假如這個(gè)請(qǐng)求之前有失敗過 ,從失敗隊(duì)列里移除
    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.
      // 如果同時(shí)創(chuàng)建了與同一地址的多路復(fù)用連接,則釋放此連接并獲取該連接。
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);

    return result;
  }

這邊尋找到一個(gè)連接后,講這個(gè)鏈接賦值個(gè)一個(gè)RealConnection,然后去調(diào)用下一個(gè)鏈條的proceed方法,就是上面的

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

根據(jù)上面的集合 下一個(gè)鏈條是CallServerInterceptor,我們來進(jìn)入CallServerInterceptor

CallServerInterceptor

這是鏈中的最后一個(gè)攔截器。 它對(duì)服務(wù)器進(jìn)行網(wǎng)絡(luò)呼叫

是不是感覺不想看了?頭暈眼皮重想睡覺?

**穩(wěn)住 我們能贏!! **

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;
    //在上個(gè)鏈條里初始化的HttpCodec  一般ed是HttpCodec1
    HttpCodec httpCodec = realChain.httpStream();
    //從上一個(gè)里面獲取streamAllocation
    StreamAllocation streamAllocation = realChain.streamAllocation();
    //獲取當(dāng)前鏈接
    RealConnection connection = (RealConnection) realChain.connection();
    //獲取請(qǐng)求
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();
    ///這應(yīng)該更新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.
      //如果請(qǐng)求中有一個(gè)“Expect:100-continue”頭,請(qǐng)等待“HTTP / 1.1 100 Continue”響應(yīng),
      // 然后再發(fā)送請(qǐng)求主體。如果我們沒有這樣做,
      // 請(qǐng)返回我們所獲得的(例如4xx響應(yīng)),而不會(huì)傳輸請(qǐng)求體。
      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”期望,請(qǐng)寫請(qǐng)求正文
        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.
      //連接正在升級(jí),但是我們需要確保攔截器看到一個(gè)非空響應(yīng)體
      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;
  }
}

上面獲取到一系列的變量之后,然后調(diào)用了

  httpCodec.writeRequestHeaders(request);

這個(gè)方法里面是這樣的

 /** Returns bytes of a request header for sending on an HTTP transport. */
  /** 返回用于在HTTP傳輸上發(fā)送的請(qǐng)求標(biāo)頭的字節(jié)*/
  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;
  }

這個(gè)就是組裝了一個(gè)頭然后請(qǐng)求過去,然后接著判斷當(dāng)前請(qǐng)求的方法和body是否合法,然后往下走,判斷一下如果請(qǐng)求中有一個(gè)“Expect:100-continue”頭,等待“HTTP / 1.1 100 Continue”響應(yīng), 平常也沒有 ,然后往下走,判斷responseBuilder 如果為空的話,調(diào)用httpCodec的createRequestBody,它里面的經(jīng)過重重調(diào)用之后,最后是調(diào)用的這個(gè)方法

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 調(diào)用過來的這個(gè)方法的countBytes是false, 用的是sink的buff 這個(gè)sink的buff現(xiàn)在就是socket的流操作 ,把請(qǐng)求提寫入到socket里面,完成請(qǐng)求,具體的整個(gè)sokcet請(qǐng)求的流程我會(huì)在教程里講,這里就不多敘述。

接著上面的說,請(qǐng)求完之后 responseBuilder 是通過httpcodec的readResponseHeaders來創(chuàng)建,里面有請(qǐng)求回來的請(qǐng)求碼, 狀態(tài)和頭部,然后根據(jù)responseBuilder構(gòu)架response,把當(dāng)前的請(qǐng)求,握手,發(fā)送請(qǐng)求的時(shí)間,服務(wù)器返回的時(shí)間放到response里面,此處忽略101先,然后看到response又通過builder去放入返回的body,如果是20X的話就拋出異常,然后返回response。 此處又該往回走,在RealInterceptorChain 里面后半部分,處理了一下結(jié)尾,然后返回Response,然后RealCall里面的getResponseWithInterceptorChain()返回Response,然后在execute或者AsyncCall的execute里面返回Response,如果是AsuncCall的話返回到回調(diào)里,這個(gè)流程就完成了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 簡介 目前在HTTP協(xié)議請(qǐng)求庫中,OKHttp應(yīng)當(dāng)是非?;鸬模褂靡卜浅5暮唵?。網(wǎng)上有很多文章寫了關(guān)于OkHttp...
    第八區(qū)閱讀 1,396評(píng)論 1 5
  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中。。。 其實(shí)如果沒有APIManager這一層是沒法使用delegate的,畢竟多個(gè)單...
    yhtang閱讀 5,251評(píng)論 1 23
  • 關(guān)于okhttp是一款優(yōu)秀的網(wǎng)絡(luò)請(qǐng)求框架,關(guān)于它的源碼分析文章有很多,這里分享我在學(xué)習(xí)過程中讀到的感覺比較好的文章...
    蕉下孤客閱讀 3,616評(píng)論 2 38