OkHttp学习(六) 3.10.0版本源码中的拦截器

系列文章

OkHttp学习(一) OSI七层模型和TCP四层模型
OkHttp学习(二) 3.10.0版本的简单使用及其流程梳理
OkHttp学习(三) 3.10.0版本源码阅读之线程池
OkHttp学习(四) 3.10.0版本源码阅读之构建者设计模式
OkHttp学习(五) 3.10.0版本源码阅读之责任链设计模式
OkHttp学习(六) 3.10.0版本源码中的拦截器
OkHttp学习(七) 手写OkHttp之框架搭建
OkHttp学习(八) 手写OkHttp之网络请求实现
OkHttp学习(九) OkHttp的连接池手写实现

整体架构

在这里插入图片描述

上篇文章我们已经分析了各个拦截器是如何串成一条链的,下面就分析链的每个节点都做了什么事情。

RetryAndFollowUpInterceptor

重试与重定向拦截器

public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Call call = realChain.call();
    EventListener eventListener = realChain.eventListener();

	// 创建资源分配器
    StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
        createAddress(request.url()), call, eventListener, callStackTrace);
    this.streamAllocation = streamAllocation;

    int followUpCount = 0;
    Response priorResponse = null;
	// 循环重试,如果成功直接跳出循环,如果失败,要么重试要么抛出异常,重试最大次数为20
    while (true) {
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response;
      boolean releaseConnection = true;
      try {
        // 从下个责任链节点中取的响应,传入了资源分配器
        response = realChain.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(), streamAllocation, 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, streamAllocation, 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.
      // 之前执行得到的priorResponse ,如果不为空,则会根据之前的priorResponse 重新创建response
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

	  // 根据response判断是否需要重定向,重定向的reques不为空t会放到下次重试中
      Request followUp = followUpRequest(response, streamAllocation.route());

	  // 重定向request为空,直接返回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()), call, eventListener, callStackTrace);
        this.streamAllocation = streamAllocation;
      } 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.创建了StreamAllocation对象,该对象用于分配资源,后面会用到
2.调用下一个拦截器,得到response
3.根据异常结果或者响应结果判断是否要进行重试。
4.重定向的次数根据++followUpCount > MAX_FOLLOW_UPS判断不超过20次
4.对response进行处理,返回给上一个拦截器。

  • 核心功能就是 连接失败重试(Retry)

    • 在发生 RouteException 或者 IOException后,会捕获建联或者读取的一些异常,根据一定的策略判断是否是可恢复的,如果可恢复会重新创建 StreamAllocation开始新的一轮请求
  • 继续发起请求(Follow up)

    • 主要有这几种类型
      • 3xx 重定向
      • 401,407 未授权,调用 Authenticator进行授权后继续发起新的请求
      • 408 客户端请求超时,如果 Request 的请求体没有被UnrepeatableRequestBody 标记,会继续发起新的请求

BridgeInterceptor

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

    RequestBody body = userRequest.body();
    // 设置Content-Type
    if (body != null) {
      MediaType contentType = body.contentType();
      if (contentType != null) {
        requestBuilder.header("Content-Type", contentType.toString());
      }

	 // Content-Length和Transfer-Encoding互斥
      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));
    }

	// 设置Connection头
    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.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

	// 创建Okhpptclitent时候配置的cookieJar,
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      // 解析成http协议的cookie格式
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }

    // 从下个链节点中取的response
    Response networkResponse = chain.proceed(requestBuilder.build());

	// 响应header, 如果没有自定义配置cookieJar==null,则什么都不做
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

	// copy一份从下个节点取的的response
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);

	// 判断服务器是否支持gzip压缩格式,如果支持则交给okio压缩
    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");
      // 处理完成后,重新生成一个response
      responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
    }

	// 将响应的response返回给上个拦截器
    return responseBuilder.build();
  }

BridgeInterceptor负责在request阶段对请求头添加一些字段,在response阶段对响应进行一些gzip解压操作。
1.负责将用户构建的Request请求转化为能够进行网络访问的请求
2.将这个符合网络请求的Request进行网络请求
3.将网络请求回来的Response转化为用户可用的Response,包括gzip压缩和gzip解压

CacheInterceptor

缓存拦截器

public Response intercept(Chain chain) throws IOException {
	//1. 读取候选缓存;
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
	// 计时开始时间
    long now = System.currentTimeMillis();

	 //2. 创建缓存策略(强制缓存,对比缓存等策略);
    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.
    }

	//根据策略,不使用网络,缓存又没有直接报错;
    // 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();
    }

	 // 4. 根据策略,不使用网络,有缓存就直接返回;
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

    Response networkResponse = null;
    try {
      // 执行下个拦截器,获得网络response
      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());
      }
    }

	// 判断是否有缓存,如果有缓存,而且下个拦截器返回的response的code为304未修改,则使用缓存
    // If we have a cache response too, then we're doing a conditional get.
    if (cacheResponse != null) {
      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 {
        closeQuietly(cacheResponse.body());
      }
    }

	// 从下个拦截器中获取到的response中读取信息
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();

	// 缓存response
    if (cache != null) {
      if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.
        CacheRequest cacheRequest = cache.put(response);
        return cacheWritingResponse(cacheRequest, response);
      }

	  // 删除无效缓存
      if (HttpMethod.invalidatesCache(networkRequest.method())) {
        try {
          cache.remove(networkRequest);
        } catch (IOException ignored) {
          // The cache cannot be written.
        }
      }
    }

	// 返回到上个拦截器
    return response;
  }
  1. 读取候选缓存;
  2. 创建缓存策略(根据头信息,判断强制缓存,对比缓存等策略);
  3. 根据策略,不使用网络,缓存又没有直接报错;
  4. 根据策略,不使用网络,有缓存就直接返回;
  5. 前面个都没有返回,读取网络结果(跑下一个拦截器);
  6. 接收到的网络结果,如果是code 304, 使用缓存,返回缓存结果(对比缓存)
  7. 读取网络结果;
  8. 对数据进行缓存;删除无效缓存;
  9. 返回网络读取的结果。

ConnectInterceptor

ConnectInterceptor负责网络连接,其本质是复用连接池中socket连接。

public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    //从拦截器链里得到StreamAllocation对象
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    //创建连接等一系列的操作
    HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
    //这里是获取前一步的connection.
    RealConnection connection = streamAllocation.connection();
	//把前面创建的连接,传递到下一个拦截器
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }

1.StreamAllocation 这里的是在RetryAndFollowUpInterceptor拦截器中构造的包含网络请求所有组件的StreamAllocation流分配对象,一步一步传递过来的。
2.HttpCodec对象用来编码request,解码response的 。
3.RealConnection 用来进行实际的io网络传输。
4.会将HttpCodec和RealConnection 传递给后面的拦截器使用。

StreamAllocation

首先,StreamAllocation的初始化在第一个拦截器RetryAndFollowUpInterceptor里面,

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

传入了三个参数,一个连接池,一个地址类,一个调用堆栈跟踪相关的。

在StreamAllocation构造函数中,主要是把这个三个参数保存为内部变量,供后面使用。
该构造器里面有两个重要的参数:
1.使用了Okhttp的连接池ConnectionPool
2.通过url创建了一个Address对象。

Okhttp连接池简单说明: 本篇只是对连接池做最简单的说明,内部的实现原理暂时不细讲。在Okhttp内部的连接池实现类为ConnectionPool,该类持有一个ArrayDeque队列作为缓存池,该队列里的元素为RealConnection(通过这个名字应该不难猜出RealConnection是来干嘛的)。该链接池在初始化OkhttpClient对象的时候由OkhttpClient的Builder类创建,并且ConnectionPool提供了put、get、evictAll等操作。但是Okhttp并没有直接对连接池进行获取,插入等操作;而是专门提供了一个叫Internal的抽象类来操作缓冲池:比如向缓冲池里面put一个RealConnection,从缓冲池get一个RealConnection对象,该类里面有一个public且为static的Internal类型的引用:

//抽象类
public abstract class Internal {
  public static Internal instance;
}1234

instance的初始化是在OkhttpClient的static语句块完成的:

static {
    Internal.instance = new Internal() {
       //省略部分代码
    };
  }

newStream()方法

主要做了如下工作:
1)从缓冲池ConnectionPool获取一个RealConnection对象,如果缓冲池里面没有就创建一个RealConnection对象并且放入缓冲池中,具体的说是放入ConnectionPool的ArrayDeque队列中。
2)获取RealConnection对象后并调用其connect(打开Socket链接)。下面具体分析:

public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
    //1. 获取设置的连接超时时间,读写超时的时间,以及是否进行重连。 
    int connectTimeout = client.connectTimeoutMillis();
    int readTimeout = client.readTimeoutMillis();
    int writeTimeout = client.writeTimeoutMillis();
    boolean connectionRetryEnabled = client.retryOnConnectionFailure();

    try {
     // 2. 获取健康可用的连接
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
     
     //3. 通过resultConnection初始化,对请求以及结果 编解码的类(分http 1.1 和http 2.0)。
     // 这里主要是初始化,在后面一个拦截器才用到这相关的东西。
      HttpCodec resultCodec = resultConnection.newCodec(client, this);

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

获取连接

findHealthyConnection()

 private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {

   // 1. 加了个死循环,一直找可用的连接
    while (true) {
     
      // 2. 这里继续去挖掘,寻找连接
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          connectionRetryEnabled);

     // 3. 连接池同步获取,上面找到的连接是否是一个新的连接,如果是的话,就直接返回了,就是我们需要找
    // 的连接了
      // If this is a brand new connection, we can skip the extensive health checks.
      synchronized (connectionPool) {
        if (candidate.successCount == 0) {
          return candidate;
        }
      }

      //4.  如果不是一个新的连接,那么通过判断,是否一个可用的连接。
      // 里面是通过Socket的一些方法进行判断的,有兴趣的,可以继续研究一下
      // 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;
    }
  }

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;
   
   // 1. 同步线程池,来获取里面的连接
    synchronized (connectionPool) {
        
         // 2.  异常的处理。做些判断,是否已经释放,是否编解码类为空,是否用户已经取消         if (released) throw new IllegalStateException("released");
         if (codec != null) throw new IllegalStateException("codec != null");
         if (canceled) throw new IOException("Canceled");
         
         // 3. 尝试用一下现在的连接,判断一下,是否有可用的连接。(使用已存在的连接)
         // Attempt to use an already-allocated connection.
          RealConnection allocatedConnection = this.connection;
         if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
            return allocatedConnection;
          }
          
         // 4. 尝试在连接池中获取一个连接,get方法中会直接调用,注意最后一个参数为空
         // 里面是一个for循环,在连接池里面,寻找合格的连接
         // 而合格的连接会通过,StreamAllocation中的acquire方法,更新connection的值。(从缓存中获取)      
         // Attempt to get a connection from the pool.
         Internal.instance.get(connectionPool, address, this, null);
         if (connection != null) {
             return connection;
          }

          selectedRoute = route;
    }
    //5. 判断上面得到的线路,是否空,如果为空的,寻找一个可用的线路
    // 对于线路的选,可以深究一下这个RouteSeletor
    // 线路的选择,多ip的支持       if (selectedRoute == null) {
      selectedRoute = routeSelector.next();// 里面又个神奇的递归
    }

    RealConnection result;

    //6.  以上都不符合,创建一个连接
    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");
   
      // 7. 由于上面我们获取了一个线路,无论是新建的,或者已有的。
      // 我们通过这个线路,继续在连接池中寻找是否有可用的连接。
      // Now that we have an IP address, make another attempt at getting a connection from the pool.
      // This could match due to connection coalescing.
      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.
      route = selectedRoute;
      refusedStreamCount = 0;
      
      // 8. 如果前面这么寻找,都没在连接池中找打可用的连接,那么就新建一个
      result = new RealConnection(connectionPool, selectedRoute);
      acquire(result);
    }
    
    // 9. 这里就是就是连接的操作了,终于找到连接的正主了,这里会调用RealConnection的连接方法,进行连接操作。
    // 如果是普通的http请求,会使用Socket进行连接
    // 如果是https,会进行相应的握手,建立通道的操作。
    // 这里就不对里面的操作进行详细分析了,有兴趣可以在进去看看
    // Do TCP + TLS handshakes. This is a blocking operation.
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    routeDatabase().connected(result.route());

    Socket socket = null;
    
   // 10. 最后就是同步加到 连接池里面了
    synchronized (connectionPool) {
      // Pool the connection.
      Internal.instance.put(connectionPool, result);

      // 最后加了一个多路复用的判断,这个是http2才有的
      // 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;
  }

建立连接

建立连接是比较重要的一步了。如果是Https还有证书步骤

public void connect(
    int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) {
  if (protocol != null) throw new IllegalStateException("already connected");
// 线路的选择
  RouteException routeException = null;
  List<ConnectionSpec> connectionSpecs = route.address().connectionSpecs();
  ConnectionSpecSelector connectionSpecSelector = new ConnectionSpecSelector(connectionSpecs);

  if (route.address().sslSocketFactory() == null) {
    if (!connectionSpecs.contains(ConnectionSpec.CLEARTEXT)) {
      throw new RouteException(new UnknownServiceException(
          "CLEARTEXT communication not enabled for client"));
    }
    String host = route.address().url().host();
    if (!Platform.get().isCleartextTrafficPermitted(host)) {
      throw new RouteException(new UnknownServiceException(
          "CLEARTEXT communication to " + host + " not permitted by network security policy"));
    }
  }

// 连接开始
  while (true) {
    try {
    // 如果要求通道模式,建立通道连接,通常不是这种
      if (route.requiresTunnel()) {
        connectTunnel(connectTimeout, readTimeout, writeTimeout);
      } else {
      // 一般都走这条逻辑了,实际上很简单就是socket的连接
        connectSocket(connectTimeout, readTimeout);
      }
      // https的建立
      establishProtocol(connectionSpecSelector);
      break;
    } catch (IOException e) {
      closeQuietly(socket);
      closeQuietly(rawSocket);
      socket = null;
      rawSocket = null;
      source = null;
      sink = null;
      handshake = null;
      protocol = null;
      http2Connection = null;

      if (routeException == null) {
        routeException = new RouteException(e);
      } else {
        routeException.addConnectException(e);
      }

      if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
        throw routeException;
      }
    }
  }

  if (http2Connection != null) {
    synchronized (connectionPool) {
      allocationLimit = http2Connection.maxConcurrentStreams();
    }
  }
}

connectSocket

private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
  Proxy proxy = route.proxy();
  Address address = route.address();

  // 根据代理类型处理socket,为true
  rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
      ? address.socketFactory().createSocket()
      : new Socket(proxy);

  rawSocket.setSoTimeout(readTimeout);
  try {
    // 连接socket,之所以这样写是因为支持不同的平台
    /**
    * 里面实际上是  socket.connect(address, connectTimeout);
    *
    */
    Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout);
  } catch (ConnectException e) {
    ConnectException ce = new ConnectException("Failed to connect to " + route.socketAddress());
    ce.initCause(e);
    throw ce;
  }
  // 得到输入/输出流
  source = Okio.buffer(Okio.source(rawSocket));
  sink = Okio.buffer(Okio.sink(rawSocket));
}

如果使用的是https协议,并且配置有真实将会协议升级。

Https协议的建立 connectTls

如果使用的是https协议socket连接完成后还有一步,就是Tls的处理

private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
  Address address = route.address();
  SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
  boolean success = false;
  SSLSocket sslSocket = null;
  try {
    // 在原来socket上加一层ssl
    // Create the wrapper over the connected socket.
    sslSocket = (SSLSocket) sslSocketFactory.createSocket(
        rawSocket, address.url().host(), address.url().port(), true /* autoClose */);

    // Configure the socket's ciphers, TLS versions, and extensions.
    ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
    if (connectionSpec.supportsTlsExtensions()) {
      Platform.get().configureTlsExtensions(
          sslSocket, address.url().host(), address.protocols());
    }

    // Force handshake. This can throw!
    sslSocket.startHandshake();
    Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession());

    // Verify that the socket's certificates are acceptable for the target host.
    if (!address.hostnameVerifier().verify(address.url().host(), sslSocket.getSession())) {
      X509Certificate cert = (X509Certificate) unverifiedHandshake.peerCertificates().get(0);
      throw new SSLPeerUnverifiedException("Hostname " + address.url().host() + " not verified:"
          + "\n    certificate: " + CertificatePinner.pin(cert)
          + "\n    DN: " + cert.getSubjectDN().getName()
          + "\n    subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
    }

    // Check that the certificate pinner is satisfied by the certificates presented.
    address.certificatePinner().check(address.url().host(),
        unverifiedHandshake.peerCertificates());

    // Success! Save the handshake and the ALPN protocol.
    String maybeProtocol = connectionSpec.supportsTlsExtensions()
        ? Platform.get().getSelectedProtocol(sslSocket)
        : null;
    socket = sslSocket;
    source = Okio.buffer(Okio.source(socket));
    sink = Okio.buffer(Okio.sink(socket));
    handshake = unverifiedHandshake;
    protocol = maybeProtocol != null
        ? Protocol.get(maybeProtocol)
        : Protocol.HTTP_1_1;
    success = true;
  } catch (AssertionError e) {
    if (Util.isAndroidGetsocknameError(e)) throw new IOException(e);
    throw e;
  } finally {
    if (sslSocket != null) {
      Platform.get().afterHandshake(sslSocket);
    }
    if (!success) {
      closeQuietly(sslSocket);
    }
  }
}

CallServerInterceptor

 public Response intercept(Chain chain) throws IOException {
    // 省略部分代码
    // 获取HttpCodec 
    HttpCodec httpCodec = realChain.httpStream();
    // 省略部分代码
    Request request = realChain.request();
    //向服务器发送请求
    httpCodec.writeRequestHeaders(request);

    Response.Builder responseBuilder = null;
    // 检测是否有请求body
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {

      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        //构建responseBuilder对象
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

       //如果服务器允许发送请求body发送
      if (responseBuilder == null) {
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } else if (!connection.isMultiplexed()) {
         //省略部分代码
      }
    }

    //结束请求
    httpCodec.finishRequest();

    //构建请求buidder对象
    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) {
      //省略部分代码
    } else {
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

    //省略部分代码

    return response;
  }

CallServerInterceptor的主要功能就是—向服务器发送请求,并最终返回Response对象供客户端使用。

总结

RetryAndFollowUpInterceptor:这个拦截器它的作用主要是负责请求的重定向操作,用于处理网络请求中,请求失败后的重试机制。
BridgeInterceptor:BridgeInterceptor负责在request阶段对请求头添加一些字段,在response阶段对响应进行一些gzip解压操作。
CacheInterceptor:用来实现响应缓存。比如获取到的 Response 带有 Date,Expires,Last-Modified,Etag 等 header,表示该 Response 可以缓存一定的时间,下次请求就可以不需要发往服务端,直接拿缓存的
ConnectInterceptor:ConnectInterceptor负责网络连接,其本质是复用连接池中socket连接。
CallServerInterceptor:CallServerInterceptor的主要功能就是—向服务器发送请求,并最终返回Response对象供客户端使用。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值