当前位置: 首页 > news >正文

OkHttp源码解析(二)

五大拦截器

一、重定向与重试拦截器

catch (IOException e) {// todo 请求发出去了,但是和服务器通信失败了。(socket 流正在读/写数据时断开连接)// HTTP/2 才会抛出 ConnectionShutdownException,所以对于 HTTP/1,requestSendStarted 一定是 trueboolean requestSendStarted = !(e instanceof ConnectionShutdownException);if (!recover(e, streamAllocation, requestSendStarted, request)) {throw e;}releaseConnection = false;continue;
}
两个异常都是根据 recover 方法判断是否能够进行重试,如果返回 true ,则表示允许重试。
private boolean recover(IOException e,StreamAllocation streamAllocation,boolean requestSendStarted,Request userRequest) {streamAllocation.streamFailed(e);// todo 1. 如果在 OkHttpClient 配置中关闭了重试(默认允许),则失败后不再重试if (!client.retryOnConnectionFailure()) return false;// todo 2. 如果是 RouteException 不用看这个条件;// 如果是 IOException,且请求体不可重复,则不能重试if (requestSendStarted && userRequest.body() instanceof UnrepeatableRequestBody) {return false;}// todo 3. 判断异常是否属于可重试的异常if (!isRecoverable(e, requestSendStarted)) return false;// todo 4. 是否还有可用的路由路径if (!streamAllocation.hasMoreRoutes()) return false;// 失败恢复时,使用相同的路由选择器并建立新连接return true;
}

1协议异常,如果是那么直接判定不能重试;(你的请求或者服务器的响应本身就存在问题,没有按照http协议来 定义数据,再重试也没用)
2超时异常,可能由于网络波动造成了Socket连接的超时,可以使用不同路线重试。
3SSL证书异常/SSL验证失败异常,前者是证书验证失败,后者可能就是压根就没证书,或者证书数据不正确, 那还怎么重试?
经过了异常的判定之后,如果仍然允许进行重试,就会再检查当前有没有可用路由路线来进行连接。简单来说,比 如 DNS 对域名解析后可能会返回多个 IP,在一个IP失败后,尝试另一个IP进行重试。

private Request followUpRequest(Response userResponse) throws IOException {if (userResponse == null) throw new IllegalStateException();Connection connection = streamAllocation.connection();Route route = connection != null ? connection.route() : null;int responseCode = userResponse.code();final String method = userResponse.request().method();switch (responseCode) {// 407 代理认证case HTTP_PROXY_AUTH: {Proxy selectedProxy = route != null ? route.proxy() : client.proxy();if (selectedProxy.type() != Proxy.Type.HTTP) {throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");}return client.proxyAuthenticator().authenticate(route, userResponse);}// 401 服务器认证case HTTP_UNAUTHORIZED:return client.authenticator().authenticate(route, userResponse);// 308/307 保持方法case HTTP_PERM_REDIRECT:case HTTP_TEMP_REDIRECT: {if (!method.equals("GET") && !method.equals("HEAD")) return null;// fall-through 到重定向处理}// 300/301/302/303 各类重定向case HTTP_MULT_CHOICE:case HTTP_MOVED_PERM:case HTTP_MOVED_TEMP:case HTTP_SEE_OTHER: {if (!client.followRedirects()) return null;String location = userResponse.header("Location");if (location == null) return null;HttpUrl url = userResponse.request().url().resolve(location);if (url == null) return null;// 跨 Scheme 的重定向是否允许boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme());if (!sameScheme && !client.followSslRedirects()) return null;Request.Builder requestBuilder = userResponse.request().newBuilder();// 是否保留请求体if (HttpMethod.permitsRequestBody(method)) {final boolean maintainBody = HttpMethod.redirectsWithBody(method);if (HttpMethod.redirectsToGet(method)) {requestBuilder.method("GET", null);} else {RequestBody requestBody = maintainBody ? userResponse.request().body() : null;requestBuilder.method(method, requestBody);}if (!maintainBody) {requestBuilder.removeHeader("Transfer-Encoding");requestBuilder.removeHeader("Content-Length");requestBuilder.removeHeader("Content-Type");}}// 跨主机时移除认证头if (!sameConnection(userResponse, url)) {requestBuilder.removeHeader("Authorization");}return requestBuilder.url(url).build();}// 408 客户端超时case HTTP_CLIENT_TIMEOUT: {if (!client.retryOnConnectionFailure()) return null;if (userResponse.request().body() instanceof UnrepeatableRequestBody) return null;// 如果前一个响应也是 408,则不再重试if (userResponse.priorResponse() != null&& userResponse.priorResponse().code() == HTTP_CLIENT_TIMEOUT) {return null;}// Respect Retry-Afterif (retryAfter(userResponse, 0) > 0) return null;return userResponse.request();}// 503 服务不可用case HTTP_UNAVAILABLE: {if (userResponse.priorResponse() != null&& userResponse.priorResponse().code() == HTTP_UNAVAILABLE) {return null;}if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {return userResponse.request();}return null;}default:return null;}
}private boolean sameConnection(Response response, HttpUrl url) {HttpUrl priorUrl = response.request().url();return priorUrl.host().equals(url.host())&& priorUrl.port() == url.port()&& priorUrl.scheme().equals(url.scheme());
}private int retryAfter(Response response, int defaultDelaySeconds) {String header = response.header("Retry-After");if (header == null) return defaultDelaySeconds;// 仅支持秒的简单实现;非法值按很大值处理表示不应立刻重试try {return Integer.parseInt(header.trim());} catch (NumberFormatException ignore) {return Integer.MAX_VALUE;}
}

二、桥接拦截器

BridgeInterceptor ,连接应用程序和服务器的桥梁,我们发出的请求将会经过它的处理才能发给服务器,比如设 置请求内容长度,编码,gzip压缩,cookie等,获取响应后保存Cookie等操作。这个拦截器相对比较简单。
补全请求头:

三、缓存拦截器

3、交给下一个责任链继续处理
4、后续工作,返回304则用缓存的响应;否则使用网络响应并缓存本次响应(只缓存Get请求的响应)
缓存拦截器的工作说起来比较简单,但是具体的实现,需要处理的内容很多。在缓存拦截器中判断是否可以使用缓 存,或是请求服务器都是通过 CacheStrategy 判断。

缓存策略

缓存检测

四、连接拦截器

五、请求服务器拦截器

六、OkHttp总结

http://www.dtcms.com/a/483035.html

相关文章:

  • 【高中小蓝本】数论 02 最大公约数 最小公倍数
  • 【LeetCode】72. 编辑距离
  • 17网站一起做网店的流程北京竞价托管代运营
  • DM线程的管理知识学习
  • System V IPC:Linux进程通信的标准方案
  • 免费的舆情网站app下载ui设计手机界面
  • Spring 三级缓存
  • 怎么样看网站用什么程序做的开发公司经营范围
  • 小广告网站音乐网站系统怎么做
  • 单片机中的TVS管
  • c++最常用的几种设计模式
  • 河南平台网站建设公司临沂企业自助建站系统
  • 下载 asp 网站源码响应式网站 翻译代码
  • 大气医院网站源码河北省住房和城乡建设厅信用网站
  • 【agent】AI 数字人构建10:FunASR 1:语音识别C++服务/客户端构建
  • 教育机构网站建设方案书企业邮箱注册需要多少钱
  • 河南省建设工程监理协会网站cms 导航网站
  • Centos Stream 8 Tomcat学习
  • 网站系统cmsphp教育网站开发工作
  • 在Ubuntu22.04和24.04中安装Docker并安装和配置Java、Mysql、Tomcat
  • 【Linux】线程同步与互斥(1)
  • 网站开发英语英语义乌网八方资源家1688网商网
  • 基于单片机的PID调节脉动真空灭菌器上位机远程监控设计
  • 汕头网站关键词优化教程资源分享网站怎么做
  • STM32H7xx 运行 LWIP 时的 MPU 配置介绍 LAT1510
  • 能动框架战场:如何摆脱供应商锁定并在下次AI战争中生存
  • 免费试用网站空间人人开发接单官网
  • 视联网技术产业观察与分析:视频隐私与安全防护
  • 南通网站建设祥云深圳罗湖网站设计公司
  • 基于蚁群算法优化BP神经网络的实现方法