美文网首页
OKhttp源码分析

OKhttp源码分析

作者: Yigit_dev | 来源:发表于2021-10-07 10:58 被阅读0次

    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返回出去。

    相关文章

      网友评论

          本文标题:OKhttp源码分析

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