OkHttp解析系列-重定向和出错重试
2015-11-18 22:21
477 查看
&emps;这是OkHttp系列博文的第一篇,之前写过一篇草稿,介绍OkHttp的整体框架,但是感觉涉及的知识太多,无法在一篇中讲述清楚,所以,之后的博文都只关注某一方面的知识,争取文章短小精悍。
今天主要研究一下OkHttp发送
我们今天研究
这是函数的第一部分,主要是将
在这段代码中,
这里我们可以看到
这一段就是根据回复的状态码生成重定向请求的代码逻辑。
HTTP_PROXY_AUTH 407 表示需要经过代理服务器认证 ,这时抛出异常,不进行重定向
HTTP_UNAUTHORIZED 401 身份未认证
HTTP_PERM_REDIRECT 308 HTTP_TEMP_REDIRECT 307 这两种状态码时,只有当请求的
HTTP_MULT_CHOICE 300 HTTP_MOVED_PERM 301 HTTP_MOVED_TEMP 302 HTTP_SEE_OTHER 303 当是这些状态码时,先判断是否运行重定向,然后获得
本文也在我的独立博文中同时发布点我
今天主要研究一下OkHttp发送
Http请求过程中的重定向和出错重试,主要涉及的源码文件有
Call.java``HttpEngine.java。
我们今天研究
Call的
Response getResponse(Request request, boolean forWebSocket) throws IOException函数,它是你调用
Call.execute()返回
Response所调用的核心函数,主要功能是新建一个
HttpEngine发送
Request然后处理出错重试和重定向问题。
设置Headers
// Copy body metadata to the appropriate request headers. RequestBody body = request.body(); if (body != null) { Request.Builder requestBuilder = request.newBuilder();//拷贝了内部数据 MediaType contentType = body.contentType(); if (contentType != null) { requestBuilder.header("Content-Type", contentType.toString()); } 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"); } request = requestBuilder.build(); }
这是函数的第一部分,主要是将
RequestBody的一些元数据拷贝到
Header的首部中,主要是
Content-Type和
Transfer-Encoding。
Content-Type相信大家都了解,标示
RequestBody的
Mime-Type,格式为
主类型/子类型,比如
text/xml。而
Transfer-Encoding是表示一种网络传输的方式,想具体了解的同学可以看一下这个链接点我.
出错重试
// Create the initial HTTP engine. Retries and redirects need new engine for each attempt. // 建立一个初始的http 引擎,每次重试和重定向都需要新的引擎 engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null, null); int followUpCount = 0; //连续发送请求 while (true) { if (canceled) { //如果被取消啦 engine.releaseConnection(); throw new IOException("Canceled"); } try { engine.sendRequest(); engine.readResponse(); } catch (RequestException e) { // The attempt to interpret the request failed. Give up. throw e.getCause(); } catch (RouteException e) { // The attempt to connect via a route failed. The request will not have been sent. HttpEngine retryEngine = engine.recover(e); //重试引擎 if (retryEngine != null) { engine = retryEngine; continue; } // Give up; recovery is not possible. throw e.getLastConnectException(); } catch (IOException e) { // An attempt to communicate with a server failed. The request may have been sent. HttpEngine retryEngine = engine.recover(e, null); if (retryEngine != null) { engine = retryEngine; continue; } // Give up; recovery is not possible. throw e; } .......
在这段代码中,
OkHttp建立一个
HttpEngine对象来负责
Http层级的请求的发送和回复的接收,
HttpEngine会在之后的博文中详细讲解。然后进入了一个
while循环,这个循环其实主要是处理重定向问题的。我们在这一节中主要关注
catch中的逻辑,这是用于处理出错重试的逻辑。由于外层有一个
while循环,所以在
catch中尝试获得
retryEngine,如果有就
continue,没有就抛出异常。
重定向处理
Response response = engine.getResponse(); // followUp这个是优化http connection的使用率的吗? Request followUp = engine.followUpRequest(); if (followUp == null) { if (!forWebSocket) { //如果没有followup并且不是为了websocket engine.releaseConnection();//关闭连接 } return response; } if (++followUpCount > MAX_FOLLOW_UPS) { throw new ProtocolException("Too many follow-up requests: " + followUpCount); } if (!engine.sameConnection(followUp.httpUrl())) { //如果followup的httpUrl不是同一个连接,也就是 //schema,host or port 有一个不同 engine.releaseConnection(); } //复用了上一次的connection啊!!!! Connection connection = engine.close(); request = followUp; //继续处理,有可能是重定向啦 engine = new HttpEngine(client, request, false, false, forWebSocket, connection, null, null, response);
这里我们可以看到
Http重定向的机制。
Request request = engine.followUpRequest()来获得重定向需要发送的
Request,如果没有或者重定向次数大于
MAX_FOLLOW_UPS就不会重新发送重定向请求。然后判断重定向请求和原请求的HttpUrl是否相同,否则也不会发送重定向请求。然后
Connection connection = engine.close()会释放资源并且复用上次的连接,然后新建一个
HttpEngine然后继续
While循环发送请求。
重定向状态码解析
public Request followUpRequest() throws IOException { if (userResponse == null) throw new IllegalStateException(); Proxy selectedProxy = getRoute() != null ? getRoute().getProxy() : client.getProxy(); int responseCode = userResponse.code(); switch (responseCode) { case HTTP_PROXY_AUTH: //407 Proxy authentication required 要先经过代理服务器认证 if (selectedProxy.type() != Proxy.Type.HTTP) { throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy"); } // fall-through case HTTP_UNAUTHORIZED: //401 没有身份认证 return OkHeaders.processAuthHeader(client.getAuthenticator(), userResponse, selectedProxy); case HTTP_PERM_REDIRECT:// 308 case HTTP_TEMP_REDIRECT: //307 // "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 (!userRequest.method().equals("GET") && !userRequest.method().equals("HEAD")) { return null; } //如果不是get和head 那么就不能自动转发 // fall-through case HTTP_MULT_CHOICE: //300 case HTTP_MOVED_PERM:// 301 case HTTP_MOVED_TEMP://302 case HTTP_SEE_OTHER: //303 // Does the client allow redirects? if (!client.getFollowRedirects()) return null;//如果不允许重定向 String location = userResponse.header("Location");//从response的头部获得的location if (location == null) return null; HttpUrl url = userRequest.httpUrl().resolve(location);//使用request的解析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(userRequest.httpUrl().scheme()); if (!sameScheme && !client.getFollowSslRedirects()) return null; // Redirects don't include a request body. Request.Builder requestBuilder = userRequest.newBuilder(); if (HttpMethod.permitsRequestBody(userRequest.method())) { requestBuilder.method("GET", 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(url)) { requestBuilder.removeHeader("Authorization"); } return requestBuilder.url(url).build(); default: return null; }
这一段就是根据回复的状态码生成重定向请求的代码逻辑。
HTTP_PROXY_AUTH 407 表示需要经过代理服务器认证 ,这时抛出异常,不进行重定向
HTTP_UNAUTHORIZED 401 身份未认证
HTTP_PERM_REDIRECT 308 HTTP_TEMP_REDIRECT 307 这两种状态码时,只有当请求的
method不为
GET和
HEAD时不进行重定向,否则按照下边一列状态码的方式处理
HTTP_MULT_CHOICE 300 HTTP_MOVED_PERM 301 HTTP_MOVED_TEMP 302 HTTP_SEE_OTHER 303 当是这些状态码时,先判断是否运行重定向,然后获得
Response中的
Location首部的值,然后用
HttpUrl去解析,如果是
host不同,那么去掉所有的认证首部,这是为了安全。
结语
今天所总结的只是Http的重定向部分和
OkHttp中的关于重定向的逻辑部分。之后会陆陆续续的继续总结关于
Http的知识。
本文也在我的独立博文中同时发布点我
相关文章推荐
- Linux基本网络命令
- 网络原因导致 npm 软件包 node-sass / gulp-sass 安装失败的处理办法
- 调度策略 http://book.2cto.com/201302/16285.html
- Linux进程调度概述 http://blog.sina.com.cn/s/blog_682686610100qqi3.html
- android:使用网络通信技术从客户端直接获取服务端的对象数据
- XMLHttpRequest对象的属性
- 数据库与MATLAB链接http://blog.sina.com.cn/s/blog_9d0b00a401012spy.html
- VirtualBox虚拟机网络设置(四种方式)
- 【转载】TCP数据包实例解析
- 【总结】【Unix网络编程】前言及基础知识
- webrtc 学习资源 http://www.cnblogs.com/lingyunhu/p/3578218.html
- XMLHttpRequest对象的方法
- TCP/IP四层模型
- 网络监测
- TCP的那些事
- 使用httperrequest,模拟发送及接收Json请求
- iOS网络构架 与 web服务器 (三次握手)
- 网络吞吐量
- 判断网络状态
- http协议介绍(servlet)