這是超文本傳輸協定版本 2 的 C 實作。
HTTP/2 的框架層是作為可重複使用的 C 函式庫實現的。最重要的是,我們實作了 HTTP/2 客戶端、伺服器和代理。我們也開發了 HTTP/2 的負載測試和基準測試工具。
HPACK 編碼器和解碼器可作為公用 API 使用。
nghttp2 最初是基於 RFC 7540 HTTP/2 和 RFC 7541 HPACK - HTTP/2 標頭壓縮開發的。現在我們正在更新程式碼以實現 RFC 9113。
nghttp2 程式碼庫是從 spdylay (https://github.com/tatsuhiro-t/spdylay) 專案中分叉出來的。
以下端點可用於嘗試我們的 nghttp2 實作。
https://nghttp2.org/(TLS + ALPN 和 HTTP/3)
此端點透過 ALPN 支援h2
、 h2-16
、 h2-14
和http/1.1
,並需要 TLSv1.2 進行 HTTP/2 連線。
它還支援 HTTP/3。
http://nghttp2.org/(HTTP 升級和 HTTP/2 直接)
h2c
和http/1.1
。
建置 libnghttp2 函式庫需要以下套件:
要建置文檔,您需要安裝:
如果您只需要 libnghttp2(C 函式庫),那麼上述套件就足夠了。使用--enable-lib-only
確保僅建置 libnghttp2。這可以避免與建立捆綁應用程式相關的潛在建置錯誤。
要在src
目錄中建置並執行應用程式( nghttp
、 nghttpd
、 nghttpx
和h2load
),需要以下套件:
要在nghttp
中啟用-a
選項(從下載的資源中取得連結資源),需要以下套件:
要在 nghttpx 中啟用 systemd 支持,需要以下軟體包:
HPACK 工具需要以下軟體包:
要在範例目錄下建立原始程式碼,需要 libevent:
為了減少長時間運行的伺服器程式( nghttpd
和nghttpx
)中的堆碎片,建議使用 jemalloc :
傑馬洛克
筆記
由於 musl 限制,Alpine Linux 目前不支援 malloc 替換。請參閱問題#762 中的詳細資訊。
對於 BoringSSL 或 aws-lc 構建,要在應用程式中啟用 RFC 8879 TLS 憑證壓縮,需要以下程式庫:
要啟用 nghttpx 的 mruby 支持,需要 mruby。我們需要明確開啟 C++ ABI 來建立 mruby,也可能需要其他 mrgems,mruby 由第三方/mruby 目錄下的 git 子模組管理。目前,mruby 對 nghttpx 的支援預設為停用狀態。若要啟用 mruby 支持,請使用--with-mruby
設定選項。請注意,在撰寫本文時,Debian/Ubuntu 中的 libmruby-dev 和 mruby 軟體包不可用於 nghttp2,因為它們不啟用 C++ ABI。要建置 mruby,需要以下軟體包:
nghttpx 支援 OpenSSL 的 neverbleed 權限分離引擎。簡而言之,當像 Heartbleed 這樣的嚴重漏洞被利用時,它可以最大限度地降低私鑰洩漏的風險。預設情況下,neverbleed 處於禁用狀態。要啟用它,請使用--with-neverbleed
配置選項。
要啟用 h2load 和 nghttpx 的實驗性 HTTP/3 支持,需要以下函式庫:
使用--enable-http3
設定選項為 h2load 和 nghttpx 啟用 HTTP/3 功能。
為了建立可選的 eBPF 程式以將傳入的 QUIC UDP 資料封包定向到 nghttpx 的正確套接字,需要以下程式庫:
使用--with-libbpf
配置選項建立 eBPF 程式。建構 libbpf 需要 libelf-dev。
對於 Ubuntu 20.04,您可以從原始碼建置 libbpf。 nghttpx 需要 eBPF 程式來重新載入其設定並熱交換其可執行檔。
編譯 libnghttp2 C 原始碼需要 C99 編譯器。已知 gcc 4.8 就夠了。為了編譯 C++ 原始碼,需要 C++20 相容的編譯器。至少 g++ >= 12 和 clang++ >= 15 已知可以工作。
筆記
要在 nghttpx 中啟用 mruby 支持,並使用--with-mruby
配置選項。
筆記
Mac OS X 用戶可能需要--disable-threads
設定選項來停用 nghttpd、nghttpx 和 h2load 中的多線程,以防止它們崩潰。歡迎提供補丁以使多線程在 Mac OS X 平台上工作。
筆記
要編譯關聯的應用程式(nghttp、nghttpd、nghttpx 和 h2load),您必須使用--enable-app
設定選項並確保滿足上述指定的要求。通常,設定腳本會檢查建置這些應用程式所需的依賴項,並自動啟用--enable-app
,因此您不必明確使用它。但如果您發現應用程式未構建,那麼使用--enable-app
可能會找到原因,例如缺少依賴項。
筆記
為了偵測第三方函式庫,使用了pkg-config(但對於某些函式庫(例如libev)我們不使用pkg-config)。預設情況下,pkg-config 在標準位置(例如/usr/lib/pkgconfig)搜尋*.pc
檔。如果需要在自訂位置使用*.pc
文件,請指定PKG_CONFIG_PATH
環境變數的路徑,並將其傳遞給配置腳本,如下所示:
$ ./configure PKG_CONFIG_PATH=/path/to/pkgconfig
對於 pkg-config 託管庫,定義了*_CFLAG
和*_LIBS
環境變數(例如OPENSSL_CFLAGS
、 OPENSSL_LIBS
)。為這些變數指定非空字串會完全覆寫 pkg-config。換句話說,如果指定了它們,pkg-config 不會用於檢測,使用者負責為這些變數指定正確的值。有關這些變數的完整列表,請執行./configure -h
。
如果您使用的是 Ubuntu 22.04 LTS,請執行以下命令來安裝所需的軟體套件:
sudo apt-get install g++ clang make binutils autoconf automake
autotools-dev libtool pkg-config
zlib1g-dev libssl-dev libxml2-dev libev-dev
libevent-dev libjansson-dev
libc-ares-dev libjemalloc-dev libsystemd-dev
ruby-dev 野牛 libelf-dev
nghttp2 計畫定期發布 tar 檔案,其中包括 nghttp2 原始碼和產生的建置文件。它們可以從發布頁面下載。
從 git 建置 nghttp2 需要 autotools 開發套件。從 tar 檔案建立不需要它們,因此更容易。通常的建置步驟如下:
$ tar xf nghttp2-XYZtar.bz2
$ cd nghttp2-XYZ
$ ./配置
$ 製作
從 git 建置很容易,但請確保至少使用 autoconf 2.68:
$ git 子模組更新 --init
$ autoreconf -i
$汽車製造商
$ 自動配置
$ ./配置
$ 製作
建立本機 Windows nghttp2 dll 最簡單的方法是使用 cmake。 Visual C++ Build Tools 的免費版本運作良好。
cmake
。cmake --build
來建立庫。請注意,上述步驟很可能只會產生 nghttp2 庫。不編譯任何捆綁應用程式。
在 Mingw 環境下,只能編譯函式庫,即libnghttp2-X.dll
和libnghttp2.a
。
如果要編譯應用程式( h2load
、 nghttp
、 nghttpx
、 nghttpd
),則需要使用 Cygwin 環境。
在Cygwin環境下,要編譯應用程式需要先編譯並安裝libev。
其次,您需要取消定義巨集__STRICT_ANSI__
,否則,函數fdopen
、 fileno
和strptime
將無法使用。
範例命令如下:
$ 匯出 CFLAGS="-U__STRICT_ANSI__ -I$libev_PREFIX/include -L$libev_PREFIX/lib"
$ 匯出 CXXFLAGS=$CFLAGS
$ ./配置
$ 製作
如果你想編譯examples/
下的應用程序,你需要從 libev 的安裝中刪除或重命名event.h
,因為它與 libevent 的安裝衝突。
使用make install
安裝 nghttp2 工具套件後,可能會遇到類似的錯誤:
nghttpx:載入共用程式庫時發生錯誤:libnghttp2.so.14:無法開啟共用物件檔案:沒有這樣的檔案或目錄
這意味著該工具無法找到libnghttp2.so
共享庫。
若要更新共享庫緩存,請執行sudo ldconfig
。
筆記
文檔仍然不完整。
若要建置文檔,請執行:
$ 製作 html
文件將在doc/manual/html/
下產生。
產生的文件不會使用make install
進行安裝。
線上文件位於 https://nghttp2.org/documentation/
若要建置啟用 HTTP/3 功能的 h2load 和 nghttpx,請使用--enable-http3
執行設定腳本。
為了讓 nghttpx 重新載入配置並交換其可執行文件,同時優雅地終止舊的工作進程,需要 eBPF。使用--enable-http3 --with-libbpf
執行設定腳本來建構 eBPF 程式。 QUIC 金鑰材料必須使用--frontend-quic-secret-file
設置,以便在重新載入期間保持現有連線處於活動狀態。
以下是建立支援 HTTP/3 的 h2load 和 nghttpx 的詳細步驟。
構建 aws-lc:
$ git clone --深度 1 -b v1.39.0 https://github.com/aws/aws-lc
$ cd aws-lc
$ cmake -B build -DDISABLE_GO=ON --install-prefix=$PWD/opt
$ make -j$(nproc) -C 構建
$ cmake --安裝構建
$ 光碟 ..
建置 nghttp3:
$ git clone --深度 1 -b v1.6.0 https://github.com/ngtcp2/nghttp3
$ cd nghttp3
$ git 子模組更新 --init --深度 1
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only
$ make -j$(nproc)
$ 進行安裝
$ 光碟 ..
建構 ngtcp2:
$ git clone --深度1 -b v1.9.1 https://github.com/ngtcp2/ngtcp2
$ cd ngtcp2
$ git 子模組更新 --init --深度 1
$ autoreconf -i
$ ./configure --prefix=$PWD/build --enable-lib-only --with-boringssl
BORINGSSL_CFLAGS="-I$PWD/../aws-lc/opt/include"
BORINGSSL_LIBS="-L$PWD/../aws-lc/opt/lib -lssl -lcrypto"
$ make -j$(nproc)
$ 進行安裝
$ 光碟 ..
如果您的 Linux 發行版沒有 libbpf-dev >= 0.7.0,請從原始碼建置:
$ git clone --深度 1 -b v1.4.6 https://github.com/libbpf/libbpf
$ cd libbpf
$ PREFIX=$PWD/build make -C src install
$ 光碟 ..
建置 nghttp2:
$ git 克隆 https://github.com/nghttp2/nghttp2
$ cd nghttp2
$ git 子模組更新 --init
$ autoreconf -i
$ ./configure --with-mruby --enable-http3 --with-libbpf
CC=clang-15 CXX=clang++-15
PKG_CONFIG_PATH="$PWD/../aws-lc/opt/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../ngtcp2/build/lib/pkgconfig:$PWD/ ../libbpf/build/lib64/pkgconfig"
LDFLAGS="$LDFLAGS -Wl,-rpath,$PWD/../aws-lc/opt/lib -Wl,-rpath,$PWD/../libbpf/build/lib64"
$ make -j$(nproc)
eBPF 程式reuseport_kern.o
應在bpf 目錄下找到。將--quic-bpf-program-file=bpf/reuseport_kern.o
選項傳遞給 nghttpx 來載入它。另請參閱 nghttpx - HTTP/2 代理 - HOW-TO 中的 HTTP/3 部分。
單元測試只需運行make check
即可完成。
我們對 nghttpx 代理伺服器進行了整合測試。測試是用 Go 程式語言編寫的,並使用其測試框架。我們依賴以下函式庫:
Go 模組會自動下載這些依賴項。
若要執行測試,請在integration-tests
目錄下執行以下命令:
$ 做到了
在測試中,我們使用連接埠 3009 來執行測試主題伺服器。
nghttp2 v1.0.0 引入了一些向後不相容的變更。在本節中,我們將描述這些變更以及如何遷移到 v1.0.0。
h2
和h2c
之前我們宣布了h2-14
和h2c-14
。 v1.0.0 實作了最終協定版本,我們將 ALPN ID 變更為h2
和h2c
。巨集NGHTTP2_PROTO_VERSION_ID
、 NGHTTP2_PROTO_VERSION_ID_LEN
、 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID
和NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN
已更新以反映此變更。
基本上,現有的應用程式不需要做任何事情,只需重新編譯就足以進行此更改。
我們使用「客戶端連線前言」來表示客戶端連線前言的前 24 個位元組。這在技術上是不正確的,因為客戶端連接前言由 24 位元組客戶端魔術位元組字串後跟 SETTINGS 幀組成。為了澄清起見,我們將此 24 位元組字串和更新的 API 稱為「客戶端魔法」。
NGHTTP2_CLIENT_CONNECTION_PREFACE
已替換為NGHTTP2_CLIENT_MAGIC
。NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN
已替換為NGHTTP2_CLIENT_MAGIC_LEN
。NGHTTP2_BAD_PREFACE
已重新命名為NGHTTP2_BAD_CLIENT_MAGIC
已棄用的NGHTTP2_CLIENT_CONNECTION_HEADER
和NGHTTP2_CLIENT_CONNECTION_HEADER_LEN
已被刪除。
如果應用程式使用這些宏,只需用新宏取代舊宏即可。從 v1.0.0 開始,客戶端魔法由庫發送(請參閱下一小節),因此客戶端應用程式可能會刪除這些巨集的使用。
以前nghttp2庫沒有發送客戶端魔術,這是客戶端連接序言的第一個24位元組字串,客戶端應用程式必須自己發送它。從 v1.0.0 開始,客戶端魔法由函式庫透過第一次呼叫nghttp2_session_send()
或nghttp2_session_mem_send2()
傳送。
發送客戶端魔法的客戶端應用程式必須刪除相關程式碼。
Alt-Svc 規範尚未最終確定。為了讓我們的 API 穩定,我們決定從 nghttp2 中刪除所有與 Alt-Svc 相關的 API。
NGHTTP2_EXT_ALTSVC
已刪除。nghttp2_ext_altsvc
已被刪除。我們已經在 v0.7 系列中刪除了 Alt-Svc 的功能,它們本質上是空的。使用這些巨集和結構的應用程序,刪除這些行。
以前nghttp2_on_invalid_frame_recv_cb_called
將nghttp2_error_code
中定義的error_code
當參數。但它們不夠詳細,無法調試。因此,我們決定使用更詳細的nghttp2_error
值。
使用此回調的應用程式應更新回呼簽章。如果它將error_code
視為 HTTP/2 錯誤代碼,請更新代碼,以便將其視為nghttp2_error
。
以前 nghttp2 不處理客戶端魔法(24 位元組位元組字串)。為了讓它處理這個問題,我們必須使用nghttp2_option_set_recv_client_preface()
。從 v1.0.0 開始,nghttp2 預設處理客戶端魔法,並且nghttp2_option_set_recv_client_preface()
被刪除。
某些應用程式可能想要停用此行為,因此我們新增了nghttp2_option_set_no_recv_client_magic()
來實現此目的。
使用nghttp2_option_set_recv_client_preface()
且值非零的應用程序,只需將其刪除即可。
使用零值或不使用nghttp2_option_set_recv_client_preface()
的應用程式必須使用非零值的nghttp2_option_set_no_recv_client_magic()
。
src
目錄包含 HTTP/2 用戶端、伺服器和代理程式。
nghttp
是一個 HTTP/2 客戶端。它可以透過先驗知識、HTTP 升級和 ALPN TLS 擴充連接到 HTTP/2 伺服器。
它具有用於幀資訊的詳細輸出模式。以下是nghttp
客戶端的範例輸出:
$ nghttp -nv https://nghttp2.org
[0.190]已連接
協商協議:h2
[0.212]接收設定訊框<長度= 12,標誌= 0x00,stream_id = 0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[設定_初始_視窗_大小(0x04):65535]
[0.212]傳送設定幀<長度= 12,標誌= 0x00,stream_id = 0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[設定_初始_視窗_大小(0x04):65535]
[0.212]傳送設定幀<長度= 0,標誌= 0x01,stream_id = 0>
;確認
(niv=0)
[0.212]傳送優先權訊框
(dep_stream_id=0,權重=201,獨佔=0)
[0.212]傳送優先權訊框
(dep_stream_id=0,權重=101,獨佔=0)
[0.212]傳送優先權訊框
(dep_stream_id=0,權重=1,獨佔=0)
[0.212]傳送優先權訊框
(dep_stream_id=7,權重=1,獨佔=0)
[0.212]傳送優先權訊框
(dep_stream_id=3,權重=1,獨佔=0)
[ 0.212] 傳送標頭訊框
; END_STREAM | END_HEADERS |優先事項
(padlen = 0,dep_stream_id = 11,權重= 16,獨佔= 0)
;開啟新串流
:方法:獲取
:小路: /
:方案:https
:權威:nghttp2.org
接受: */*
接受編碼:gzip、deflate
用戶代理:nghttp2/1.0.1-DEV
[0.221]接收設定訊框<長度= 0,標誌= 0x01,stream_id = 0>
;確認
(niv=0)
[0.221]recv(stream_id = 13):方法:GET
[0.221]recv(stream_id = 13):方案:https
[0.221]recv(stream_id=13):路徑:/stylesheets/screen.css
[0.221]recv(stream_id = 13):權威:nghttp2.org
[0.221]recv(stream_id = 13)接受編碼:gzip,deflate
[0.222]recv(stream_id=13)用戶代理:nghttp2/1.0.1-DEV
[0.222]接收PUSH_PROMISE幀<長度= 50,標誌= 0x04,stream_id = 13>
; END_HEADERS
(padlen = 0,promise_stream_id = 2)
[0.222]recv(stream_id = 13):狀態:200
[0.222]recv(stream_id = 13)日期:2015年5月21日星期四16:38:14 GMT
[0.222]recv(stream_id=13)內容類型:text/html
[0.222]recv(stream_id=13)最後修改時間:2015 年 5 月 15 日星期五 15:38:06 GMT
[0.222]recv(stream_id = 13)etag:W /“555612de-19f6”
[0.222]recv(stream_id=13)連結:; rel=預載; as=樣式表
[0.222]recv(stream_id = 13)內容編碼:gzip
[0.222]recv(stream_id=13)伺服器:nghttpx nghttp2/1.0.1-DEV
[0.222]recv(stream_id = 13)透過:1.1 nghttpx
[0.222]recv(stream_id = 13)嚴格傳輸安全:最大年齡= 31536000
[0.222]recv標頭訊框<長度= 166,標誌= 0x04,stream_id = 13>
; END_HEADERS
(padlen=0)
;第一個響應頭
[0.222]接收資料幀<長度= 2601,標誌= 0x01,stream_id = 13>
; END_STREAM
[0.222]recv(stream_id = 2):狀態:200
[0.222]recv(stream_id = 2)日期:2015年5月21日星期四16:38:14 GMT
[0.222]recv(stream_id=2)內容類型:text/css
[0.222]recv(stream_id=2)最後修改時間:2015 年 5 月 15 日星期五 15:38:06 GMT
[0.222]recv(stream_id = 2)etag:W /“555612de-9845”
[0.222]recv(stream_id = 2)內容編碼:gzip
[0.222]recv(stream_id=2)伺服器:nghttpx nghttp2/1.0.1-DEV
[0.222]recv(stream_id = 2)透過:1.1 nghttpx
[0.222]recv(stream_id = 2)嚴格傳輸安全:最大年齡= 31536000
[0.222]recv標頭訊框<長度= 32,標誌= 0x04,stream_id = 2>
; END_HEADERS
(padlen=0)
;首先推送響應頭
[0.228]接收資料幀<長度= 8715,標誌= 0x01,stream_id = 2>
; END_STREAM
[ 0.228] 發送 GOAWAY 幀
(last_stream_id = 2,error_code = NO_ERROR(0x00),opaque_data(0)= [])
HTTP 升級的執行方式如下:
$ nghttp -nvu http://nghttp2.org
[0.011]已連接
[0.011]HTTP升級請求
取得/HTTP/1.1
主辦單位:nghttp2.org
連接:升級、HTTP2 設定
升級:h2c
HTTP2 設定:AAMAAAABkAAQAAP__
接受: */*
用戶代理:nghttp2/1.0.1-DEV
[0.018]HTTP升級回應
HTTP/1.1 101 切換協議
連接:升級
升級:h2c
[0.018]HTTP升級成功
[0.018]接收設定訊框<長度= 12,標誌= 0x00,stream_id = 0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[設定_初始_視窗_大小(0x04):65535]
[ 0.018] 傳送 SETTINGS 幀
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[設定_初始_視窗_大小(0x04):65535]
[ 0.018] 傳送 SETTINGS 幀
;確認
(niv=0)
[ 0.018] 傳送優先權訊框
(dep_stream_id=0,權重=201,獨佔=0)
[ 0.018] 傳送優先權訊框
(dep_stream_id=0,權重=101,獨佔=0)
[ 0.018] 傳送優先權訊框
(dep_stream_id=0,權重=1,獨佔=0)
[ 0.018] 傳送優先權訊框
(dep_stream_id=7,權重=1,獨佔=0)
[ 0.018] 傳送優先權訊框
(dep_stream_id=3,權重=1,獨佔=0)
[ 0.018] 傳送優先權訊框
(dep_stream_id=11,權重=16,獨佔=0)
[0.019]recv(stream_id = 1):方法:GET
[0.019]recv(stream_id = 1):方案:http
[0.019]recv(stream_id=1):路徑:/stylesheets/screen.css
[0.019]recv(stream_id = 1)主機:nghttp2.org
[0.019]recv(stream_id=1)用戶代理:nghttp2/1.0.1-DEV
[0.019]接收PUSH_PROMISE幀<長度= 49,標誌= 0x04,stream_id = 1>
; END_HEADERS
(padlen = 0,promise_stream_id = 2)
[0.019]recv(stream_id = 1):狀態:200
[0.019]recv(stream_id = 1)日期:2015年5月21日星期四16:39:16 GMT
[0.019]recv(stream_id=1)內容類型:text/html
[0.019]recv(stream_id = 1)內容長度:6646
[0.019]recv(stream_id=1)最後修改時間:2015 年 5 月 15 日星期五 15:38:06 GMT
[0.019]recv(stream_id=1)etag:“555612de-19f6”
[0.019]recv(stream_id=1)連結:; rel=預載; as=樣式表
[0.019]recv(stream_id = 1)接受範圍:位元組
[0.019]recv(stream_id=1)伺服器:nghttpx nghttp2/1.0.1-DEV
[0.019]recv(stream_id = 1)透過:1.1 nghttpx
[0.019]recv標頭訊框<長度= 157,標誌= 0x04,stream_id = 1>
; END_HEADERS
(padlen=0)
;第一個響應頭
[0.019]接收資料幀<長度= 6646,標誌= 0x01,stream_id = 1>
; END_STREAM
[0.019]recv(stream_id = 2):狀態:200
[0.019]recv(stream_id = 2)日期:2015年5月21日星期四16:39:16 GMT
[0.019]recv(stream_id=2)內容類型:文字/css
[0.019]recv(stream_id = 2)內容長度:38981
[0.019]recv(stream_id=2)最後修改時間:2015 年 5 月 15 日星期五 15:38:06 GMT
[0.019]recv(stream_id=2)etag:“555612de-9845”
[0.019]recv(stream_id = 2)接受範圍:位元組
[0.019]recv(stream_id=2)伺服器:nghttpx nghttp2/1.0.1-DEV
[0.019]recv(stream_id = 2)透過:1.1 nghttpx
[0.019]recv標頭訊框<長度= 36,標誌= 0x04,stream_id = 2>
; END_HEADERS
(padlen=0)
;首先推送響應頭
[0.026]接收資料幀<長度= 16384,標誌= 0x00,stream_id = 2>
[0.027]接收資料幀<長度= 7952,標誌= 0x00,stream_id = 2>
[ 0.027] 傳送 WINDOW_UPDATE 訊框
(視窗大小增量=33343)
[ 0.032] 傳送 WINDOW_UPDATE 訊框
(視窗大小增量=33707)
[0.032]接收資料幀<長度= 14645,標誌= 0x01,stream_id = 2>
; END_STREAM
[0.032]接收設定訊框<長度= 0,標誌= 0x01,stream_id = 0>
;確認
(niv=0)
[ 0.032] 發送 GOAWAY 幀
(last_stream_id = 2,error_code = NO_ERROR(0x00),opaque_data(0)= [])
使用-s
選項, nghttp
列印出一些請求的計時訊息,按完成時間排序:
$ nghttp -nas https://nghttp2.org/
***** 統計數據 *****
請求時間:
responseEnd:收到回應的最後一個位元組的時間
相對於連線結束
requestStart:發送請求的第一個位元組之前的時間
相對於 connectEnd。如果顯示“*”,則為
由伺服器推送。
流程:回應結束-請求開始
代碼:HTTP狀態代碼
大小:作為回應正文接收的位元組數,不包含
通貨膨脹。
URI:請求URI
請參閱http://www.w3.org/TR/resource-timing/#processing-model
按“完整”排序
id responseEnd requestStart 處理程式碼 大小 請求路徑
13 +37.19ms +280us 36.91ms 200 2K /
2 +72.65ms * +36.38ms 36.26ms 200 8K /stylesheets/screen.css
17 +77.43ms +38.67ms 38.75ms 200 3K /javascripts/octopress.js
15 +78.12ms +38.66ms 39.46ms 200 3K /javascripts/modernizr-2.0.js
使用-r
選項, nghttp
將更詳細的計時資料以 HAR 格式寫入給定檔案。
nghttpd
是一個多執行緒靜態 Web 伺服器。
預設情況下,它使用 SSL/TLS 連線。使用--no-tls
選項停用它。
nghttpd
僅接受透過 ALPN 的 HTTP/2 連線或直接 HTTP/2 連線。不支援 HTTP 升級。
-p
選項允許用戶配置伺服器推送。
就像nghttp
一樣,它有一個用於幀資訊的詳細輸出模式。以下是nghttpd
的範例輸出:
$ nghttpd --no-tls -v 8080
IPv4:監聽 0.0.0.0:8080
IPv6: 聽 :::8080
[id=1] [1.521] 傳送 SETTINGS 訊框
(niv=1)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[id=1] [1.521] 接收設定訊框<長度=12,標誌=0x00,stream_id=0>
(niv=2)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
[設定_初始_視窗_大小(0x04):65535]
[id=1] [1.521] 接收設定訊框<長度=0,標誌=0x01,stream_id=0>
;確認
(niv=0)
[id=1] [1.521] 接收優先權訊框
(dep_stream_id=0,權重=201,獨佔=0)
[id=1] [1.521] 接收優先權訊框
(dep_stream_id=0,權重=101,獨佔=0)
[id=1] [1.521] 接收優先權訊框
(dep_stream_id=0,權重=1,獨佔=0)
[id=1] [1.521] 接收優先權訊框
(dep_stream_id=7,權重=1,獨佔=0)
[id=1] [1.521] 接收優先權訊框
(dep_stream_id=3,權重=1,獨佔=0)
[id=1][1.521]recv(stream_id=13):方法:GET
[id=1][1.521]recv(stream_id=13):路徑:/
[id=1][1.521]recv(stream_id=13):方案:http
[id=1][1.521]recv(stream_id=13):權限:localhost:8080
[id=1][1.521]recv(stream_id=13)接受:*/*
[id=1] [1.521] recv (stream_id=13) 接受編碼:gzip、deflate
[id=1] [1.521] recv (stream_id=13) 使用者代理:nghttp2/1.0.0-DEV
[id=1] [1.521] recv HEADERS 幀
; END_STREAM | END_HEADERS |優先事項
(padlen = 0,dep_stream_id = 11,權重= 16,獨佔= 0)
;開啟新串流
[id=1] [1.521] 傳送 SETTINGS 訊框
;確認
(niv=0)
[id=1] [1.521] 發送 HEADERS 幀
; END_HEADERS
(padlen=0)
;第一個響應頭
:狀態:200
伺服器:nghttpd nghttp2/1.0.0-DEV
內容長度:10
快取控制:最大年齡=3600
日期: 2015 年 5 月 15 日星期五 14:49:04 GMT
最後修改時間:2014 年 9 月 30 日星期二 12:40:52 GMT
[id=1] [1.522] 傳送資料幀
; END_STREAM
[id=1] [1.522]stream_id=13 已關閉
[id=1] [1.522] recv GOAWAY 幀
(last_stream_id = 0,error_code = NO_ERROR(0x00),opaque_data(0)= [])
[id=1] [1.522] 已關閉
nghttpx
是 HTTP/3、HTTP/2 和 HTTP/1.1 的多執行緒反向代理,為 http://nghttp2.org 提供支援並支援 HTTP/2 伺服器推送。
我們重新設計了nghttpx
命令列介面,因此,1.8.0 或更早版本存在一些不相容的情況。這對於擴展其功能並確保未來版本中的進一步功能增強是必要的。請閱讀從 nghttpx v1.8.0 或更早版本遷移以了解如何從早期版本遷移。
nghttpx
在 TLS 中實現了重要的效能導向的功能,例如會話 ID、會話票證(具有自動金鑰輪換)、OCSP 裝訂、動態記錄大小、ALPN、前向保密和 HTTP/2。 nghttpx
也提供透過 memcached 在多個nghttpx
實例之間共用會話快取和票證金鑰的功能。
nghttpx
有 2 種運行模式:
模式選項 | 前端 | 後端 | 筆記 |
---|---|---|---|
預設模式 | HTTP/3、HTTP/2、HTTP/1.1 | HTTP/1.1、HTTP/2 | 反向代理 |
--http2-proxy | HTTP/3、HTTP/2、HTTP/1.1 | HTTP/1.1、HTTP/2 | 正向代理 |
目前有趣的模式是預設模式。它的工作方式類似於反向代理,偵聽 HTTP/3、HTTP/2 和 HTTP/1.1,並且可以部署為現有 Web 伺服器的 SSL/TLS 終結器。
在所有模式下,前端連線預設透過 SSL/TLS 加密。若要停用加密,請在--frontend
選項中使用no-tls
關鍵字。如果停用加密,傳入的 HTTP/1.1 連線可以透過 HTTP Upgrade 升級到 HTTP/2。在另一塊硬碟上,後端連線預設不加密。若要加密後端連接,請在--backend
選項中使用tls
關鍵字。
nghttpx
支援設定檔。請參閱--conf
選項和範例設定檔nghttpx.conf.sample
。
在預設模式下, nghttpx
作為後端伺服器的反向代理:
用戶端 <-- (HTTP/3, HTTP/2, HTTP/1.1) --> nghttpx <-- (HTTP/1.1, HTTP/2) --> Web 伺服器
[反向代理]
使用--http2-proxy
選項,它充當轉送代理,即所謂的安全 HTTP/2 代理:
客戶端 <-- (HTTP/3、HTTP/2、HTTP/1.1) --> nghttpx <-- (HTTP/1.1) --> 代理
[安全代理](例如,Squid、ATS)
上面範例中的Client
需要配置為使用nghttpx
作為安全代理。
在撰寫本文時,Chrome 和 Firefox 都支援安全 HTTP/2 代理程式。將 Chrome 配置為使用安全代理程式的一種方法是建立一個 proxy.pac 腳本,如下所示:
function FindProxyForURL ( url , host ) {
return "HTTPS SERVERADDR:PORT" ;
}
SERVERADDR
和PORT
是執行 nghttpx 的機器的主機名稱/位址和連接埠。請注意,Chrome 需要有效的安全代理證書。
然後使用以下參數運行 Chrome:
$ google-chrome --proxy-pac-url=file:///path/to/proxy.pac --use-npn
後端 HTTP/2 連線可以透過 HTTP 代理進行隧道傳輸。代理是使用--backend-http-proxy-uri
指定的。下圖說明了 nghttpx 如何透過 HTTP 代理與外部 HTTP/2 代理進行通訊:
客戶端 <-- (HTTP/3、HTTP/2、HTTP/1.1) --> nghttpx <-- (HTTP/2) --
--======================---> HTTP/2 代理
(HTTP 代理隧道)(例如 nghttpx -s)
h2load
程式是 HTTP/3、HTTP/2 和 HTTP/1.1 的基準測試工具。 h2load
的 UI 深受weighttp
(https://github.com/lighttpd/weighttp) 的啟發。典型用法如下:
$ h2load -n100000 -c100 -m100 https://localhost:8443/
開始基準測試...
產生執行緒 #0:100 個同時客戶端,100000 個總請求
協定:TLSv1.2
密碼:ECDHE-RSA-AES128-GCM-SHA256
伺服器臨時金鑰:ECDH P-256 256 位
進度:完成10%
進度:完成20%
進度:完成30%
進度:已完成 40%
進度:已完成 50%
進度:完成 60%
進度:完成70%
進度:完成80%
進度:完成90%
進度:100%完成
完成時間為 771.26ms,129658 req/s,4.71MB/s
請求:總共 100000 個、已啟動 100000 個、已完成 100000 個、成功 100000 個、失敗 0 個、出錯 0 個
狀態代碼:100000 2xx、0 3xx、0 4xx、0 5xx
流量:總計 3812300 字節,標頭 1009900 字節,資料 1000000 字節
最小值 最大值 平均標準差 +/- 標準差
請求時間:25.12ms 124.55ms 51.07ms 15.36ms 84.87%
連線時間:208.94ms 254.67ms 241.38ms 7.95ms 63.00%
到第一個位元組的時間:209.11ms 254.80ms 241.51ms 7.94ms 63.00%
上面的範例總共發出了 100,000 個請求,使用 100 個並發客戶端(即 100 個 HTTP/2 會話),每個客戶端最多 100 個串流。使用-t
選項, h2load
將使用多個本機執行緒以避免客戶端的單一核心飽和。
警告
請勿針對公共伺服器使用此工具。這被認為是 DOS 攻擊。請僅將其用於您的私人伺服器。
如果啟用了實驗性的 HTTP/3,h2load 可以傳送請求給 HTTP/3 伺服器。為此,請將h3
指定為--alpn-list
選項,如下所示:
$ h2load --alpn-list h3 https://127.0.0.1:4433
對於 nghttp2 v1.58 或更早版本,請使用--npn-list
而不是--alpn-list
。
src
目錄包含 HPACK 工具。 deflatehd
程式是一個命令列標頭壓縮工具。 inflatehd
程式是命令列頭解壓縮工具。這兩個工具從 stdin 讀取輸入並將輸出寫入 stdout。錯誤將寫入 stderr。他們採用 JSON 作為輸入和輸出。我們(大部分)使用 https://github.com/http2jp/hpack-test-case 中描述的相同 JSON 資料格式。
deflatehd
程式從 stdin 讀取 JSON 資料或 HTTP/1 樣式的標頭字段,並以 JSON 格式輸出壓縮標頭區塊。
對於 JSON 輸入,根 JSON 物件必須包含cases
鍵。它的值必須包括輸入標頭集的序列。它們共享相同的壓縮上下文,並按照它們出現的順序進行處理。序列中的每個項目都是 JSON 對象,並且必須包含headers
鍵。它的值是一個 JSON 物件數組,其中僅包含一個名稱/值對。
例子:
{
"cases" :
[
{
"headers" : [
{ ":method" : " GET " },
{ ":path" : " / " }
]
},
{
"headers" : [
{ ":method" : " POST " },
{ ":path" : " / " }
]
}
]
}
使用-t
選項,程式可以接受更熟悉的 HTTP/1 樣式標頭欄位區塊。每個標頭集均由空白行分隔:
例子:
:方法:獲取
:方案:https
:小路: /
:方法: 發布
用戶代理:nghttp2
輸出是 JSON 物件。它應該包含一個cases
鍵,其值是一個 JSON 物件數組,該數組至少具有以下鍵:
output_length
/ input_length
* 100範例:
{
"cases" :
[
{
"seq" : 0 ,
"input_length" : 66 ,
"output_length" : 20 ,
"percentage_of_original_size" : 30.303030303030305 ,
"wire" : " 01881f3468e5891afcbf83868a3d856659c62e3f " ,
"headers" : [
{
":authority" : " example.org "
},
{
":method" : " GET "
},
{
":path" : " / "
},
{
":scheme" : " https "
},
{
"user-agent" : " nghttp2 "
}
],
"header_table_size" : 4096
}
,
{
"seq" : 1 ,
"input_length" : 74 ,
"output_length" : 10 ,
"percentage_of_original_size" : 13.513513513513514 ,
"wire" : " 88448504252dd5918485 " ,
"headers" : [
{
":authority" : " example.org "
},
{
":method" : " POST "
},
{
":path" : " /account "
},
{
":scheme" : " https "
},
{
"user-agent" : " nghttp2 "
}
],
"header_table_size" : 4096
}
]
}
輸出可用作inflatehd
和deflatehd
的輸入。
使用-d
選項,會新增額外的header_table
鍵,其關聯值包括處理對應標頭集後動態標頭表的狀態。該值至少包含以下鍵:
referenced
為true
,則它位於參考集中。 size
包括開銷(32 位元組)。此index
對應於頭表的索引。 name
是標頭欄位名稱, value
是標頭欄位值。max_deflate_size
內所佔用的空間條目的總和。max_size
。在這種情況下,編碼器只使用最多第一個max_deflate_size
緩衝區。由於頭表大小仍然是max_size
,編碼器必須追蹤max_deflate_size
之外但max_size
之內的條目,並確保它們不再被引用。例子:
{
"cases" :
[
{
"seq" : 0 ,
"input_length" : 66 ,
"output_length" : 20 ,
"percentage_of_original_size" : 30.303030303030305 ,
"wire" : " 01881f3468e5891afcbf83868a3d856659c62e3f " ,
"headers" : [
{
":authority" : " example.org "
},
{
":method" : " GET "
},
{
":path" : " / "
},
{
":scheme" : " https "
},
{
"user-agent" : " nghttp2 "
}
],
"header_table_size" : 4096 ,
"header_table" : {
"entries" : [
{
"index" : 1 ,
"name" : " user-agent " ,
"value" : " nghttp2 " ,
"referenced" : true ,
"size" : 49
},
{
"index" : 2 ,
"name" : " :scheme " ,
"value" : " https " ,
"referenced" : true ,
"size" : 44
},
{
"index" : 3 ,
"name" : " :path " ,
"value" : " / " ,
"referenced" : true ,
"size" : 38
},
{
"index" : 4 ,
"name" : " :method " ,
"value" : " GET " ,
"referenced" : true ,
"size" : 42
},
{
"index" : 5 ,
"name" : " :authority " ,
"value" : " example.org " ,
"referenced" : true ,
"size" : 53
}
],
"size" : 226 ,
"max_size" : 4096 ,
"deflate_size" : 226 ,
"max_deflate_size" : 4096
}
}
,
{
"seq" : 1 ,
"input_length" : 74 ,
"output_length" : 10 ,
"percentage_of_original_size" : 13.513513513513514 ,
"wire" : " 88448504252dd5918485 " ,
"headers" : [
{
":authority" : " example.org "
},
{
":method" : " POST "
},
{
":path" : " /account "
},
{
":scheme" : " https "
},
{
"user-agent" : " nghttp2 "
}
],
"header_table_size" : 4096 ,
"header_table" : {
"entries" : [
{
"index" : 1 ,
"name" : " :method " ,
"value" : " POST " ,
"referenced" : true ,
"size" : 43
},
{
"index" : 2 ,
"name" : " user-agent " ,
"value" : " nghttp2 " ,
"referenced" : true ,
"size" : 49
},
{
"index" : 3 ,
"name" : " :scheme " ,
"value" : " https " ,
"referenced" : true ,
"size" : 44
},
{
"index" : 4 ,
"name" : " :path " ,
"value" : " / " ,
"referenced" : false ,
"size" : 38
},
{
"index" : 5 ,
"name" : " :method " ,
"value" : " GET " ,
"referenced" : false ,
"size" : 42
},
{
"index" : 6 ,
"name" : " :authority " ,
"value" : " example.org " ,
"referenced" : true ,
"size" : 53
}