HTTP2協議


 

 

如今http/2已經在互聯網上廣泛應用,大部分主流瀏覽器都已經支持,大型網站也都在使用http/2,今天就讓小編帶你們詳細了解http/2協議吧!

 

前言  

http應該大家都不陌生,目前使用最多的應該還是HTTP/1.1版本的http協議,那么HTTP/1.1到底有些什么樣的問題,導致需要HTTP/2呢?總結主要有以下兩點:

 

  • 頭阻塞:HTTP/1.1中只有收到當前請求響應后才能重用當前tcp連接發送下一次請求。雖然HTTP/1.1提出了pipeline,旨在緩解這個問題。但是一方面pipeline在復雜的網絡環境下很難實現和普及,其次就算都用上了pipeline,pipeline也只是使得可以在本次響應完成之前可以發送下次請求,但響應依然要嚴格按照順序返回,也就是如果前一個響應被阻塞,后邊的響應將不會到來,頭阻塞問題還是沒有完全解決。目前客戶端(比如瀏覽器)一般會同時打開多個tcp連接來繞開頭阻塞的問題,chrome默認會打開6個tcp鏈接,並發請求各種類型數據。但是這就帶來了一個矛盾,tcp鏈接越多,肯定對資源的浪費是越大的,鏈接越少,頭阻塞的問題就會越突出。

     

  • 重復的未壓縮頭數據的傳輸:自HTTP/1.1之后,HTTP請求中通常帶有大量ASCII編碼的頭部,這些頭部通常大部分不會變化,需要每次請求都攜帶(尤其像是User Agent、Cookie這些值比較長的頭部字段),會給本來就擁擠的網絡帶來很大的壓力。

 

HTTP2協議初探 https://mp.weixin.qq.com/s/RYVPLG-ROcWH2YVQmVqgig

 

 

除此之外還有一些問題,比如TCP 的慢啟動,起初會限制連接的最大速度,如果數據成功傳輸,會隨着時間的推移提高傳輸的速度,而大量http的業務請求其實本身都只是短鏈接的,所以導致tcp的流控算法沒有很好的應用。為此,HTTP/2的出現就很有必要了。

 

HTTP/2概述

以下是HTTP/2的發展歷程:

 

  • 2009年,Google提出了一項實驗性的協議SPDY(讀音同speedy),旨在開發者不修改當前網站實現的前提下,提高頁面加載速度。SPDY提出后,Chrome、Firefox、Opera等主流瀏覽器先后給出了實現,很多大型網站(如Google、Twitter、Facebook等)分別提供了其對SPDY會話的實現。

     

  • 2012年,HTTP-WG提出了在SPDY基礎上構建HTTP/2的草案。

     

  • 2013年給出了第一個對HTTP/2的實現,自此HTTP/2、SPDY並行發展,在客戶端和服務器上進行了廣泛可靠的測試。

     

  • 2015年,Google 宣布放棄對SPDY的繼續支持,標志着HTTP/2正式登上歷史舞台。

 

那么HTTP/2有些什么特點呢:

 

  • HTTP/2 可以讓我們的應用更快、更簡單、更穩定。

     

  • 通過有效壓縮 HTTP 標頭字段將協議開銷降至最低,同時增加對請求優先級和服務器推送的支持。

     

  • HTTP/2帶來了大量其他協議層面的輔助實現,例如新的流控制、錯誤處理和升級機制。

     

  • HTTP/2 沒有改動 HTTP 的應用語義,而是修改了數據的格式和傳輸方式,只需要升級基礎設施(代理、web框架等),上層的業務代碼都可以不必修改而在新協議下運行。

 

注:上一個HTTP版本是1.1,為什么這之后不是 HTTP/1.2呢?

因為 HTTP/2 引入了一個新的二進制分幀層,該層無法與之前的 HTTP/1.x 服務器和客戶端兼容,因此協議的主版本提升到 HTTP/2,且HTTP/2后HTTP協議沒有小版本號,后面直接就是HTTP3。

 

那HTTP/2是怎么做到加入了這么多功能和優化,但是上層業務不需要改代碼呢?一切都是因為引入了一個二進制分幀層,並且HTTP/2的新特性也都是建立在這個基礎之上的。這又印證了那句話,"計算機中任何問題都可以通過增加一個中間層來解決"。

圖片

圖1

 

如上圖1所示,HTTP/2.0在應用層(HTTP)和傳輸層(TCP或者TLS)之間增加一個二進制分幀層,HTTP/2.0通信都在一個TCP連接上完成,這個連接可以承載任意數量的雙向數據流,相應的每個數據流以消息的形式發送。而消息由一或多個幀組成,這些幀可以亂序發送,然后根據每個幀首部的流標識符重新組裝。

圖片

圖2

 

圖2是另一張描述連接(connection)、流(stream)、消息(message)、幀(frame)關系的圖,這里解釋下這幾個概念。   

 

  • (Frame):是HTTP/2通信的最小單位,請求和響應都分為首部幀和消息幀單獨傳輸,除此之外還有一些其他的類型的幀,下面在介紹幀結構的時候會再次提到。

 

  • 消息(Message):指邏輯上的HTTP消息,比如一次請求、一次響應等。它由一個或多個幀組成,以二進制壓縮格式存放 HTTP/1 中的頭部。    

 

  • 流(Stream):

  • 是一條邏輯上的連接,是TCP連接中的一個虛擬通道(每個Tcp連接會建立多個steam);

  • 它可以承載雙向的消息,每個流都有一個唯一的整數標識符,幀會記錄Stream 的id;

  • 不同 Stream 的幀是可以亂序發送的(因此可以並發不同的 Stream ),因為每個幀的頭部會攜帶 Stream ID 信息,所以接收端可以通過 Stream ID 有序組裝成 HTTP 消息。同一 Stream 內部的幀必須是嚴格有序的;

  • 客戶端和服務器雙方都可以建立 Stream, Stream ID 也是有區別的,客戶端建立的 Stream 必須是奇數號,而服務器建立的 Stream 必須是偶數號;

  • 同一個連接中的 Stream ID 是不能復用的,只能順序遞增,所以當 Stream ID 耗盡時,需要發一個控制幀 GOAWAY,用來關閉 TCP 連接;

 

  • 連接(Connection):即TCP連接

     

這種在一個TCP連接中划分多個邏輯鏈接,把所有兩端的流量都集中在一個TCP連接的做法,減少了服務端的壓力,雖然流量還是這么多流量,但是創建的socket會明顯減少,內存占用更少,每個連接吞吐量更大。並且HTTP 性能優化的關鍵並不在於高帶寬,而是低延遲,這種做法避免了TCP的慢啟動,能更有效的利用到TCP的流控算法。

 

幀結構 

圖片

圖3

 

讓我們再來看一下幀的結構,幀由幀頭(Frame Header)和幀負載(Frame Payload)構成,如上圖3是幀頭的結構,幀頭總共9個字節,包括幀長度、幀類型、標識位、保留位和流標識符。    

 

其中幀長度為3個字節,表示幀負載(Frame Playload)的長度(不包括幀頭9字節),這個長度的最大值通過SETTING類型的幀中攜帶的SETTINGS_MAX_FRAME_SIZE參數來設置。假如一個HEADERS幀不夠傳輸所有的HEADER字段,則會把剩余的部分放到后續的CONTINUATION類型的幀來傳輸。   

 

幀類型,1個字節,表示幀的類型,目前HTTP/2 總共定義了 10 種類型的幀,如下圖4所示。

圖片

圖4

 

TCP本身有探活的機制,為什么HTTP/2里又要重新定義一個PING的幀呢?因為TCP協議棧其實是內核里實現的,收到包后會自動ack把數據緩存到內核緩存區里,然后再復制到應用程序的進程空間,所以其實TCP的探活不能反映應用程序是否正常,可能應用程序就是因為某些原因卡住了,但是進程又沒掛,ack還是正常的。所以在應用層實現一種探活機制是很有必要的。        

 

標識位,一字節,總共可以保存 8 個標志位,用於攜帶簡單的控制信息,這里同一位在不同類型的幀里的語義可能不太一樣,目前HTTP/2總共只定義了有5種標志,如下圖5所示。

圖片

圖5

 

可以發現ACK和END_STREAM在標識位的里的位置一樣,都是0x01,第一位,但是在不類型的幀里,這一位可能表示是ACK可能表示是END_STREAM。有些幀是沒有定義標志位(字段還是存在的,只是為0x00),包括PRIORITY、RST_STREAM、GOAWAY、WINDOW_UPDATE這些幀。(標志位后面是1位的保留位,暫時未定義)

 

保留位后面是31位的流標識符,所以流標識符的最大值是 2^31,大約是 21 億。這個值表示幀屬於的流,某些幀的這個值必須為0,比如SETTINGS、PING、GOAWAY,因為這些幀並不屬於任何流,是和整個連接相關的。WINDOW_UPDATE的這個值可以為0,可以不為0,為0則表示這個幀的payload描述的是整個連接的流控信息,不為0則是描述的指定的這個流的流控信息。其他類型的幀流標識符不能為0,圖6很好地描述了不同類型的幀的標志位和流標示符的情況:

圖片

圖6

 

幀體則在不同類型的幀里的結構都不一樣,這里不再贅述,感興趣的可以看下參考鏈接。

 

HTTP/2特性

相比HTTP1,HTTP/2除了上面說的分幀層,HTTP/2還有如下特性:首部壓縮、流優先級、服務端推送、流量控制,下面一一介紹。

 

首部壓縮

HTTP/1.1包含很多固定字段,比如Cookie、User Agent、Accept 等,這些字段加起來也高達幾百字節甚至上千字節,HTTP/2對這些內容進行了二進制壓縮,所以header在HTTP/2里不是直接可讀的格式。同時因為大量請求響應報文里很多header都是重復的,這些會占用額外帶寬和cpu。HTTP/2把header的字段在客戶端和服務端分別給緩存了。 

       

HTTP/2處於安全考慮,沒有使用常見的壓縮算法比如gzip,而是定義了一個名叫的HPACK的算法,由靜態表、動態表、哈夫曼編碼等方式構成,更詳細的可以看下參考鏈接。

 

請求優先級

將 HTTP 消息分解為很多獨立的幀之后,我們就可以復用一個連接,客戶端和服務器交錯發送和傳輸這些幀的順序就成為關鍵的性能決定因素。而對於瀏覽器來說,這是一種用於提升瀏覽性能的關鍵功能,網絡中擁有多種資源類型,它們的依賴關系和權重各不相同,比如可以定義傳輸HTML的流的優先級比傳輸圖片文件的流的優先級更高,來讓服務端優先傳輸HTML。

圖片

圖7

 

如上圖7所示,HTTP/2定義了每個流的依賴關系,依賴於另一個流的流是依賴流,被依賴的是父流。兄弟節點則會定一個一個權重值,這個權重是個介於1至256之間的整數,兄弟節點依靠權重來分配資源。這個模型構建了一個虛擬的流,作為所有流的祖先節點,是這個依賴樹的根節點(其實就是id為0的流,當然這個流是不存在的),流節點不能依賴自身。

 

請求先級這個特性主要還是客戶端用來調整服務端發送資源的順序,比如瀏覽器建議服務端返回資源的優先級。但是,請求優先級只是一個建議,而不是要求,更不是保證,流優先級不保證流相對於任何其他流的任何特定處理或傳輸順序。也就是說,客戶端定義了流優先級,並把這個信息通過HEADERS或者PRIORITY幀傳輸給服務端,但是服務端會不會按照這個定義的優先級順序來進行處理和返回,則完全沒有保證,可能服務端沒有實現任何和流優先級相關的功能,也可能實現了,一旦服務端實現了,就可以利用客戶端給予的信息來做一些優化。

 

服務器推送

服務器推送這個機制允許服務端主動向客戶端在新開的流上推送一些資源,典型的場景是瀏覽器請求一個html頁面,html里會引用css和相關圖片,當服務端收到html頁面的請求時,除了返回html文件,還會直接把相關的css和圖片文件都返回,這樣瀏覽器就不用請求多次再來獲取html頁面里引用的資源了。服務器推送在語義上等同於服務器響應一個請求,為了不改變HTTP的語義怎么來做呢,服務端會先返回一個幀作為請求,然后在一個新打開的流上,返回一個幀作為響應,大概過程是:

 

  • Push Requests:當收到一個客戶端請求時,服務端決定推送一些資源,此時服務端會發送一個PUSH_PROMISE幀給客戶端,這個PUSH_PROMISE幀包含了后面返回的響應幀所在的流的id,還包含了響應的header的信息。客戶端在接收到 PUSH_PROMISE幀后,它可以根據自身情況選擇拒絕響應流(發送RST_STREAM幀),例如,如果資源已經位於緩存中,便可能會發生這種情況。同時這個PUSH_PROMISE在語義上視作客戶端對服務端的一個請求。

     

  • Push Responses:發送PUSH_PROMISE幀后,服務端會直接發送數據幀到之前PUSH_PROMISE承諾的流上,作為響應,(因為header已經在PUSH_PROMISE傳輸了,這里只要傳輸body即可)也就是這里的數據幀在語義上相當於對PUSH_PROMISE幀的一個響應。

 

圖片

圖8

 

服務端推送只能由服務端進行,客戶端(發起連接的一方)無法推送,也就是只能是單向的。所以在第一眼看到服務端推送這個功能時,還以為和我們常規理解的服務端推送技術一樣,還想着是否在HTTP/2時代就不需要websocket了,現在看來還是不太一樣,正是因為這個推送是單向的,所以還是受到了很大的限制,場景也比較有限。

 

流量控制

由於 HTTP/2 數據流在一個 TCP 連接內復用,TCP 流控制既不夠精細,也無法提供必要的應用級 API 來調節各個數據流的傳輸。為了解決這一問題,HTTP/2 提供了一組簡單的機制,允許客戶端和服務器實現其自己的數據流和連接級流控制。

 

流量控制基於WINDOW_UPDATE幀,發送者通告他們准備在 stream 流,或者整個連接上接收多少個八位字節,流量控制是定向的,發送者提供信息,接收者控制自己的速度。對於一個新的stream流和整個連接,流量控制窗口的初始值為 65,535 個八位字節。並且只有DATA幀才會受到流控的限制,因為其他幀都不大,而且也比較重要,這確保了重要的控制幀不會被流量控制阻擋。無法禁用流量控制。建立 HTTP/2 連接后,客戶端將與服務器交換 SETTINGS 幀,這會在兩個方向上設置流控制窗口。

 

HTTP/2 僅定義 WINDOW_UPDATE 幀的格式和語義。未規定發送方何時發送此幀或其發送的值,也未規定接受方如何選擇發送數據包。實現方能夠選擇任何適合其需求的算法,也就是HTTP/2的流量控制只是定義了流控信息的交換格式,至於怎么利用這些信息來進行流控,客戶端和服務端可以有不同的實現。

 

HTTP/2帶來的變化

那么HTTP/2到底給我們帶來了哪些改變呢?最主要的一點就是更快了,上面的請求優先級、服務端推送,流量控制其實都在某種程度上讓HTTP/2的請求響應更快了,並且因為瀏覽器可以在一個TCP上多路復用並發請求資源,不再局限於每個域只能打開一定的TCP連接,所以不會出現請求超過這個數目的資源時,后續的請求只能阻塞等待前面的請求完成的情況。在這個網站(https://HTTP/2.akamai.com/demo)上可以感受下HTTP/1.1和HTTP/2速度的差異。除此之外,有一些HTTP/1.1里的做法在HTTP/2里就不太適用了。

 

  • 域名分片:HTTP/1.x中,因為瀏覽器會對並發連接有限制。為了突破這個限制,通常會把請求的資源置於不同的域名下(如 shard1.example.org, shard2.example.org)。而在HTTP/2中,因為不需要新開連接來解決頭阻塞問題,直接可以在一個連接上多路復用,所以這種做法沒什么必要了。

     

  • 雪碧圖:HTTP/1.x中,為了緩解請求多個圖片帶來的頭阻塞問題,通常采用把多個圖片拼接成一個大圖,然后一個請求將所有圖片加載在瀏覽器中,然后通過css或者js將所需要的部分按需展示出來,這種做法增加了代碼的復雜性,並且瀏覽器渲染過程中,內存需要加載更多的圖片,同上,因為HTTP/2支持連接多路復用,這種做法也沒必要了。

     

  • 拼接js、css:類似雪碧圖,拼接JavaScript、CSS的做法同樣是為了減少請求數,以避免潛在的頭阻塞問題。

     

  • 資源內聯:為了防止在請求頁面后,還需要多次請求頁面里引用的css和圖片,有時會把css、base64編碼的圖片直接內連到頁面里一起返回,HTTP/2的服務端推送(server push)正好解決了這個問題。

    

當然,HTTP/2也不是那么完美,也仍然會存在一些問題,比如:

 

  • 解決了HTTP1.1的隊頭阻塞問題,但是TCP 的隊頭阻塞並沒有徹底解決,HTTP/2 出現丟包時,整個 TCP 都要等待重傳,那么就會阻塞該 TCP 連接中的所有請求。

     

  • TCP 連接斷掉會導致所有的Stream斷掉,而在HTTP/1.1里斷掉一個TCP連接影響不會這么大。

     

  • 這篇文章(https://www.lucidchart.com/techblog/2019/04/10/why-turning-on-http2-was-a-mistake/)描述了一個問題,因為瀏覽器不再像之前那樣限制並發的請求(通過限制TCP連接來實現),導致服務器的壓力更大,並且會出現更多毛刺現象,更多尖銳的流量,監控上的流量不像之前HTTP/1.1那么平滑,需要重新調整下負載均衡的設置(雖然HTTP2也會通過SETTING幀來設置最大的並發Stream數,但是默認是100,還是比較大)

 

HTTP/2協議協商

對於客戶端或者服務端來說,怎么確認對端是否支持HTTP/2呢?這里就要說下HTTP/2的協議協商機制。HTTP/2 使用HTTP/1.1相同"http"和"https" URI scheme。且共享相同的默認端口號: "http" 為 80,"https" 為 443。因此,對於客戶端來說首先需要發現上游服務器是否支持 HTTP/2,對於 "http" 和 "https" URI,確定支持 HTTP/2 的方式是不同的。對此,RFC中定義了兩個不同的標識符:

 

  • 字符串"h2"標識:表示HTTP/2使用傳輸層安全性(TLS)協議,在TLS上加密通信,對應https

     

  • 字符串"h2c"標識:表示HTTP/2,不使用TLS,直接建立在TCP明文通信,對應http

 

h2c的協議協商

h2c,即直接建立在TCP上的HTTP/2的協議協商,簡單來說就是客戶端會先用HTTP/1.1協議的格式開始通信,然后協商升級到HTTP/2,這依賴於HTTP/1.1的協議升級機制。HTTP/1.1(也僅限1.1版本)提供了一種特殊的機制,這一機制允許將一個已建立的連接升級成新的、不相容的協議,比如HTTP/2,比如websocket。通常來說這一機制總是由客戶端發起的,服務端可以選擇是否要升級到新協議。借助這一技術,連接可以以常用的協議啟動(如HTTP/1.1),隨后再升級到HTTP/2甚至是WebSockets,大致過程如下:

 

當客戶端支持HTTP/2,試圖升級到HTTP/2,可以先發送一個普通的請求(GET,POST等),這個請求需要添加三項額外的header:

 

  • Connection: Upgrade    // 設置 Connection 頭的值為 "Upgrade" 來指示這是一個升級請求。

     

  • Upgrade: h2c     // 設置指定要升級的協議名稱,這里就是h2c。

     

  • HTTP/2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>    // 這個header包含了base64url編碼的settings幀,作為客戶端的初始化配置。

 

服務端收到請求,如果服務端不支持h2c,會忽略客戶端發送的 "Upgrade 頭部字段,返回一個常規的響應:例如一個200 OK,此時客戶端就明白服務端不支持h2c,之后將會降級成HTTP/1.1進行通信。如果服務端支持h2c,決定升級這個連接,則會返回一個 101 Switching Protocols 響應狀態碼,並且原樣返回Connection: Upgrade和Upgrade: h2c。並且在body里就會返回一個HTTP/2的settings幀,來初始化配置,作為服務端的連接序言,之后服務端可以和客戶端開始通信了。

 

客戶端在接收到101響應后,也必須發送一個連接序言(包括一個MAGIC幀和SETTINGS幀),然后可以開始傳輸其他幀,和服務端通信了。

 

h2的協議協商

 HTTP/2 over TLS 使用 "h2"作為協議標識符,因為h2是建立在TLS之上,而TLS本身在握手的時候就會有一個協議協商的過程,在ClientHello中,客戶端會發送自己支持的一系列協議或者算法(雖然都是和后續的TLS建立有關),然后服務端會在這其中選擇一組協議或者算法來作為后續建立TLS的依據,並在ServerHello里返回。並且TLS還提供了擴展機制,借着這個擴展機制,可以在TLS握手階段做一些額外的事情,h2的協議協商就是利用TLS的擴展機制來完成的。

 

h2使用名為ALPN(Application Layer Protocol Negotiation,應用層協議協商)的TLS擴展協議進行協商。正如HTTP/2前身是SPDY協議,Goggle在當時也開發了一個名為NPN(Next Protocol Negotiation,下一代協議協商協議)的TLS擴展,這就是ALPN的前身。顧名思義,APLN就是用來在TLS里協商上層協議使用什么協議的一個擴展。在HTTP/2以及之后,上文提到的HTTP/1.1的協議升級機制就被廢除了,基於TLS的應用層協議均可以通過ALPN來進行協商。       

 

h2協商大概的過程,就是客戶端TLS握手時候,會在ClientHello中帶上ALPN擴展,在里面會指定客戶端支持的應用層協議(一般能看到HTTP/1.1、h2、h2c三種),服務端收到后會返回ServerHello,如果不能識別ALPN,則結果里不會包括ALPN擴展,如果支持h2,則會在ALPN擴展返回h2。

 

HTTP/2 協議本身並沒有要求它必須基於 HTTPS(TLS)部署,但是出於以下原因,實際使用中,HTTP/2 和 HTTPS 幾乎都是捆綁在一起。一方面,HTTPS可以保證數據傳輸的安全性,另一方面因為TLS是建立在TCP之上的,對中間節點是保密的,所以具備更好的連通性,基於HTTPS部署的新協議具有更高的連接成功率。然后最重要的,當前的主流瀏覽器都只支持HTTPS訪問HTTP/2服務,並且很多web框架也只支持h2方式的HTTP/2請求,比如go的gin。這也是個好事,可以進一步推動大家都來使用HTTPS。

 

HTTP/2的支持情況

目前HTTP/2已經很成熟了,在互聯網上已經比較普及了。客戶端側,主流的瀏覽器都已經支持了HTTP/2,在caniuse上可以看到瀏覽器對HTTP/2的支持情況,如下圖9所示:

圖片

圖9

 

服務端側,nginx、haproxy等主流的反向代理也都支持了HTTP/2,很多語言的web框架也都能通過某些方式來支持HTTP/2,另外一個殺手級應用grpc就是使用HTTP/2通信的。golang對HTTP/2支持的比較早:

 

  • go在1.6開始支持HTTP/2,這個時候只支持HTTP/2 over TLS,也就是h2。只要是TLS部署,則http庫就會默認進行HTTPS/2協商,協商失敗則蛻化為HTTPS/1

     

  • go在1.8開始支持 HTTP/2 server push

     

  • go在1.11開始支持 HTTP/2 h2c(因為一般是不建議使用h2c這種方式來暴露HTTP/2服務的,所以社區經過不斷討論最后才決定合並對h2c的支持)

     

  • gin目前只支持h2方式的HTTP/2,不支持h2c方式的HTTP/2(看到有相關的pr,不知道未來是否會支持)

 

如果想讓服務升級到HTTP/2,可以有這么幾種方式:

 

  • 如果只有靜態資源,比如只是靜態頁面,像是博客這種,可以直接托管到支持HTTP/2的cdn或者其他的托管網站上。

     

  • 另外一種是服務端直接支持HTTP/2,需要看下框架是否支持,如果框架支持的話其實基本不需要更改業務層代碼,因為HTTP/2保持語義不變,只是傳輸方式改變,這些復雜的事情一般web框架都會直接給處理了,對上層暴露的接口則保持了兼容(比如在go里,官方的net庫直接就支持了HTTP/2,可能已經用上了都不知道)

     

  • 最后一種辦法則是在反向代理這一層做協議轉換,比如使用haproxy、nginx等,haproxy在用戶側使用HTTP/2通信,和后端服務之間則是使用HTTP/1.1通信

 

參考鏈接

  • https://schecterdamien.github.io/2021/07/25/http2/

  • https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn

  • https://datatracker.ietf.org/doc/html/rfc7540

  • https://imququ.com/post/protocol-negotiation-in-http2.html

  • https://halfrost.com/http2-header-compression/

  • https://halfrost.com/http2-http-frames-definitions/

  • https://zhuanlan.zhihu.com/p/359920955

  • https://github.com/amandakelake/blog/issues/35

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM