OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor
来源:互联网 发布:新闻联播配音软件 编辑:程序博客网 时间:2024/05/19 09:37
我们看下它的intercept
public Response intercept(Chain chain) throws IOException { Request request = chain.request(); streamAllocation = new StreamAllocation( client.connectionPool(), createAddress(request.url())); int followUpCount = 0; Response priorResponse = null; while (true) { if (canceled) { streamAllocation.release(); throw new IOException("Canceled"); } Response response = null; boolean releaseConnection = true; try { response = ((RealInterceptorChain) chain).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(), true, request)) throw e.getLastConnectException(); releaseConnection = false; continue; } catch (IOException e) { // An attempt to communicate with a server failed. The request may have been sent. if (!recover(e, false, 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. if (priorResponse != null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build(); } Request followUp = followUpRequest(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) { 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())); } else if (streamAllocation.stream() != null) { throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?"); } request = followUp; priorResponse = response; } }
这里首先创建了一个StreamAllocation,StreamAllocation是用来做连接分配的,传递的参数有两个,一个是前面创建的连接池,另外一个是调用createAddress创建的Address
private Address createAddress(HttpUrl url) { SSLSocketFactory sslSocketFactory = null; HostnameVerifier hostnameVerifier = null; CertificatePinner certificatePinner = null; if (url.isHttps()) { sslSocketFactory = client.sslSocketFactory(); hostnameVerifier = client.hostnameVerifier(); certificatePinner = client.certificatePinner(); } return new Address(url.host(), url.port(), client.dns(), client.socketFactory(), sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(), client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector()); }
public Address(String uriHost, int uriPort, Dns dns, SocketFactory socketFactory, SSLSocketFactory sslSocketFactory, HostnameVerifier hostnameVerifier, CertificatePinner certificatePinner, Authenticator proxyAuthenticator, Proxy proxy, List<Protocol> protocols, List<ConnectionSpec> connectionSpecs, ProxySelector proxySelector) { this.url = new HttpUrl.Builder() .scheme(sslSocketFactory != null ? "https" : "http") .host(uriHost) .port(uriPort) .build(); if (dns == null) throw new NullPointerException("dns == null"); this.dns = dns; if (socketFactory == null) throw new NullPointerException("socketFactory == null"); this.socketFactory = socketFactory; if (proxyAuthenticator == null) { throw new NullPointerException("proxyAuthenticator == null"); } this.proxyAuthenticator = proxyAuthenticator; if (protocols == null) throw new NullPointerException("protocols == null"); this.protocols = Util.immutableList(protocols); if (connectionSpecs == null) throw new NullPointerException("connectionSpecs == null"); this.connectionSpecs = Util.immutableList(connectionSpecs); if (proxySelector == null) throw new NullPointerException("proxySelector == null"); this.proxySelector = proxySelector; this.proxy = proxy; this.sslSocketFactory = sslSocketFactory; this.hostnameVerifier = hostnameVerifier; this.certificatePinner = certificatePinner; }更加client和请求的相关信息初始化了 Address
再看下 StreamAllocation的创建
public StreamAllocation(ConnectionPool connectionPool, Address address) { this.connectionPool = connectionPool; this.address = address; this.routeSelector = new RouteSelector(address, routeDatabase()); }这里保存了前面传过来的连接池和地址,并创建了一个RouteSelector,并进行了路由的一个选择
回到intercept,进入while循环
1、首先查看请求是否已经取消
2、调用RealInterceptorChain的proceed处理这个请求并把刚创建的StreamAllocation传递进去
3、如果前面第二步没有出现异常,则说明请求完成,设置releaseConnection为false,出现异常则将releaseConnection置为true,并释放前面创建的StreamAllocation
4、priorResponse不为空,则说明前面已经获取到了响应,这里会结合当前获取的Response和先前的Response
5、调用followUpRequest查看响应是否需要重定向,如果不需要重定向则返回当前请求
6、重定向次数+1,并且判断StreamAllocation是否需要重新创建
7、重新设置request,并把当前的Response保存到priorResponse,继续while循环
我们看下对是否需要重定向的判断followUpRequest
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) { 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); case HTTP_UNAUTHORIZED: return client.authenticator().authenticate(route, userResponse); case HTTP_PERM_REDIRECT: case HTTP_TEMP_REDIRECT: // "If the 307 or 308 status code is received in response to a request other than GET // or HEAD, the user agent MUST NOT automatically redirect the request" if (!method.equals("GET") && !method.equals("HEAD")) { return null; } // fall-through case HTTP_MULT_CHOICE: case HTTP_MOVED_PERM: case HTTP_MOVED_TEMP: case HTTP_SEE_OTHER: // Does the client allow redirects? if (!client.followRedirects()) return null; String location = userResponse.header("Location"); if (location == null) return null; HttpUrl url = userResponse.request().url().resolve(location); // Don't follow redirects to unsupported protocols. if (url == null) return null; // If configured, don't follow redirects between SSL and non-SSL. boolean sameScheme = url.scheme().equals(userResponse.request().url().scheme()); if (!sameScheme && !client.followSslRedirects()) return null; // Redirects don't include a request body. Request.Builder requestBuilder = userResponse.request().newBuilder(); if (HttpMethod.permitsRequestBody(method)) { if (HttpMethod.redirectsToGet(method)) { requestBuilder.method("GET", null); } else { requestBuilder.method(method, null); } requestBuilder.removeHeader("Transfer-Encoding"); requestBuilder.removeHeader("Content-Length"); requestBuilder.removeHeader("Content-Type"); } // When redirecting across hosts, drop all authentication headers. This // is potentially annoying to the application layer since they have no // way to retain them. if (!sameConnection(userResponse, url)) { requestBuilder.removeHeader("Authorization"); } return requestBuilder.url(url).build(); case HTTP_CLIENT_TIMEOUT: // 408's are rare in practice, but some servers like HAProxy use this response code. The // spec says that we may repeat the request without modifications. Modern browsers also // repeat the request (even non-idempotent ones.) if (userResponse.request().body() instanceof UnrepeatableRequestBody) { return null; } return userResponse.request(); default: return null; } }这里主要是根据响应码,查看是否需要重定向,并重新设置请求
这样RetryAndFollowUpInterceptor拦截器就分析完了,下一个拦截器的启动是通过调用RealInterceptorChain的proceed
public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream, Connection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; // If we already have a stream, confirm that the incoming request will use it. if (this.httpStream != null && !sameConnection(request.url())) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must retain the same host and port"); } // If we already have a stream, confirm that this is the only call to chain.proceed(). if (this.httpStream != null && calls > 1) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must call proceed() exactly once"); } // Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpStream, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); // Confirm that the next interceptor made its required call to chain.proceed(). if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) { throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once"); } // Confirm that the intercepted response isn't null. if (response == null) { throw new NullPointerException("interceptor " + interceptor + " returned null"); } return response; }这里index为1,创建的RealInterceptorChain的index为2,获取到的拦截器是BridgeInterceptor,下一篇我们分析它的intercept方法
0 0
- OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor
- 浅析 OkHttp 拦截器之 RetryAndFollowUpInterceptor
- OKhttp源码解析---拦截器之BridgeInterceptor
- OKhttp源码解析---拦截器之CacheInterceptor
- OKhttp源码解析---拦截器之ConnectInterceptor
- OKhttp源码解析---拦截器之CallServerInterceptor
- Okhttp源码解析之Interceptor(拦截器)
- OkHttp框架的RetryAndFollowUpInterceptor请求重定向源码解析
- OkHttp 3.x 源码解析之Interceptor 拦截器
- OkHttp 源码解析(3) Interceptor 拦截器
- okhttp源码分析(二)-RetryAndFollowUpInterceptor过滤器
- OkHttp之拦截器
- OkHttp源码解析之概述
- OkHttp完全解析(六)拦截器
- OkHttp完全解析(六)拦截器
- OkHttp 3.x 源码解析之Dispatcher
- okhttp之自定义Interceptor:缓存拦截器
- Okhttp-wiki 之 Interceptors 拦截器
- js 获取日期函数
- 二. play开发Hello World
- Service Worker初体验(Progressive Web Apps)
- ifunk一体机,良心机
- java数据类型的相互转换
- OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor
- redis链表结构
- coredata学习总结(五)
- Android常见开源解决方案
- 用命令行方式获取打包签名文件的SHA1和MD5
- mysql的char和varchar
- Android 自定义控件 自定义属性 无法找到报异常
- JavaScript学习记录02--基础篇之javascript基本语法(一)
- WebService概述及案例分析