OKhttp源码分析

OkHttp是一个高效的HTTP客户端,它有以下默认特性:

  • 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
  • 连接池减少请求延时
  • 透明的GZIP压缩减少响应数据的大小
  • 缓存响应内容,避免一些完全重复的请求

OKhttp的一般使用方法如下:

//异步执行
    private void asyncGetRequests() {
        String url = "https://www.alibaba.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .get()//默认就是GET请求
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() { //执行异步请求
            @Override
            public void onFailure(Call call, IOException e) {
                do.....
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                    do.....
            }
        });
    }

//同步执行
    private void syncGetRequests() {
        String url = "https://wwww.alibaba.com";
        OkHttpClient okHttpClient = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .get()//默认就是GET请求
                .build();
        final Call call = okHttpClient.newCall(request);
        new Thread(() -> {
            try {
                Response response = call.execute(); //执行同步请求
                String result = response.body().string();
                runOnUiThread(() -> tvContent.setText("synGetRequests run: " + result));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

主要步骤

  1. 创建OkHttpClient和Request实例
  2. 通过OkHttpClient和Request的实例创建Call对象
  3. 通过调用call对象的enqueue或execute方法执行异步或同步调用

整体流程

下图展示了同步/异步请求的整体流程


image.png

类说明

  • OkHttpClient:主要配置了网络请求的很多参数,最好是单例的形式存在,因为里面可以复用线程池和连接池。
  • Call:一个接口类,具体去执行一个请求,只能被执行一次,它由RealCall实现。
  • RealCall:定义了一个请求执行的具体过程,包括同步/异步,并且定义了拦截器的排列顺序。
  • AsyncCall:是RealCall的内部类,它集成自NamedRunnable实现了Runnable,所以它是在子线程运行,里面具体处理了异步请求网络执行过程。
  • Transmitter:意为发射器,是应用层和网络层的桥梁,在进行连接、真正发出请求和读取响应中起到很重要的作用,它里面保存了OkHttpClient、连接池、call、事件监听器、call超时时间等信息。
  • Dispatcher:调度器,主要是异步请求的并发控制、把异步请求放入线程池执行,实现方法是promoteAndExecute()。 promoteAndExecute()有两处调用:添加异步请求时、同步/异步请求 结束时,里面使用了几个数组维护当前包含了那些Call,包括readyAsyncCalls、runningAsyncCalls(最大值为64,这里限制了线程池产生线程的个数)和runningSyncCalls。
  • RealInterceptorChain:实现自Interceptor.Chain,它是一个拦截器链,它整合了所有拦截器,它具体实现了proceed方法,在这个方法里面依次获取了所有拦截器并执行了拦截器的intercept方法,并传入了下一个拦截器的索引。

流程中主要通过getResponseWithInterceptorChain方法获取Response返回结果,并且在这个方法里面整合了所有拦截器,我们具体看看整个过程。

拦截器组装过程

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());//OkHttpClient.Build#addInterceptor添加自定义拦截器
    interceptors.add(new RetryAndFollowUpInterceptor(client));//重试和重定向拦截器
    interceptors.add(new BridgeInterceptor(client.cookieJar()));//应用层和网络层的桥接拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));//缓存处理相关的拦截器
    interceptors.add(new ConnectInterceptor(client));//连接服务的拦截器,真正的网络请求在这里实现
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());//OkHttpClient.Build#addNetworkInterceptor添加自定义网络网络拦截器
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));//负责写请求和读响应的拦截器,Okio主场
    //拦截器链,这里index为0,所以默认是先执行interceptors中的第一个拦截器
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      //拦截器链 开始执行第一个index = 0 的拦截器
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

通过上面的代码我们可以知道,我们是通过一下顺序加入的拦截器:

  1. 通过addInterceptor 方式加入的自定义拦截器
  2. RetryAndFollowUpInterceptor 重试和重定向拦截器
  3. BridgeInterceptor 桥拦截器
  4. CacheInterceptor 缓存拦截器
  5. ConnectInterceptor 连接拦截器
  6. 通过addNetworkInterceptor 方式加入的自定义拦截器
  7. CallServerInterceptor 请求服务拦截器

所有拦截器都实现Interceptor接口

Interceptor {

  //把拦截器链传递下去,实现请求的处理过程,返回response
  Response intercept(Chain chain) throws IOException;

  //拦截器链,负责把所有拦截器串联起来
  interface Chain {
    Request request();

    //Chain的核心方法,进行拦截器的层次调用
    Response proceed(Request request) throws IOException;

    //返回请求执行的 连接. 仅网络拦截器可用; 应用拦截器就是null.
    @Nullable Connection connection();

    Call call();
    int connectTimeoutMillis();
    Chain withConnectTimeout(int timeout, TimeUnit unit);
    int readTimeoutMillis();
    Chain withReadTimeout(int timeout, TimeUnit unit);
    int writeTimeoutMillis();
    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

Interceptor是个接口类,只有一个intercept方法,参数是Chain对象。内部接口类Chain拦截器链,有个proceed方法,参数是Request对象,返回值是Response,这个方法的实现就是请求的处理过程了。

  public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
   //省略部分代码。。。。
    // Call the next interceptor in the chain.
    //获取下一个拦截器的RealInterceptorChain对象,并执行当前拦截器的intercept方法,并将chain作为参数传入
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
  //省略部分代码。。。。
    return response;
  }

综上所述,拦截器通过chain的执行过程类似于洋葱,从最外层,层层传入,并重最内层,层层传出。所有拦截器都可以处理当前获取的request,并将结果传入内层,也可以处理当前获取的response,并将结果传入最外层,除了最后一个拦截器,重这里我们也可以看出通过addInterceptor和addNetworkInterceptor的区别,前者更外层所以它只能处理最开始的request和最后的response,整个请求过程调用一次,而后者更内层,所以它可以被调用多次,比如重试或者重定向后都会调用它,并且它可以拿到很多connection和net相关的数据。


image.png
image.png

下面我们就依次分析每个拦截器。

重试/重定向拦截器

RetryAndFollowUpInterceptor,意为“重试和重定向拦截器”,作用是连接失败后进行重试、对请求结果跟进后进行重定向,我们先看看它整个执行过程,如下图:


image.png
@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Transmitter transmitter = realChain.transmitter();

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      // 准备连接请求
      // 主机、端口、协议都相同时,连接可复用
      transmitter.prepareToConnect(request);

      if (transmitter.isCanceled()) {
        throw new IOException("Canceled");
      }

      Response response;
      boolean success = false;
      try {
        // 执行其他(下一个->下一个->...)拦截器的功能,获取Response;
        response = realChain.proceed(request, transmitter, null);
        success = true;
      } catch (RouteException e) {
        // 连接路由异常,此时请求还未发送。尝试恢复
        // 返回true,continue,继续下一个while循环
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), transmitter, false, request)) {
          throw e.getFirstConnectException();
        }
        continue;
      } catch (IOException e) {
        // IO异常,请求可能已经发出。尝试恢复
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, transmitter, requestSendStarted, request)) throw e;
        continue;
      } finally {
        // 请求没成功,释放资源
        // The network call threw an exception. Release any resources.
        if (!success) {
          transmitter.exchangeDoneDueToException();
        }
      }

      // 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();
      }

      Exchange exchange = Internal.instance.exchange(response);
      Route route = exchange != null ? exchange.connection().route() : null;
      // 后面的拦截器执行完了,拿到Response
      // 解析看下是否需要重试或重定向,需要则返回新的Request
      Request followUp = followUpRequest(response, route);

      if (followUp == null) {
        if (exchange != null && exchange.isDuplex()) {
          transmitter.timeoutEarlyExit();
        }
        // 如果followUpRequest返回的Request为空,那边就表示不需要执行重试或者重定向,直接返回数据
        return response;
      }

      RequestBody followUpBody = followUp.body();
      if (followUpBody != null && followUpBody.isOneShot()) {
        // 如果followUp不为null,请求体不为空,但只需要请求一次时,那么就返回response;
        return response;
      }

      closeQuietly(response.body());
      if (transmitter.hasExchange()) {
        exchange.detachWithViolence();
      }

      // 判断重试或者重定向的次数是否超过最大的次数20,是的话则抛出异常
      if (++followUpCount > MAX_FOLLOW_UPS) {
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
      // 将需要重试或者重定向的请求赋值给新的请求
      // 继续执行新请求
      request = followUp;
      priorResponse = response;
    }//...while (true)
  }

总结

  1. 通过realChain.proceed发起请求获取response
  2. 如果发生异常,则执行recover方法判断是否可以重试
  3. 如果请求成功,则通过followUpRequest判断response是否需要重定向
  4. 如果不需要重定向,则请求成功返回

桥拦截器

BridgeInterceptor ,意为 桥拦截器,相当于 在 请求发起端 和 网络执行端 架起一座桥,把应用层发出的请求 变为 网络层认识的请求,把网络层执行后的响应 变为 应用层便于应用层使用的结果。


image.png
public final class BridgeInterceptor implements Interceptor {

  //Cookie管理器,初始化OkhttpClient时创建的
  //默认是CookieJar.NO_COOKIES
  private final CookieJar cookieJar;

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

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

    RequestBody body = userRequest.body();
    // 处理封装"Content-Type", "Content-Length","Transfer-Encoding","Host","Connection",
    // "Accept-Encoding","Cookie","User-Agent"等请求头
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

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

    if (userRequest.header("Host") == null) {
      requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }
    // 在服务器支持gzip压缩的前提下,客户端不设置Accept-Encoding=gzip的话,
    // okhttp会自动帮我们开启gzip和解压数据,如果客户端自己开启了gzip,就需要自己解压服务器返回的数据了。
    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    //从cookieJar中获取cookie,添加到header
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }
    // 把处理好的新请求往下传递,执行后续的拦截器的逻辑
    Response networkResponse = chain.proceed(requestBuilder.build());
    //从networkResponse中获取 header "Set-Cookie" 存入cookieJar
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    // 获取返回体的Builder
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    // 处理返回的Response的"Content-Encoding"、"Content-Length"、"Content-Type"等返回头
    // 如果我们没有手动添加"Accept-Encoding: gzip",这里会创建 能自动解压的responseBody--GzipSource
    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);
      String contentType = networkResponse.header("Content-Type");
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }
    //然后新构建的response返回出去
    return responseBuilder.build();
  }
}

总结

主要就是添加了一些header、如果有cookie就添加、如果需要gzip就做压缩和解压缩。

缓存拦截器

CacheInterceptor,缓存拦截器,提供网络请求缓存的存取。合理使用本地缓存,有效地减少网络开销、减少响应延迟。


image.png
public final class CacheInterceptor implements Interceptor {
  final @Nullable InternalCache cache;

  public CacheInterceptor(@Nullable InternalCache cache) {
    this.cache = cache;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    // 先获取候选缓存,前提是有配置缓存,也就是cache不为空;
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
    // 执行获取缓存策略的逻辑
    // 缓存策略决定是否使用缓存:
    // strategy.networkRequest为null,不使用网络
    // strategy.cacheResponse为null,不使用缓存。
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    // 网络请求
    Request networkRequest = strategy.networkRequest;
    // 本地的缓存保存的请求
    Response cacheResponse = strategy.cacheResponse;

    //根据缓存策略更新统计指标:请求次数、网络请求次数、使用缓存次数
    if (cache != null) {
      cache.trackResponse(strategy);
    }

    //有缓存 但不能用,关掉
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }
    // networkRequest == null 不能用网络
    // 如果不使用网络数据且缓存数据为空,那么返回一个504的Response,并且body为空
    // If we're forbidden from using the network and the cache is insufficient, fail.
    if (networkRequest == null && cacheResponse == null) {
      return new Response.Builder()
          .request(chain.request())
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(Util.EMPTY_RESPONSE)
          .sentRequestAtMillis(-1L)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();
    }

    // 如果不需要使用网络数据,那么就直接返回缓存的数据
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    /*
     * 到这里,networkRequest != null (cacheResponse可能null,可能!null)
     * 没有命中强缓存的情况下,进行网络请求,获取response
     * 先判断是否是协商缓存(304)命中,命中则更新缓存返回response
     * 未命中使用网络请求的response返回并添加缓存
     */
    Response networkResponse = null;
    try {
      // 执行后续的拦截器逻辑
      networkResponse = chain.proceed(networkRequest);
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      // 如果缓存数据不为空并且code为304,表示数据没有变化,继续使用缓存数据;
      if (networkResponse.code() == HTTP_NOT_MODIFIED) {
        Response response = cacheResponse.newBuilder()
            .headers(combine(cacheResponse.headers(), networkResponse.headers()))
            .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
            .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
            .cacheResponse(stripBody(cacheResponse))
            .networkResponse(stripBody(networkResponse))
            .build();
        networkResponse.body().close();

        // Update the cache after combining headers but before stripping the
        // Content-Encoding header (as performed by initContentStream()).
        cache.trackConditionalCacheHit();
        // 更新缓存数据
        cache.update(cacheResponse, response);
        return response;
      } else {
        //如果是非304,说明服务端资源有更新,就关闭缓存body
        closeQuietly(cacheResponse.body());
      }
    }

    // 协商缓存也未命中,获取网络返回的response
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

    if (cache != null) {
      //网络响应可缓存(请求和响应的 头 Cache-Control都不是'no-store')
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // 将网络数据保存到缓存中
        // InternalCache接口,实现在Cache类中
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

      //OkHttp默认只会对get请求进行缓存
      //不是get请求就移除缓存
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          // 缓存失效,那么就移除缓存
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }
    return response;
  }
  ...
}

总结

  • 通过缓存策略决定是使用缓存数据还是网络数据
  • 默认只缓存GET请求
  • 缓存保存在用户指定的目录,内部通过DiskLruCache实现,使用URL的md5做为key

连接拦截器

ConnectInterceptor,连接拦截器,主要负责连接的创建和复用。

image.png
@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    Transmitter transmitter = realChain.transmitter();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");

    // 实现了复杂的网络请求,Exchange用于下一个拦截器CallServerInterceptor进行网络请求使用;
    /*
        1:调用transmitter.newExchange方法;
        2:通过Transmitter的ExchangeFinder调用了find方法;
        3:ExchangeFinder里调用了findHealthyConnection方法;
        4:ExchangeFinder里findHealthyConnection调用了findConnection方法创建了RealConnection;
        5:调用了RealConnection的connect方法,实现了TCP + TLS 握手,底层通过socket来实现的;
        6: 通过RealConnection的newCodec方法创建了两个协议类,一个是Http1ExchangeCodec,对应着HTTP1.1,
        一个是Http2ExchangeCodec,对应着HTTP2.0;
    */
    //创建一个交换器Exchange
    Exchange exchange = transmitter.newExchange(chain, doExtensiveHealthChecks);
    // 执行后续的拦截器逻辑,注意是三个参数
    return realChain.proceed(request, transmitter, exchange);
  }

类说明

  • Transmitter:意为发射器,是把请求从应用端发射到网络层,它持有请求的连接、请求、响应和流,一个请求对应一个Transmitter实例,一个数据流。
  • Exchange:request、responseIO管理,对ExchangeCodec的包装,增加了事件回调,一个请求对应一个Exchange实例。
  • ExchangeCodec:接口类,负责真正的IO写请求、读响应,实现类有Http1ExchangeCodec、Http2ExchangeCodec,对应HTTP1.1、HTTP2.0。
  • ExchangeFinder:从连接池中寻找可用TCP连接,然后通过连接得到ExchangeCodec。
  • Route:一个具体连接服务器的路由,包括address(IP地址通过DNS可能获取多个)和proxy(通过OKhttpclient配置没有则使用默认的)
  • RouteSelector:配置代理服务器以及通过DNS获取IP地址resetNextInetSocketAddress。
  • ConnectionPool:连接池,用于管理http1.1/http2.0连接重用,以减少网络延迟。
    相同Address的http请求可以共享一个连接,ConnectionPool就是实现了连接的复用。
    1.默认最多保存5个空闲连接,并且空闲5分钟后移除。
    2.通过put方法给连接池添加连接,首先通过cleanupRunning线程移除空闲并过期的连
    接,这个线程在整个线程池里面只会存在一个。
    3.通过transmitterAcquirePooledConnection取一个可以复用的连接。
  • RealConnection:一个具体的连接,通过connect闯将一个连接通道,供后续IO操作使用。

总结

  1. 主要是获取一个Exchange对象并通过realChain.proceed方法传递给后续拦截器做具体的IO操作使用。
  2. 通过ExchangeFinder去获取一个ExchangeCodec,即具体操作request和response的协议。
  3. 通过RealConnectionPool连接池去复用连接,连接池复用逻辑如下图所示:
image.png

请求服务连接器

CallServerInterceptor,请求服务拦截器,也就是真正地去进行网络IO读写了——写入http请求的header和body数据、读取响应的header和body。


image.png
public final class CallServerInterceptor implements Interceptor {
  private final boolean forWebSocket;

  public CallServerInterceptor(boolean forWebSocket) {
    this.forWebSocket = forWebSocket;
  }

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    //ConnectInterceptor拦截器传入的exchange
    Exchange exchange = realChain.exchange();
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();

    // 将请求头写入到socket中,底层通过ExchangeCodec协议类
    // (对应Http1ExchangeCodec和Http2ExchangeCodec)
    // 最终是通过Okio来实现的,具体实现在RealBufferedSink这个类里面
    exchange.writeRequestHeaders(request);

    // 如果有body的话,通过Okio将body写入到socket中,用于发送给服务器
    boolean responseHeadersStarted = false;
    Response.Builder responseBuilder = null;
    //含body的请求
    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"的响应,然后再发送请求body.
      // 如果没有收到这个响应(例如收到的响应是4xx),那就不发送body了。
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        exchange.flushRequest();
        responseHeadersStarted = true;
        exchange.responseHeadersStart();
        //读取响应头
        responseBuilder = exchange.readResponseHeaders(true);
      }
      // responseBuilder为null说明服务端返回了100,也就是可以继续发送body了
      // 底层通过ExchangeCodec协议类(对应Http1ExchangeCodec和Http2ExchangeCodec)来读取返回头header的数据
      if (responseBuilder == null) {
        if (request.body().isDuplex()) {//默认是false不会进入
          // Prepare a duplex body so that the application can send a request body later.
          exchange.flushRequest();
          BufferedSink bufferedRequestBody = Okio.buffer(
              exchange.createRequestBody(request, true));
          request.body().writeTo(bufferedRequestBody);
        } else {
          // Write the request body if the "Expect: 100-continue" expectation was met.
          // 满足了 "Expect: 100-continue" ,写请求body
          BufferedSink bufferedRequestBody = Okio.buffer(
              exchange.createRequestBody(request, false));
          request.body().writeTo(bufferedRequestBody);
          bufferedRequestBody.close();
        }
      } else {
        //没有满足 "Expect: 100-continue" ,请求发送结束
        exchange.noRequestBody();
        if (!exchange.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.
          exchange.noNewExchangesOnConnection();
        }
      }
    } else {
      //没有body,请求发送结束
      exchange.noRequestBody();
    }

    //请求发送结束
    if (request.body() == null || !request.body().isDuplex()) {
      //真正将写到socket输出流的http请求数据发送。
      exchange.finishRequest();
    }
    //回调 读响应头开始事件(如果上面没有)
    if (!responseHeadersStarted) {
      exchange.responseHeadersStart();
    }
    //读响应头(如果上面没有)
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(false);
    }

    // 创建返回体Response
    Response response = responseBuilder
        .request(request)
        .handshake(exchange.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();

    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
      // 服务端又返回了个100,就再尝试获取真正的响应
      response = exchange.readResponseHeaders(false)
          .request(request)
          .handshake(exchange.connection().handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build();

      code = response.code();
    }
    //回调读响应头结束
    exchange.responseHeadersEnd(response);
    //这里就是获取响应body了
    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的数据
      // 底层通过ExchangeCodec协议类(对应Http1ExchangeCodec和Http2ExchangeCodec)
      response = response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build();
    }
    //请求头中Connection是close,表示请求完成后要关闭连接
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
      exchange.noNewExchangesOnConnection();
    }
    //204(无内容)、205(重置内容),body应该是空
    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:对外提供了writeXX、openXX、flushXX等方法通过ExchangeCodec操作request和response的传递。
  • ExchangeCodec:它有两个实现类Http1ExchangeCodec(HTTP/1.1)和Http2ExchangeCodec(HTTP/2),里面根据不同的协议规范制定了具体的格式和IO过程。
  • Okio:具体执行IO操作的框架

总结

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

推荐阅读更多精彩内容