<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

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

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

    2. 服務器端
        作為Java程序員,選用一款使用Java語言編寫的開源HTTP/2服務器端實現似乎是很自然的結果。實際上,在日后的研究中,我們也需要查看服務器端的源代碼。這對于深入地理解HTTP/2,并發現實現中可能的問題,具有現實意義。
        本文選擇Jetty的最新版本9.3.11作為服務器端。Jetty是一個成熟的Servlet容器,這為開發Web應用程序提供了極大便利。而本文第1節中提到的Netty是一個傳輸層框架,它專注于網絡程序。可以使用Netty去開發一個Servlet容器,但這顯然不如直接使用Jetty方便。
        安裝和配置Jetty是一件很容易的事情,具體過程如下所示。
        假設此時已經下載并解壓好了Jetty 9.3.11的壓縮文件,目錄名為jetty-9.3.11。在其中創建一個test-base子目錄,作為將要創建的Jetty Base的目錄。
    $ cd jetty-9.3.11
    $ mkdir test-base
    $ cd test-base
    在創建Base時,加入支持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
        注意,在上述過程中,會根據當前環境變量中使用的Java版本(此處為1.8.0_60)去下載一個對應的TLS-ALPN實現jar文件(此處為alpn-boot-8.1.5.v20150921.jar),該jar會用于對h2的支持。當啟動Jetty時,該jar會被Java的Bootstrap class loader加載到類路徑中。
        創建一個最簡單的Web應用,使它在根目錄下包含一個文本文件index,內容為"HTTP/2 Test"。再包含一個簡單的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主要是定義了一個Servlet,具體內容如下:
    <?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>
    該應用的部署路徑為jetty-9.3.11/test-base/webapps/test.war。在該WAR文件所在的目錄下,創建一個test.xml,其內容如下所示:
    <?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>
    啟動Jetty服務器,使用默認的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
        根據上述日志可知,Jetty啟用了Web應用test.war,還啟動了兩個ServerConnector,一個支持h2c,另一個支持h2。值得注意的是,這兩個ServerConnector還分別支持h2c-17, h2c-16, h2c-15, h2c-14和h2-17, h2-16, h2-15, h2-14。這是因為,HTTP/2在正式發布之前,先后發布了18個草案,其編號為00-17。所以,這里的h2c-XX和h2-XX指的就是第XX號草案。

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

        curl在支持HTTP/2時,實際上是使用了nghttp2的C庫,所以需要先安裝nghttp2。另外,為了讓curl支持h2,就必須要有TLS-ALPN的支持。那么,一般地還需要安裝OpenSSL 1.0.2+。
        網絡上關于在Linux下安裝支持HTTP/2的curl的資源有很多,過程并不難,但有點兒繁,要安裝的依賴比較多,本文就不贅述了。如果是使用Windows,筆者比較推薦通過Cygwin來安裝和使用curl。在Windows中安裝Cygwin非常簡單,在Cygwin中執行各種命令時,感覺上就如同在使用Linux,盡管它并不是一個虛擬機。通過Cygwin安裝curl,它會自動地安裝所需的各種依賴程序和庫。
        在筆者的機器上,通過查看curl的版本會出現如下信息:
    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. 第一次嘗試
        在第一次嘗試中,只需要簡單地訪問第2節中部署的Web應用中的靜態文本文件index,以感受下h2c,完整命令如下:
    $ curl -v --http2 http://localhost:8080/index
    在輸出中包含有如下的內容:
    ...
    > 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
    ">"是客戶端發送的請求,"<"是服務器端發送的響應,而"*"是curl對當前過程的說明。結合本系列第一篇文章中所簡述的HTTP 2協議,可以有以下的基本理解。
    [1]客戶端發起了一個HTTP/1.1的請求,其中攜帶有Upgrade頭部,要求服務器端升級到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]服務器端同意升級,返回響應"101 Switching Protocols",然后客戶端收到了101響應,HTTP/2連接進行確認。
    < HTTP/1.1 101 Switching Protocols
    * Received 101
    * Using HTTP2, server supports multi-use
    * Connection state changed (HTTP/2 confirmed)
    [3]服務器端響應最終結果。狀態行中出現的HTTP版本為HTTP/2,狀態代碼為200,且后面沒有跟著"OK"。最后輸出了index文件的內容"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. 一個局限
        這次,在發起的請求中包含體部,命令如下:
    $ curl -v --http2 -d "body" http://localhost:8080/index
    在輸出中包含有如下的內容:
    ...
    > 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節中的輸出進行比較,會發現缺少了"101 Switching Protocols"那一段,而且最終響應狀態行中出現的HTTP版本是HTTP/1.1。這就說明服務器端不同意升級,后面繼續使用HTTP/1.1。剛剛部署的Jetty未做任何改變怎么會突然不支持HTTP/2了呢?或者這是curl的問題?其實,這是因為Jetty服務器端在實現h2c時不支持請求中包含體部。另外,Apache httpd也有同樣的問題。如果是使用h2,則沒有這個限制。這背后的原因超出了本文的范疇,不作表述。

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

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

    評論

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

    主站蜘蛛池模板: 亚洲AV成人一区二区三区AV| 亚洲综合亚洲综合网成人| 亚洲久本草在线中文字幕| 国产va免费观看| 亚洲精品久久久www| 四虎国产精品永免费| 亚洲毛片不卡av在线播放一区| 一级做a爰黑人又硬又粗免费看51社区国产精品视| 免费的一级黄色片| 免费在线观看亚洲| 久久久久无码专区亚洲av| 国产免费久久精品丫丫| 亚洲AV无码一区二区乱孑伦AS| 无码精品一区二区三区免费视频| 亚洲视频在线视频| 免费中文熟妇在线影片 | 国产精品免费久久久久久久久| 亚洲午夜爱爱香蕉片| 最近2019中文免费字幕在线观看| 亚洲国产综合无码一区| 2021在线观看视频精品免费| 亚洲人xxx日本人18| 全免费a级毛片免费看不卡| 无遮挡a级毛片免费看| 亚洲av最新在线网址| 永久免费av无码网站韩国毛片| 亚洲.国产.欧美一区二区三区| 中文国产成人精品久久亚洲精品AⅤ无码精品| 黄色视屏在线免费播放| 亚洲大尺码专区影院| 成人爱做日本视频免费| 免费91麻豆精品国产自产在线观看| 亚洲精品视频在线观看免费 | 亚洲一级黄色视频| **实干一级毛片aa免费| 国产成人精品亚洲一区| 久久精品国产亚洲av四虎| 免费高清av一区二区三区| a级片免费观看视频| 亚洲国产精品无码久久久秋霞1| 国产AV无码专区亚洲AWWW|