okhttp 之 ConnectInterceptor拦截器分析

还是一样 先从重写方法开始

  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.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
//通过newStream  获取一个   HttpCodec 
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//获得一个连接
RealConnection connection = streamAllocation.connection();

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

我们先进入.newStream(client, chain, doExtensiveHealthChecks) 这个方法看下

public HttpCodec newStream(
  OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
 //获取连接超时时间
int connectTimeout = chain.connectTimeoutMillis();
//读写等超时时间
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();

try {
// findHealthyConnection 找一个SOCKET连接  先判断有没有健康的连接 没有则重新握手  创建  连接缓存
  RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
      writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
   //基于okio的输入输出流  具体可以进去看源码 返回一个 HttpCodec
    HttpCodec resultCodec = resultConnection.newCodec(client, chain, this)
        //拿到一个连接;
     RealConnection connection = streamAllocation.connection();
  synchronized (connectionPool) {
    codec = resultCodec;
    return resultCodec;
  }
} catch (IOException e) {
  throw new RouteException(e);
}

}

我们进入 findHealthyConnection这个方法看下

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

  RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
      pingIntervalMillis, connectionRetryEnabled);
  //后面没啥看的
  // If this is a brand new connection, we can skip the extensive health checks.
  synchronized (connectionPool) {
    if (candidate.successCount == 0) {
      return candidate;
    }
  }

  // Do a (potentially slow) check to confirm that the pooled connection is still good. If it
  // isn't, take it out of the pool and start again.
  if (!candidate.isHealthy(doExtensiveHealthChecks)) {
    noNewStreams();
    continue;
  }

  return candidate;
}

}
我们再进入 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,
  int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
boolean foundPooledConnection = false;
RealConnection result = null;
Route selectedRoute = null;
Connection releasedConnection;
Socket toClose;
synchronized (connectionPool) {
  if (released) throw new IllegalStateException("released");
  if (codec != null) throw new IllegalStateException("codec != null");
  if (canceled) throw new IOException("Canceled");

  // Attempt to use an already-allocated connection. We need to be careful here because our
  // already-allocated connection may have been restricted from creating new streams.
  releasedConnection = this.connection;
  toClose = releaseIfNoNewStreams();
  if (this.connection != null) {
    // We had an already-allocated connection and it's good.
    result = this.connection;
    releasedConnection = null;
  }
// 做一系列的判断
  ...  
  synchronized (connectionPool) {
  if (canceled) throw new IOException("Canceled");

  if (newRouteSelection) {
    // Now that we have a set of IP addresses, make another attempt at getting a connection from
    // the pool. This could match due to connection coalescing.
    //循环路由
    List<Route> routes = routeSelection.getAll();
    for (int i = 0, size = routes.size(); i < size; i++) {
      Route route = routes.get(i);
      Internal.instance.get(connectionPool, address, this, route);
      if (connection != null) {
        foundPooledConnection = true;
        result = connection;
        this.route = route;
        break;
      }
    }
  }

  if (!foundPooledConnection) {
    if (selectedRoute == null) {
      selectedRoute = routeSelection.next();
    }

    // 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;
 //如果没有找到可用的 
    result = new RealConnection(connectionPool, selectedRoute);
    acquire(result, false);
  }
}

// If we found a pooled connection on the 2nd time around, we're done.
if (foundPooledConnection) {
  eventListener.connectionAcquired(call, result);
  return result;
}

// Do TCP + TLS handshakes. This is a blocking operation.
//建立连接
result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
    connectionRetryEnabled, call, eventListener);
routeDatabase().connected(result.route());

Socket socket = null;
synchronized (connectionPool) {
  reportedAcquired = true;

  // Pool the connection.
  Internal.instance.put(connectionPool, result);

  // If another multiplexed connection to the same address was created concurrently, then
  // release this connection and acquire that one.
  if (result.isMultiplexed()) {
    socket = Internal.instance.deduplicate(connectionPool, address, this);
    result = connection;
  }
}
closeQuietly(socket);

eventListener.connectionAcquired(call, result);
return result;

}
我们再进入.connect这个方法看下

public void connect(int connectTimeout, int readTimeout, int writeTimeout,
  int pingIntervalMillis, boolean connectionRetryEnabled, Call call,
  EventListener eventListener) {
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 {
      //HTTPS 隧道
    if (route.requiresTunnel()) {
      connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener);
      if (rawSocket == null) {
        // We were unable to connect the tunnel but properly closed down our resources.
        break;
      }
    } else {
      //进行一个Socket 连接
      connectSocket(connectTimeout, readTimeout, call, eventListener);
    }
    establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener);
    eventListener.connectEnd(call, route.socketAddress(), route.proxy(), protocol);
    break;
  } catch (IOException e) {
    closeQuietly(socket);
    closeQuietly(rawSocket);
    socket = null;
    rawSocket = null;
    source = null;
    sink = null;
    handshake = null;
    protocol = null;
    http2Connection = null;

    eventListener.connectFailed(call, route.socketAddress(), route.proxy(), null, e);

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

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

if (route.requiresTunnel() && rawSocket == null) {
  ProtocolException exception = new ProtocolException("Too many tunnel connections attempted: "
      + MAX_TUNNEL_ATTEMPTS);
  throw new RouteException(exception);
}

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

}

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

推荐阅读更多精彩内容

  • 因为项目集成,在使用Fresco的时候,有集成OkHttp,所以接下来是OkHttp3.0的 简单一个列子,其中里...
    TragedyGo阅读 1,347评论 0 1
  • 简介 目前在HTTP协议请求库中,OKHttp应当是非常火的,使用也非常的简单。网上有很多文章写了关于OkHttp...
    第八区阅读 1,370评论 1 5
  • 周得到006 20180521-20180527 壹|精读营之旅 精读营历时半个月,前两天的准备,十天的打卡,后三...
    过云雨Milo阅读 227评论 0 1
  • 小住老屋(二) 母亲张罗着烧中饭。我帮母亲着火。母亲不让,她怕灶堂的灰沾到我身上。看着母亲把树叶送进灶堂,我问母亲...
    春之原野阅读 422评论 5 6
  • CASSIEVONG诗无是英国轻奢珠宝品牌, 致力于打造属于现代女性诗意无限的幻美仙境, 每件珠宝饰品都拥有最新颖...
    CASSIEVONG诗无阅读 611评论 0 0