Okhttp对http2的支持简单分析
来源:互联网 发布:漫威电影观看顺序 知乎 编辑:程序博客网 时间:2024/06/12 14:55
在《 Okhttp之RealConnection建立链接简单分析》一文中简单的分析了RealConnection的connect方法的作用:打开一个TCP链接或者打开一个隧道链接,在打开tcp链接之后就调用establishProtocol,本篇主要是对此方法来进行分析:
*/ private void establishProtocol(ConnectionSpecSelector connectionSpecSelector) throws IOException { //不是https请求即普通的http请求,那么协议定义为http/1.1 if (route.address().sslSocketFactory() == null) { protocol = Protocol.HTTP_1_1; socket = rawSocket; return; } //https请求 //使用SSLSocket,同时会赋值protocol和socket,主要用来判断服务器是否支持http2.0 connectTls(connectionSpecSelector); //对http2的支持 if (protocol == Protocol.HTTP_2) { socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream. http2Connection = new Http2Connection.Builder(true) .socket(socket, route.address().url().host(), source, sink) .listener(this) .build(); http2Connection.start(); } }
大体上看该方法主要做了如下工作:
1、如果是http请求的话,就将协议protocol定义为http/1.1
2、如果是https的请求的话,就调用connectTls创建一个SSLSocket,然后通过一些列的方法调用(比如三次握手啦之类的),初始化protocol:关键代码如下:
sslSocket = (SSLSocket) sslSocketFactory.createSocket( rawSocket, address.url().host(), address.url().port(), true /* autoClose */); // Configure the socket's ciphers, TLS versions, and extensions. ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket); if (connectionSpec.supportsTlsExtensions()) { Platform.get().configureTlsExtensions( sslSocket, address.url().host(), address.protocols()); } // Force handshake. This can throw! sslSocket.startHandshake(); Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession()); //////////////分割线////////////////////// String maybeProtocol = connectionSpec.supportsTlsExtensions() ? Platform.get().getSelectedProtocol(sslSocket) : null; //socket为sslSocket socket = sslSocket; //将输入输出流交给Okio source = Okio.buffer(Okio.source(socket)); sink = Okio.buffer(Okio.sink(socket)); protocol = maybeProtocol != null ? Protocol.get(maybeProtocol) : Protocol.HTTP_1_1;
为什么是https的话就开始判断是否是http2呢?其实是因为http2就目前来说在实际使用中,只用于https协议场景下,通过握手阶段extension字段协商而来,当然HTTP2.0其实可以支持非HTTPS的(参考资料),但是现在主流的浏览器像chrome,firefox表示还是只支持基于 TLS 部署的HTTP2.0协议,所以要想升级成HTTP2.0还是先升级HTTPS为好(参考资料)。而上面的代码分割线以上的部门就是做了这这些协商工作。
分割线一下的部分就是对协议的初始化了。其实,如果打印Okhttp的response对象的toString(),会发现其协议版本也在里面体现出来:Response{protocol=http/1.1, code=200, message=OK, url=http://www.xxx.com/}
3、最后就进入了对http2的处理。
总的来说establishProtocol方法的作用查询服务器对http版本的支持情况,如果是http2协议的服务器,就交给http2Connection 来处理请求。
当协商完成后客户端和服务端就可以发送所谓的链接序言了,且该序列后跟着一个设置帧(SETTING),其可为空帧,什么是链接序言呢?用字符串表示的话就是:字符串PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)
那么okhttp是怎么发送序言的呢?追踪下Http2Connection的start方法最终进入:
void start(boolean sendConnectionPreface) throws IOException { if (sendConnectionPreface) { //发送链接序言 writer.connectionPreface(); //发送setting帧 writer.settings(okHttpSettings); //省略部分代码 } //开启线程执行readerRunnable new Thread(readerRunnable).start(); // Not a daemon thread. }
上面的代码connectionPreface其实就是发送链接序言的:
static final ByteString CONNECTION_PREFACE = ByteString.encodeUtf8("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"); public synchronized void connectionPreface() throws IOException { //发送链接序言 sink.write(CONNECTION_PREFACE.toByteArray()); sink.flush(); }
HTTP/2中基本的协议单位是帧。每个帧都有不同的类型和用途。 例如,报头(HEADERS)和数据(DATA)帧组成了基本的HTTP 请求和响应;其他帧例如 设置(SETTINGS)、窗口更新(WINDOW_UPDATE)和推送承诺(PUSH_PROMISE) 是用来实现HTTP/2的其他功能(摘自《http/2.0中文翻译》。
而Okhttp2对也是严格按照这些帧的种类来进行数据的读取和解析:具体在readerRunnable的execute方法:
protected void execute() { //省略了部分代码,比如try cath reader.readConnectionPreface(this); //不断读取http2.0的数据帧 while (reader.nextFrame(false, this)) { } } }
其核心逻辑也就是在Http2Reader的nextFrame中:
public boolean nextFrame(boolean requireSettings, Handler handler) throws IOException { try { source.require(9); // Frame header size } catch (IOException e) { return false; // This might be a normal socket close. } //省略了部分代码 switch (type) { case TYPE_DATA: readData(handler, length, flags, streamId); break; case TYPE_HEADERS: readHeaders(handler, length, flags, streamId); break; case TYPE_PRIORITY: readPriority(handler, length, flags, streamId); break; case TYPE_RST_STREAM: readRstStream(handler, length, flags, streamId); break; case TYPE_SETTINGS: readSettings(handler, length, flags, streamId); break; case TYPE_PUSH_PROMISE: readPushPromise(handler, length, flags, streamId); break; case TYPE_PING: readPing(handler, length, flags, streamId); break; case TYPE_GOAWAY: readGoAway(handler, length, flags, streamId); break; case TYPE_WINDOW_UPDATE: readWindowUpdate(handler, length, flags, streamId); break; default: // Implementations MUST discard frames that have unknown or unsupported types. source.skip(length); } return true; }
到此为止,本篇博文简单解析完毕,至于对每一帧怎么处理以及每个帧的含义可以参考《Http2.0中文解析》。在这里就不做大篇幅的说明了(因为某些具体的处理细节我还没高透彻)
- Okhttp对http2的支持简单分析
- nginx启用对HTTP2的支持
- nginx如何启用对HTTP2的支持 | nginx如何验证HTTP2是否已启用
- http2概述及Java9对其的支持
- 对okhttp的简单封装
- 基于OKHttp实现对Https的支持
- okhttp源码的简单分析
- 对HTTP2.0的了解
- 关于对OKHttp的简单使用
- 如何让Wireshark支持(加密的HTTP2等)HTTPS报文分析
- OkHttp之BridgeInterceptor简单分析
- Okhttp之CacheInterceptor简单分析
- OkHttp之ConnectInterceptor简单分析
- Okhttp之CallServerInterceptor简单分析
- 对Okhttp的封装Okhttp-utils
- OkHttp的简单使用
- OKHttp的简单使用
- okhttp简单的应用
- PHP代码审计CSRF 跨站请求伪造
- 多线程端口扫描
- 准确率(Accuracy), 精确率(Precision), 召回率(Recall)和F1-Measure
- Java-9-简单的OKHttp使用
- poj1661 Help Jimmy-dp动态规划 (M)
- Okhttp对http2的支持简单分析
- js里面在对象里面属性可以写对象类型吗
- HDU
- 从mysql获取数据方法的封装
- 实践spring data jpa:Executing an update/delete query........
- 概率DP 换教室
- Day07
- git命令学习
- No.3 平衡二叉树