<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    John Jiang

    a cup of Java, cheers!
    https://github.com/johnshajiang/blog

       :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
      131 隨筆 :: 1 文章 :: 530 評(píng)論 :: 0 Trackbacks
    探索HTTP/2: 初試HTTP/2
    目前支持HTTP/2的服務(wù)器端與客戶端實(shí)現(xiàn)已有不少,探索HTTP/2系列的第二篇就分別以Jetty和curl作為服務(wù)器端和客戶端,描述了HTTP/2測(cè)試環(huán)境的搭建過程。本文還將使用這個(gè)測(cè)試環(huán)境去展示Jetty在實(shí)現(xiàn)HTTP/2時(shí)的一個(gè)局限和一個(gè)Bug。(2016.09.22最后更新)

    1. HTTP/2的實(shí)現(xiàn)
        目前已經(jīng)有眾多的服務(wù)器端和客戶端實(shí)現(xiàn)了對(duì)HTTP/2的支持。在服務(wù)器端,著名的Apache httpd從2.4.17版,Nginx從1.9.5版,開始支持HTTP/2。在客戶端,主流的瀏覽器,如Chrome,F(xiàn)ireFox和IE,的最新版均支持HTTP/2,但它們都只支持運(yùn)行在TLS上的HTTP/2(即h2)。使用Java語言實(shí)現(xiàn)的,則有Jetty和Netty,它們都實(shí)現(xiàn)了服務(wù)器端和客戶端。此處有一份HTTP/2實(shí)現(xiàn)的列表:https://github.com/http2/http2-spec/wiki/Implementations
        另外,還有一些工具支持對(duì)HTTP/2的分析與調(diào)試,如curl和WireShark。這里也有一份此類工具的列表:https://github.com/http2/http2-spec/wiki/Tools

    2. 服務(wù)器端
        作為Java程序員,選用一款使用Java語言編寫的開源HTTP/2服務(wù)器端實(shí)現(xiàn)似乎是很自然的結(jié)果。實(shí)際上,在日后的研究中,我們也需要查看服務(wù)器端的源代碼。這對(duì)于深入地理解HTTP/2,并發(fā)現(xiàn)實(shí)現(xiàn)中可能的問題,具有現(xiàn)實(shí)意義。
        本文選擇Jetty的最新版本9.3.11作為服務(wù)器端。Jetty是一個(gè)成熟的Servlet容器,這為開發(fā)Web應(yīng)用程序提供了極大便利。而本文第1節(jié)中提到的Netty是一個(gè)傳輸層框架,它專注于網(wǎng)絡(luò)程序??梢允褂肗etty去開發(fā)一個(gè)Servlet容器,但這顯然不如直接使用Jetty方便。
        安裝和配置Jetty是一件很容易的事情,具體過程如下所示。
        假設(shè)此時(shí)已經(jīng)下載并解壓好了Jetty 9.3.11的壓縮文件,目錄名為jetty-9.3.11。在其中創(chuàng)建一個(gè)test-base子目錄,作為將要?jiǎng)?chuàng)建的Jetty Base的目錄。
    $ cd jetty-9.3.11
    $ mkdir test-base
    $ cd test-base
    在創(chuàng)建Base時(shí),加入支持http,https,http2(h2),http2c(h2c)和deploy的模塊。
    $ java -jar ../start.jar --add-to-startd=http,https,http2,http2c,deploy

    ALERT: There are enabled module(s) with licenses.
    The following 1 module(s):
     + contains software not provided by the Eclipse Foundation!
     + contains software not covered by the Eclipse Public License!
     + has not been audited for compliance with its license

     Module: alpn
      + ALPN is a hosted at github under the GPL v2 with ClassPath Exception.
      + ALPN replaces/modifies OpenJDK classes in the java.sun.security.ssl package.
      + http://github.com/jetty-project/jetty-alpn
      + http://openjdk.java.net/legal/gplv2+ce.html

    Proceed (y/N)? y
    INFO: server          initialised (transitively) in ${jetty.base}\start.d\server.ini
    INFO: http            initialised in ${jetty.base}\start.d\http.ini
    INFO: ssl             initialised (transitively) in ${jetty.base}\start.d\ssl.ini
    INFO: alpn            initialised (transitively) in ${jetty.base}\start.d\alpn.ini
    INFO: http2c          initialised in ${jetty.base}\start.d\http2c.ini
    INFO: https           initialised in ${jetty.base}\start.d\https.ini
    INFO: deploy          initialised in ${jetty.base}\start.d\deploy.ini
    INFO: http2           initialised in ${jetty.base}\start.d\http2.ini
    DOWNLOAD: http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.5.v20150921/alpn-boot-8.1.5.v20150921.jar to ${jetty.base}\lib\alpn\alpn-boot-8.1.5.v20150921.jar
    DOWNLOAD: https://raw.githubusercontent.com/eclipse/jetty.project/master/jetty-server/src/test/config/etc/keystore?id=master to ${jetty.base}\etc\keystore
    MKDIR: ${jetty.base}\webapps
    INFO: Base directory was modified
        注意,在上述過程中,會(huì)根據(jù)當(dāng)前環(huán)境變量中使用的Java版本(此處為1.8.0_60)去下載一個(gè)對(duì)應(yīng)的TLS-ALPN實(shí)現(xiàn)jar文件(此處為alpn-boot-8.1.5.v20150921.jar),該jar會(huì)用于對(duì)h2的支持。當(dāng)啟動(dòng)Jetty時(shí),該jar會(huì)被Java的Bootstrap class loader加載到類路徑中。
        創(chuàng)建一個(gè)最簡(jiǎn)單的Web應(yīng)用,使它在根目錄下包含一個(gè)文本文件index,內(nèi)容為"HTTP/2 Test"。再包含一個(gè)簡(jiǎn)單的Servlet,代碼如下:
    package test;

    import java.io.IOException;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    public class TestServlet extends HttpServlet {

        
    private static final long serialVersionUID = 5222793251610509039L;

        @Override
        
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                
    throws ServletException, IOException {
            response.getWriter().println("Test");
        }

        @Override
        
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                
    throws ServletException, IOException {
            doGet(request, response);
        }
    }
    web.xml主要是定義了一個(gè)Servlet,具體內(nèi)容如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
        metadata-complete="false" version="3.1">

        
    <welcome-file-list>
            
    <welcome-file>index</welcome-file>
        
    </welcome-file-list>

        
    <servlet>
            
    <servlet-name>test</servlet-name>
            
    <servlet-class>test.TestServlet</servlet-class>
        
    </servlet>
        
    <servlet-mapping>
            
    <servlet-name>test</servlet-name>
            
    <url-pattern>/test/*</url-pattern>
        
    </servlet-mapping>
    </web-app>
    該應(yīng)用的部署路徑為jetty-9.3.11/test-base/webapps/test.war。在該WAR文件所在的目錄下,創(chuàng)建一個(gè)test.xml,其內(nèi)容如下所示:
    <?xml version="1.0"  encoding="ISO-8859-1"?>
    <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">

    <Configure class="org.eclipse.jetty.webapp.WebAppContext">
      
    <Set name="contextPath">/</Set>
      
    <Set name="war"><SystemProperty name="jetty.base" default="."/>/webapps/test.war</Set>
    </Configure>
    啟動(dòng)Jetty服務(wù)器,使用默認(rèn)的HTTP和HTTPS端口,分別為8080和8443。
    $ java -jar ../start.jar
    2016-09-15 21:15:51.190:INFO:oejs.Server:main: jetty-9.3.11.v20160721
    2016-09-15 21:15:51.237:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/] at interval 1
    2016-09-15 21:15:52.251:INFO:oejw.StandardDescriptorProcessor:main: NO JSP Support for /test.war, did not find org.eclipse.jetty.jsp.JettyJspServlet
    2016-09-15 21:15:52.313:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@4520ebad{/test.war,file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/test.war/,AVAILABLE}{D:\http2\jetty\jetty-9.3.11\test-base\webapps\test.war}
    2016-09-15 21:15:52.391:INFO:oejw.StandardDescriptorProcessor:main: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
    2016-09-15 21:15:52.391:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@711f39f9{/,file:///D:/http2/jetty/jetty-9.3.11/test-base/webapps/test.war/,AVAILABLE}{/test.war}
    2016-09-15 21:15:52.532:INFO:oejs.AbstractConnector:main: Started ServerConnector@1b68ddbd{HTTP/1.1,[http/1.1, h2c, h2c-17, h2c-16, h2c-15, h2c-14]}{0.0.0.0:8080}
    2016-09-15 21:15:52.735:INFO:oejus.SslContextFactory:main: x509=X509@e320068(jetty,h=[jetty.eclipse.org],w=[]) for SslContextFactory@1f57539(file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore,file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore)
    2016-09-15 21:15:52.735:INFO:oejus.SslContextFactory:main: x509=X509@76f2b07d(mykey,h=[],w=[]) for SslContextFactory@1f57539(file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore,file:///D:/http2/jetty/jetty-9.3.11/test-base/etc/keystore)
    2016-09-15 21:15:53.234:INFO:oejs.AbstractConnector:main: Started ServerConnector@4b168fa9{SSL,[ssl, alpn, h2, h2-17, h2-16, h2-15, h2-14, http/1.1]}{0.0.0.0:8443}
    2016-09-15 21:15:53.249:INFO:oejs.Server:main: Started @3940ms
        根據(jù)上述日志可知,Jetty啟用了Web應(yīng)用test.war,還啟動(dòng)了兩個(gè)ServerConnector,一個(gè)支持h2c,另一個(gè)支持h2。值得注意的是,這兩個(gè)ServerConnector還分別支持h2c-17, h2c-16, h2c-15, h2c-14和h2-17, h2-16, h2-15, h2-14。這是因?yàn)椋琀TTP/2在正式發(fā)布之前,先后發(fā)布了18個(gè)草案,其編號(hào)為00-17。所以,這里的h2c-XX和h2-XX指的就是第XX號(hào)草案。

    3. 客戶端
        其實(shí)最方便的客戶端就是瀏覽器了。只要使用的FireFox或Chrome版本不是太老,肯定都已經(jīng)支持了HTTP/2,而且這一功能是默認(rèn)打開的。也就是說,當(dāng)使用FireFox去訪問前面所部署的Web應(yīng)用時(shí),就是在使用HTTP/2,但你不會(huì)感覺到這種變化。使用FireFox提供的Developer Tools中的Network工具查看服務(wù)器端的響應(yīng),會(huì)發(fā)現(xiàn)HTTP版本為HTTP/2.0。但此處希望這個(gè)客戶端能夠提供更為豐富的與服務(wù)器端進(jìn)行交互的功能,那么瀏覽器就并不合適了。
        Jetty也實(shí)現(xiàn)了支持HTTP/2的客戶端,但這個(gè)客戶端是一個(gè)API,需要編寫程序去訪問HTTP/2服務(wù)器端。而且,目前該API的設(shè)計(jì)抽象層次較低,需要應(yīng)用程序員對(duì)HTTP/2協(xié)議,比如各種幀,有較深入的了解。這對(duì)于初涉HTTP/2的開發(fā)者來說,顯然很不合適。本文選擇使用C語言編寫的一個(gè)工具,其實(shí)也是HTTP/2的客戶端實(shí)現(xiàn)之一,curl。

        curl在支持HTTP/2時(shí),實(shí)際上是使用了nghttp2的C庫,所以需要先安裝nghttp2。另外,為了讓curl支持h2,就必須要有TLS-ALPN的支持。那么,一般地還需要安裝OpenSSL 1.0.2+。
        網(wǎng)絡(luò)上關(guān)于在Linux下安裝支持HTTP/2的curl的資源有很多,過程并不難,但有點(diǎn)兒繁,要安裝的依賴比較多,本文就不贅述了。如果是使用Windows,筆者比較推薦通過Cygwin來安裝和使用curl。在Windows中安裝Cygwin非常簡(jiǎn)單,在Cygwin中執(zhí)行各種命令時(shí),感覺上就如同在使用Linux,盡管它并不是一個(gè)虛擬機(jī)。通過Cygwin安裝curl,它會(huì)自動(dòng)地安裝所需的各種依賴程序和庫。
        在筆者的機(jī)器上,通過查看curl的版本會(huì)出現(xiàn)如下信息:
    curl 7.50.2 (x86_64-unknown-cygwin) libcurl/7.50.2 OpenSSL/1.0.2h zlib/1.2.8 libidn/1.29 libpsl/0.14.0 (+libidn/1.29) libssh2/1.7.0 nghttp2/1.14.0
    Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
    Features: Debug IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets Metalink PSL
    由上可知,筆者使用的curl版本是7.50.2,nghttp2版本是1.14.0,而OpenSSL版本是1.0.2h。

    4. 第一次嘗試
        在第一次嘗試中,只需要簡(jiǎn)單地訪問第2節(jié)中部署的Web應(yīng)用中的靜態(tài)文本文件index,以感受下h2c,完整命令如下:
    $ curl -v --http2 http://localhost:8080/index
    在輸出中包含有如下的內(nèi)容:
    ...
    > GET /index HTTP/1.1
    > Host: localhost:8080
    > User-Agent: curl/7.50.2
    > Accept: */*
    > Connection: Upgrade, HTTP2-Settings
    > Upgrade: h2c
    > HTTP2-Settings: AAMAAABkAAQAAP__
    >
    ...
    < HTTP/1.1 101 Switching Protocols
    * Received 101
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    ...
    < HTTP/2 200
    < server: Jetty(9.3.11.v20160721)
    < last-modified: Wed, 14 Sep 2016 12:52:32 GMT
    < content-length: 11
    < accept-ranges: bytes
    <
    ...
    HTTP/2 Test
    ">"是客戶端發(fā)送的請(qǐng)求,"<"是服務(wù)器端發(fā)送的響應(yīng),而"*"是curl對(duì)當(dāng)前過程的說明。結(jié)合本系列第一篇文章中所簡(jiǎn)述的HTTP 2協(xié)議,可以有以下的基本理解。
    [1]客戶端發(fā)起了一個(gè)HTTP/1.1的請(qǐng)求,其中攜帶有Upgrade頭部,要求服務(wù)器端升級(jí)到HTTP/2(h2c)。
    > GET /index HTTP/1.1
    > Host: localhost:8080
    > User-Agent: curl/7.50.2
    > Accept: */*
    > Connection: Upgrade, HTTP2-Settings
    > Upgrade: h2c
    > HTTP2-Settings: AAMAAABkAAQAAP__
    >
    [2]服務(wù)器端同意升級(jí),返回響應(yīng)"101 Switching Protocols",然后客戶端收到了101響應(yīng),HTTP/2連接進(jìn)行確認(rèn)。
    < HTTP/1.1 101 Switching Protocols
    * Received 101
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    [3]服務(wù)器端響應(yīng)最終結(jié)果。狀態(tài)行中出現(xiàn)的HTTP版本為HTTP/2,狀態(tài)代碼為200,且后面沒有跟著"OK"。最后輸出了index文件的內(nèi)容"HTTP/2 Test"。
    < HTTP/2 200
    < server: Jetty(9.3.11.v20160721)
    < last-modified: Wed, 14 Sep 2016 12:52:32 GMT
    < content-length: 11
    < accept-ranges: bytes
    <
    ...
    HTTP/2 Test

    5. 一個(gè)局限
        這次,在發(fā)起的請(qǐng)求中包含體部,命令如下:
    $ curl -v --http2 -d "body" http://localhost:8080/index
    在輸出中包含有如下的內(nèi)容:
    ...
    > POST /index HTTP/1.1
    > Host: localhost:8080
    > User-Agent: curl/7.50.2
    > Accept: */*
    > Connection: Upgrade, HTTP2-Settings
    > Upgrade: h2c
    > HTTP2-Settings: AAMAAABkAAQAAP__
    > Content-Length: 4
    > Content-Type: application/x-www-form-urlencoded
    >
    ...
    < HTTP/1.1 200 OK
    < Last-Modified: Wed, 14 Sep 2016 12:52:32 GMT
    < Accept-Ranges: bytes
    < Content-Length: 11
    ...
    HTTP/2 Test
        和第4節(jié)中的輸出進(jìn)行比較,會(huì)發(fā)現(xiàn)缺少了"101 Switching Protocols"那一段,而且最終響應(yīng)狀態(tài)行中出現(xiàn)的HTTP版本是HTTP/1.1。這就說明服務(wù)器端不同意升級(jí),后面繼續(xù)使用HTTP/1.1。剛剛部署的Jetty未做任何改變?cè)趺磿?huì)突然不支持HTTP/2了呢?或者這是curl的問題?其實(shí),這是因?yàn)镴etty服務(wù)器端在實(shí)現(xiàn)h2c時(shí)不支持請(qǐng)求中包含體部。另外,Apache httpd也有同樣的問題。如果是使用h2,則沒有這個(gè)限制。這背后的原因超出了本文的范疇,不作表述。

    6. 一個(gè)Bug
        在這次嘗試中,測(cè)試一下兩端對(duì)100-continue的支持。如果請(qǐng)求中使用了頭部"Expect: 100-continue",那么正常地該請(qǐng)求要有體部。但由于在第5節(jié)中介紹的問題,此時(shí)不能再使用h2c,而只能使用h2。另外,這次不訪問靜態(tài)文件,而是訪問Servlet(此處為/test)。完整命令如下:
    $ curl -vk --http2 -H "Expect: 100-continue" -d "body" https://localhost:8443/test
    在輸出的最后出現(xiàn)了如下信息:
    curl: (92) HTTP/2 stream 1 was not closed cleanly: CANCEL (err 8)
    這其實(shí)是Jetty的一個(gè)Bug,正在開發(fā)中的9.3.12已經(jīng)修復(fù)了它。

    7. 小結(jié)
        HTTP/2依然算是新潮的技術(shù),對(duì)各家的實(shí)現(xiàn),無論是服務(wù)器端,客戶端,還是分析工具,都要持有一份懷疑態(tài)度。這些實(shí)現(xiàn)和工具都是程序,都有可能存在bug。而且協(xié)議對(duì)許多細(xì)節(jié)沒有作出規(guī)定,各家都會(huì)發(fā)揮自己的想像力。比如,Apache httpd和Jetty在實(shí)現(xiàn)服務(wù)器端推送時(shí),其方式就不盡相同。
        在開發(fā)自己的HTTP/2實(shí)現(xiàn)或應(yīng)用的時(shí)候,需要同時(shí)使用已有的不同服務(wù)器端和客戶端去部署多套測(cè)試環(huán)境進(jìn)行對(duì)比分析。
    posted on 2016-09-20 16:42 John Jiang 閱讀(4453) 評(píng)論(1)  編輯  收藏 所屬分類: Java原創(chuàng)HTTP/2探索HTTP/2

    評(píng)論

    # re: 探索HTTP/2: 初試HTTP/2(原) 2016-09-22 10:54 John Jiang
    Jetty剛剛發(fā)布了9.3.12,其中就包含了對(duì)本文中提到的100-continue問題的修復(fù)。  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 九九精品国产亚洲AV日韩| 亚洲第一页在线播放| 久久亚洲精品AB无码播放| 久久精品国产亚洲AV麻豆网站| 亚洲经典在线观看| 亚洲中文无码亚洲人成影院| jizzjizz亚洲日本少妇| 国产精品九九久久免费视频| 性色午夜视频免费男人的天堂| 国产一卡二卡3卡四卡免费| 国产又粗又长又硬免费视频| 久久久久无码专区亚洲av| 鲁丝片一区二区三区免费| 国产高清免费视频| 免费观看国产小粉嫩喷水| 亚洲精品无码高潮喷水在线| 亚洲性色高清完整版在线观看| 亚洲av无码专区在线观看亚| 精品一区二区三区高清免费观看 | 亚洲色偷偷偷网站色偷一区| 亚洲熟妇丰满xxxxx| 在线免费视频你懂的| 亚洲视频免费一区| 免费在线观看理论片| 亚洲天堂视频在线观看| 亚洲精品成a人在线观看夫| 国产在线观a免费观看| 国拍在线精品视频免费观看| 亚洲欧洲一区二区三区| 亚洲精品美女视频| 污视频网站在线免费看| 4虎1515hh永久免费| 亚洲VA综合VA国产产VA中| 国产成人免费一区二区三区| 亚洲成AV人在线观看天堂无码| 亚洲乱人伦中文字幕无码| 国内精品免费视频精选在线观看 | 中文字幕人成人乱码亚洲电影| 亚洲国产成人久久| 国产成人无码精品久久久久免费 | 久久免费看黄a级毛片|