文章目录
- 1. 作用
- 2. 错误重试
- 3. 重定向
- 4. 验证
- 5. 其他
1. 作用
此拦截器的主要功能:
- 重试
- 重定向跟踪
- 验证
2. 错误重试
发生错误后,首先会判断该请求是不是可恢复的
有以下情况之一,则该请求是不可恢复的,否则是可恢复的
- 配置了
retryOnConnectionFailure
为false
,即指定错误时不恢复 - 请求只能发送一次,并且已经发送了(已经发送是指,请求已经经过了所有的拦截器处理,数据已经写入到
Socket
中。在源码中,只有传输文件限定了只能发送一次) - 当错误类型为:
底层协议出错
、使用Https
协议时,证书认证出错,证书锁定出错。 - Sockert 连接超时(不是
HTTP
超时) - 没有可用路由
- 如果是可恢复的,则会在记录下当前错误后,将该请求重新发送
- 如果是不可恢复的,则抛出
IO
异常
3. 重定向
当接收到响应后,会根据响应的状态码来判断是否需要重定向。
重定向的状态码为:
- 307:临时重定向
- 308:永久重定向
- 300:请选择
- 301:永久移动
- 302:临时移动
- 303:see other
如果需要重定向,会重新构建一个Request
,然后发送
4. 验证
根据响应的状态码判断是否需要向服务器(或理服务器)发送验证信息。
当接收到的响应状态码为:
- 407:代理服务器验证
- 401:服务器验证
会构建一个新的包含验证信息的Request
,再次发送。
5. 其他
当接收到诸如:
503
服务器当前不可用408
:http超时
等状态码时。
会检查Retry-After
的值来判断是否可以重新发送请求。
判断标准: 有Retry-After
头信息,并且Retry-After
表明了可以立刻再次请求
有一个方法:
val followUp = followUpRequest(response, exchange)
这个方法的作用是:根据response
,判断需不需要再次发送请求。如果需要,就返回一个待发送的新请求,如果不需要,就返回null。(ps:上面的重定向和验证都是需要重新发送新的请求的)。
方法详细信息
/********************///*** Figures out the HTTP request to make in response to receiving [userResponse]. This will* either add authentication headers, follow redirects or handle a client request timeout. If a* follow-up is either unnecessary or not applicable, this returns null.*/@Throws(IOException::class)private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
val route = exchange?.connection?.route()val responseCode = userResponse.codeval method = userResponse.request.methodwhen (responseCode) {
HTTP_PROXY_AUTH -> {
//代理服务器要求验证val selectedProxy = route!!.proxyif (selectedProxy.type() != Proxy.Type.HTTP) {
throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")}return client.proxyAuthenticator.authenticate(route, userResponse)}//服务器要求验证HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)//重定向HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
return buildRedirectRequest(userResponse, method)}//HTTP 超时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 (!client.retryOnConnectionFailure) {
// The application layer has directed us not to retry the request.return null}val requestBody = userResponse.request.bodyif (requestBody != null && requestBody.isOneShot()) {
return null}val priorResponse = userResponse.priorResponseif (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.return null}if (retryAfter(userResponse, 0) > 0) {
return null}return userResponse.request}//503 Server UnavailableHTTP_UNAVAILABLE -> {
val priorResponse = userResponse.priorResponseif (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
// We attempted to retry and got another timeout. Give up.return null}if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
// specifically received an instruction to retry without delayreturn userResponse.request}return null}//未知HTTP_MISDIRECTED_REQUEST -> {
// OkHttp can coalesce HTTP/2 connections even if the domain names are different. See// RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then// we can retry on a different connection.val requestBody = userResponse.request.bodyif (requestBody != null && requestBody.isOneShot()) {
return null}if (exchange == null || !exchange.isCoalescedConnection) {
return null}exchange.connection.noCoalescedConnections()return userResponse.request}else -> return null}}