最後,這會歸並回到官方 Apache 文檔,這裡只會留下一個到那裡的鏈接。暫時我們還沒做到。
源碼
你可以從這裡得到 Apache 版本。Apache 2.4.17 及其更高版本都支持 HTTP/2。我不會再重復介紹如何構建該服務器的指令。在很多地方有很好的指南,例如這裡。
(有任何這個試驗性軟件包的相關鏈接?在 Twitter 上告訴我吧 @icing)
編譯支持 HTTP/2
在你編譯版本之前,你要進行一些配置。這裡有成千上萬的選項。和 HTTP/2 相關的是:
--enable-http2
啟用在 Apache 服務器內部實現該協議的 ‘http2’ 模塊。
--with-nghttp2=
指定 http2 模塊需要的 libnghttp2 模塊的非默認位置。如果 nghttp2 是在默認的位置,配置過程會自動采用。
--enable-nghttp2-staticlib-deps
很少用到的選項,你可能想將 nghttp2 庫靜態鏈接到服務器裡。在大部分平台上,只有在找不到共享 nghttp2 庫時才有用。
如果你想自己編譯 nghttp2,你可以到 nghttp2.org 查看文檔。最新的 Fedora 以及其它版本已經附帶了這個庫。
TLS 支持
大部分人想在浏覽器上使用 HTTP/2, 而浏覽器只在使用 TLS 連接(https:// 開頭的 url)時才支持 HTTP/2。你需要一些我下面介紹的配置。但首先你需要的是支持 ALPN 擴展的 TLS 庫。
ALPN 用來協商negotiate服務器和客戶端之間的協議。如果你服務器上 TLS 庫還沒有實現 ALPN,客戶端只能通過 HTTP/1.1 通信。那麼,可以和 Apache 鏈接並支持它的是什麼庫呢?
OpenSSL 1.0.2 及其以後。
??? (別的我也不知道了)
如果你的 OpenSSL 庫是 Linux 版本自帶的,這裡使用的版本號可能和官方 OpenSSL 版本的不同。如果不確定的話檢查一下你的 Linux 版本吧。
配置
另一個給服務器的好建議是為 http2 模塊設置合適的日志等級。添加下面的配置:
1 2 3 4 5 6# 放在某個地方的這樣一行 LoadModule http2_module modules/mod_http2.so LogLevel http2:info
當你啟動服務器的時候,你可以在錯誤日志中看來類似的一行:
1 2[timestamp] [http2:info] [pid XXXXX:tid numbers] mod_http2 (v1.0.0, nghttp2 1.3.4), initializing...
協議
那麼,假設你已經編譯部署好了服務器, TLS 庫也是最新的,你啟動了你的服務器,打開了浏覽器。。。你怎麼知道它在工作呢?
如果除此之外你沒有添加其它的服務器配置,很可能它沒有工作。
你需要告訴服務器在哪裡使用該協議。默認情況下,你的服務器並沒有啟動 HTTP/2 協議。因為這樣比較安全,也許才能讓你已有的部署可以繼續工作。
你可以用新的 Protocols 指令啟用 HTTP/2 協議:
1 2 3 4 5 6# 對於 https 服務器 Protocols h2 http/1.1 ... # 對於 http 服務器 Protocols h2c http/1.1
你可以給整個服務器或者指定的 vhosts 添加這個配置。
SSL 參數
對於 TLS (SSL),HTTP/2 有一些特殊的要求。閱讀下面的“ https:// 連接”一節了解更詳細的信息。
http:// 連接 (h2c)
盡管現在還沒有浏覽器支持,但是 HTTP/2 協議也工作在 http:// 這樣的 url 上, 而且 mod_h[ttp]2 也支持。啟用它你唯一所要做的是在 Protocols 配置中啟用它:
1 2# 對於 http 服務器 Protocols h2c http/1.1
這裡有一些支持 h2c 的客戶端(和客戶端庫)。我會在下面介紹:
curl
Daniel Stenberg 維護的用於訪問網絡資源的命令行客戶端 curl 當然支持。如果你的系統上有 curl,有一個簡單的方法檢查它是否支持 http/2:
1 2 3 4sh> curl -V curl 7.43.0 (x86_64-apple-darwin15.0) libcurl/7.43.0 SecureTransport zlib/1.2.5 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
不好了。這些功能中沒有 'HTTP2'。你想要的是下面這樣:
1 2 3 4sh> curl -V url 7.45.0 (x86_64-apple-darwin15.0.0) libcurl/7.45.0 OpenSSL/1.0.2d zlib/1.2.8 nghttp2/1.3.4 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets
如果你的 curl 支持 HTTP2 功能,你可以用一些簡單的命令檢查你的服務器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14sh> curl -v --http2 http:/// ... > Connection: Upgrade, HTTP2-Settings > Upgrade: h2c > HTTP2-Settings: AAMAAABkAAQAAP__ > < HTTP/1.1 101 Switching Protocols < Upgrade: h2c < Connection: Upgrade * Received 101 * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) ...
恭喜,如果看到了有 ...101 Switching... 的行就表示它正在工作!
有一些情況不會發生 HTTP/2 的升級切換Upgrade。如果你的第一個請求有請求數據body,例如你上傳一個文件時,就不會觸發升級切換。h2c 限制部分有詳細的解釋。
nghttp
nghttp2 可以一同編譯它自己的客戶端和服務器。如果你的系統中有該客戶端,你可以簡單地通過獲取一個資源來驗證你的安裝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15sh> nghttp -uv http:/// [ 0.001] Connected [ 0.001] HTTP Upgrade request ... Connection: Upgrade, HTTP2-Settings Upgrade: h2c HTTP2-Settings: AAMAAABkAAQAAP__ ... [ 0.005] HTTP Upgrade response HTTP/1.1 101 Switching Protocols Upgrade: h2c Connection: Upgrade [ 0.006] HTTP Upgrade success ...
這和我們上面 curl 例子中看到的 Upgrade 輸出很相似。
有另外一種在命令行參數中不用 -u 參數而使用 h2c 的方法。這個參數會指示 nghttp 進行 HTTP/1 升級切換過程。但如果我們不使用呢?
1 2 3 4 5 6 7 8 9 10 11 12sh> nghttp -v http:/// [ 0.002] Connected [ 0.002] send SETTINGS frame ... [ 0.002] send HEADERS frame ; END_STREAM | END_HEADERS | PRIORITY (padlen=0, dep_stream_id=11, weight=16, exclusive=0) ; Open new stream :method: GET :path: / :scheme: http ...
連接馬上使用了 HTTP/2!這就是協議中所謂的直接direct模式,當客戶端發送一些特殊的 24 字節到服務器時就會發生:
10x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
用 ASCII 表示是:
1PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
支持 h2c 的服務器在一個新的連接中看到這些信息就會馬上切換到 HTTP/2。HTTP/1.1 服務器則認為是一個可笑的請求,響應並關閉連接。
因此,直接模式只適合於那些確定服務器支持 HTTP/2 的客戶端。例如,當前一個升級切換過程成功了的時候。
直接模式的魅力是零開銷,它支持所有請求,即使帶有請求數據部分(查看h2c 限制)。
對於 2.4.17 版本,明文連接時默認啟用 H2Direct 。但是有一些模塊和這不兼容。因此,在下一版本中,默認會設置為off,如果你希望你的服務器支持它,你需要設置它為:
1H2Direct on
https:// 連接 (h2)
當你的 mod_h[ttp]2 可以支持 h2c 連接時,那就可以一同啟用 h2 兄弟了,現在的浏覽器僅支持它和https: 一同使用。
HTTP/2 標准對 https:(TLS)連接增加了一些額外的要求。上面已經提到了 ALNP 擴展。另外的一個要求是不能使用特定黑名單中的加密算法。
盡管現在版本的 mod_h[ttp]2 不增強這些算法(以後可能會),但大部分客戶端會這麼做。如果讓你的浏覽器使用不恰當的算法打開 h2 服務器,你會看到不明確的警告 INADEQUATE_SECURITY,浏覽器會拒接連接。
一個可行的 Apache SSL 配置類似:
1 2 3SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK SSLProtocol All -SSLv2 -SSLv3 ...
(是的,這確實很長。)
這裡還有一些應該調整,但不是必須調整的 SSL 配置參數:SSLSessionCache, SSLUseStapling等,其它地方也有介紹這些。例如 Ilya Grigorik 寫的一篇超贊的博客: 高性能浏覽器網絡。
curl
再次回到 shell 使用 curl(查看上面的“curl h2c”章節了解要求),你也可以通過 curl 用簡單的命令檢測你的服務器:
1 2 3 4 5 6 7 8sh> curl -v --http2 https:/// ... * ALPN, offering h2 * ALPN, offering http/1.1 ... * ALPN, server accepted to use h2 ...
恭喜你,能正常工作啦!如果還不能,可能原因是:
你的 curl 不支持 HTTP/2。查看上面的“檢測 curl”一節。
你的 openssl 版本太低不支持 ALPN。
不能驗證你的證書,或者不接受你的算法配置。嘗試添加命令行選項 -k 停用 curl 中的這些檢查。如果可以工作,就重新配置你的 SSL 和證書。
nghttp
我們已經在 h2c 討論過 nghttp。如果你用它來進行 https: 連接,你會看到類似下面的信息:
1 2sh> nghttp https:/// [ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
這有兩種可能,你可以通過添加 -v 來檢查。如果是:
1 2 3sh> nghttp -v https:/// [ 0.034] Connected [ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
這意味著你服務器使用的 TLS 庫沒有實現 ALPN。有時候正確安裝有點困難。多看看 Stackoverflow 吧。
你看到的也可能是:
1 2 3 4sh> nghttp -v https:/// [ 0.034] Connected The negotiated protocol: http/1.1 [ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
這表示 ALPN 能正常工作,但並沒有用 h2 協議。你需要像上面介紹的那樣檢查你服務器上的 Protocols 配置。如果一開始在 vhost 部分設置不能正常工作,試著在通用部分設置它。
Firefox
更新: Apache Lounge 的 Steffen Land 告訴我 Firefox 上有個 HTTP/2 指示插件。你可以看到有多少地方用到了 h2(提示:Apache Lounge 用 h2 已經有一段時間了...)
你可以在 Firefox 浏覽器中打開開發者工具,在那裡的網絡標簽頁查看 HTTP/2 連接。當你打開了 HTTP/2 並重新刷新 html 頁面時,你會看到類似下面的東西:
在響應頭中,你可以看到奇怪的 X-Firefox-Spdy 條目中列出了 “h2”。這表示在這個 https: 連接中使用了 HTTP/2。
Google Chrome
在 Google Chrome 中,你在開發者工具中看不到 HTTP/2 指示器。相反,Chrome 用特殊的地址chrome://net-internals/#http2 給出了相關信息。(LCTT 譯注:Chrome 已經有一個 “HTTP/2 and SPDY indicator” 可以很好的在地址欄識別 HTTP/2 連接)
如果你打開了一個服務器的頁面,可以在 Chrome 中查看那個 net-internals 頁面,你可以看到類似下面這樣:
如果你的服務器在上面的列表中,就表示它正在工作。
Microsoft Edge
Windows 10 中 Internet Explorer 的繼任者 Edge 也支持 HTTP/2。你也可以在開發者工具的網絡標簽頁看到 HTTP/2 協議。
Safari
在 Apple 的 Safari 中,打開開發者工具,那裡有個網絡標簽頁。重新加載你的服務器上的頁面,並在開發者工具中選擇顯示了加載的那行。如果你啟用了在右邊顯示詳細視圖,看 Status 部分。那裡顯示了HTTP/2.0 200,像這樣:
重新協商
https: 連接重新協商是指正在運行的連接中特定的 TLS 參數會發生變化。在 Apache httpd 中,你可以在 directory 配置中改變 TLS 參數。如果進來一個獲取特定位置資源的請求,配置的 TLS 參數會和當前的 TLS 參數進行對比。如果它們不相同,就會觸發重新協商。
這種最常見的情形是算法變化和客戶端證書。你可以要求客戶訪問特定位置時需要通過驗證,或者對於特定資源,你可以使用更安全的、對 CPU 壓力更大的算法。
但不管你的想法有多麼好,HTTP/2 中都不可以發生重新協商。在同一個連接上會有 100 多個請求,那麼重新協商該什麼時候做呢?
對於這種配置,現有的 mod_h[ttp]2 還沒有辦法。如果你有一個站點使用了 TLS 重新協商,別在上面啟用 h2!
當然,我們會在後面的版本中解決這個問題,然後你就可以安全地啟用了。
限制
非 HTTP 協議
實現除 HTTP 之外協議的模塊可能和 mod_http2 不兼容。這在其它協議要求服務器首先發送數據時無疑會發生。
NNTP 就是這種協議的一個例子。如果你在服務器中配置了 mod_nntp_like_ssl,那麼就不要加載 mod_http2。等待下一個版本。
h2c 限制
h2c 的實現還有一些限制,你應該注意:
在虛擬主機中拒絕 h2c
你不能對指定的虛擬主機拒絕 h2c 直連。連接建立而沒有看到請求時會觸發直連,這使得不可能預先知道 Apache 需要查找哪個虛擬主機。
有請求數據時的升級切換
對於有數據的請求,h2c 升級切換不能正常工作。那些是 PUT 和 POST 請求(用於提交和上傳)。如果你寫了一個客戶端,你可能會用一個簡單的 GET 或者 OPTIONS * 來處理那些請求以觸發升級切換。
原因從技術層面來看顯而易見,但如果你想知道:在升級切換過程中,連接處於半瘋狀態。請求按照 HTTP/1.1 的格式,而響應使用 HTTP/2 幀。如果請求有一個數據部分,服務器在發送響應之前需要讀取整個數據。因為響應可能需要從客戶端處得到應答用於流控制及其它東西。但如果仍在發送 HTTP/1.1 請求,客戶端就仍然不能以 HTTP/2 連接。
為了使行為可預測,幾個服務器在實現上決定不在任何帶有請求數據的請求中進行升級切換,即使請求數據很小。
302 時的升級切換
有重定向發生時,當前的 h2c 升級切換也不能工作。看起來 mod_http2 之前的重寫有可能發生。這當然不會導致斷路,但你測試這樣的站點也許會讓你迷惑。
h2 限制
這裡有一些你應該意識到的 h2 實現限制:
連接重用
HTTP/2 協議允許在特定條件下重用 TLS 連接:如果你有帶通配符的證書或者多個 AltSubject 名稱,浏覽器可能會重用現有的連接。例如:
你有一個 a.example.org 的證書,它還有另外一個名稱 b.example.org。你在浏覽器中打開 URLhttps://a.example.org/,用另一個標簽頁加載 https://b.example.org/。
在重新打開一個新的連接之前,浏覽器看到它有一個到 a.example.org 的連接並且證書對於b.example.org 也可用。因此,它在第一個連接上面發送第二個標簽頁的請求。
這種連接重用是刻意設計的,它使得使用了 HTTP/1 切分sharding來提高效率的站點能夠不需要太多變化就能利用 HTTP/2。
Apache mod_h[ttp]2 還沒有完全實現這點。如果 a.example.org 和 b.example.org 是不同的虛擬主機, Apache 不會允許這樣的連接重用,並會告知浏覽器狀態碼 421 Misdirected Request。浏覽器會意識到它需要重新打開一個到 b.example.org 的連接。這仍然能工作,只是會降低一些效率。
我們期望下一次的發布中能有合適的檢查。
Münster, 12.10.2015,
Stefan Eissing, greenbytes GmbH
Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. See LICENSE for details.
該項目由 icing 維護。