成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

NGINX HTTP2 處理流程

BDEEFE / 3629人閱讀

摘要:本文通過(guò)一個(gè)小例子串一遍處理的流程。主要涉及到的協(xié)議以及的處理流程。所以對(duì)協(xié)議的服務(wù)發(fā)起請(qǐng)求時(shí),一般瀏覽器會(huì)建立條連接,并行的去請(qǐng)求不同的資源。表明該字段是否使用了編碼。

本文通過(guò)一個(gè)小例子串一遍nginx處理http2的流程。主要涉及到http2的協(xié)議以及nginx的處理流程。

http2簡(jiǎn)介

http2比較http1.1主要有如下五個(gè)方面的不同:

二進(jìn)制協(xié)議 http1.1請(qǐng)求行和請(qǐng)求頭部都是純文本編碼,即可以直接按ascii字符解釋,而http2是有自己的編碼格式。并且nginx中http2必須建立在ssl協(xié)議之上。

頭部壓縮 舉個(gè)例子,HTTP1.1傳一個(gè)header ,需要11個(gè)字符.http2中有一個(gè)靜態(tài)索引表,客戶端傳索引鍵,例如1,nginx通過(guò)查表能知道1代表method: GET.nginx中除了該靜態(tài)表,還會(huì)有一個(gè)動(dòng)態(tài)表,保存例如host這種變化的頭部

多路復(fù)用 http1.1一個(gè)連接上只能傳輸一個(gè)請(qǐng)求,當(dāng)一個(gè)請(qǐng)求結(jié)束之后才能傳輸下一個(gè)請(qǐng)求。所以對(duì)http1.1協(xié)議的服務(wù)發(fā)起請(qǐng)求時(shí),一般瀏覽器會(huì)建立6條連接,并行的去請(qǐng)求不同的資源。而http2的二進(jìn)制協(xié)議中有一個(gè)frame的概念,每個(gè)frame有自己的id,所以一個(gè)連接上可以同時(shí)多路復(fù)用傳輸多個(gè)不同id的frame

主動(dòng)push http1.1是請(qǐng)求-響應(yīng)模型,而http2可以主動(dòng)給客戶端推送資源

優(yōu)先級(jí) 既然多路復(fù)用,所有數(shù)據(jù)跑在了一條通道上,必然會(huì)有優(yōu)先級(jí)的需求

本文的例子主要通過(guò)解析報(bào)文說(shuō)明頭三個(gè)特性

配置環(huán)境

NGINX配置如下:

    server {
        listen 8443 ssl http2;
        access_log  logs/host_server2.access.log  main;
        ssl_certificate /home/xiaoju/nginx-2/nginx-selfsigned.crt;
        ssl_certificate_key /home/xiaoju/nginx-2/nginx-selfsigned.key;
        ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

        location / {
            root   html;
            index  index.html index.htm /abc.html;
            access_log  logs/host_location3.access.log  main;
            http2_push /favicon.ico;
            http2_push /nginx.png;
        }
    }

客戶端按如下方式發(fā)起請(qǐng)求:

curl  -k  -I   -L https://IP:8443
HTTP/2 200  //可以看到,返回是http/2
server: nginx/1.14.0
date: Tue, 11 Dec 2018 09:20:33 GMT
content-type: text/html
content-length: 664
last-modified: Tue, 11 Dec 2018 04:19:32 GMT
etag: "5c0f3ad4-298"
accept-ranges: bytes
請(qǐng)求解析 客戶端請(qǐng)求問(wèn)題

先思考一個(gè)問(wèn)題,上文配置中使用curl發(fā)送請(qǐng)求時(shí),為何直接返回的是http/2,而不是http/1.1(雖然服務(wù)端配置了使用http2,但萬(wàn)一客戶端未支持http2協(xié)議,直接返回http2客戶端會(huì)解析不了)

因?yàn)閚ginx中http2必須在ssl之上,所以我們首先通過(guò)在nginx代碼中的ssl握手部分打斷點(diǎn)gdb跟一下.

(gdb) b ngx_ssl_handshake_handler  //ssl握手函數(shù)
Breakpoint 1 at 0x47ddb5: file src/event/ngx_event_openssl.c, line 1373.
(gdb) c
Continuing.
Breakpoint 1, ngx_ssl_handshake_handler (ev=0x16141f0) at src/event/ngx_event_openssl.c:1373
1373    {

1390        c->ssl->handler(c); //實(shí)際處理邏輯位于ngx_http_ssl_handshake_handler
(gdb) s
ngx_http_ssl_handshake_handler (c=0x15da400) at src/http/ngx_http_request.c:782
782    {

(gdb) n
805            if (hc->addr_conf->http2) { //配置http2后hc->addr_conf->http2標(biāo)志位為1

(gdb) n
808                SSL_get0_alpn_selected(c->ssl->connection, &data, &len);//從ssl協(xié)議中取出alpn


(gdb) n
820                if (len == 2 && data[0] == "h" && data[1] == "2") { //如果為h2,說(shuō)明客戶端支持升級(jí)到http2協(xié)議

(gdb) n
821                    ngx_http_v2_init(c->read);//開(kāi)始進(jìn)入http2的初始化階段

簡(jiǎn)單說(shuō)就是通過(guò)ssl協(xié)議握手階段獲取一個(gè)alpn相關(guān)的配置,如果是h2,就進(jìn)入http2的處理流程。我們通過(guò)wireshark抓包可以更直觀的看出這個(gè)流程


如上圖,在ssl握手中的Client Hello 階段有一個(gè)協(xié)議擴(kuò)展alpn

http2報(bào)文格式

http2 以一個(gè)preface開(kāi)頭,接著是一個(gè)個(gè)的frame,其中每個(gè)frame都有一個(gè)header,如下:


其中l(wèi)ength代表frame內(nèi)容的長(zhǎng)度,type表明frame的類型,flag給frame做一些特殊的標(biāo)記,sid代表的就是frame的id.

其中 frame有如下10種類型

#define NGX_HTTP_V2_DATA_FRAME           0x0 //body數(shù)據(jù)
#define NGX_HTTP_V2_HEADERS_FRAME        0x1 //header數(shù)據(jù)
#define NGX_HTTP_V2_PRIORITY_FRAME       0x2 //優(yōu)先級(jí)設(shè)置
#define NGX_HTTP_V2_RST_STREAM_FRAME     0x3 //重置一個(gè)stream
#define NGX_HTTP_V2_SETTINGS_FRAME       0x4 //其他設(shè)置項(xiàng),例如是否開(kāi)啟push,同時(shí)能夠處理的stream數(shù)量等
#define NGX_HTTP_V2_PUSH_PROMISE_FRAME   0x5 //push
#define NGX_HTTP_V2_PING_FRAME           0x6 //ping
#define NGX_HTTP_V2_GOAWAY_FRAME         0x7 //goaway.發(fā)送此frame后會(huì)重新建立連接
#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME  0x8 //窗口更新 流控使用
#define NGX_HTTP_V2_CONTINUATION_FRAME   0x9 //當(dāng)一個(gè)frame發(fā)送不完數(shù)據(jù)時(shí),可以按continuation格式繼續(xù)發(fā)送

frame ID在客戶端按奇數(shù)遞增,例如1,3,5,偶數(shù)型id留給服務(wù)端推送push時(shí)使用,設(shè)置連接屬性相關(guān)的frame id都為0

flags有如下定義:

#define NGX_HTTP_V2_NO_FLAG              0x00 //未設(shè)置
#define NGX_HTTP_V2_ACK_FLAG             0x01 //ack flag
#define NGX_HTTP_V2_END_STREAM_FLAG      0x01 //結(jié)束stream
#define NGX_HTTP_V2_END_HEADERS_FLAG     0x04 //結(jié)束headers
#define NGX_HTTP_V2_PADDED_FLAG          0x08 //填充flag
#define NGX_HTTP_V2_PRIORITY_FLAG        0x20 //優(yōu)先級(jí)設(shè)置flag

如下是一個(gè)http頭類型frame具體的內(nèi)容格式:


padded和priority由上文頭部的flag決定是否有這兩字段。接下來(lái)占8bit的flag決定header是否需要索引,如果需要,索引號(hào)是多少。

huff(1)表明該字段是否使用了huffman編碼。header_value_len(7)和header_value是具體頭字段的value值

如下是一個(gè)設(shè)置相關(guān)的frame


如下是一個(gè)窗口更新的frame


下邊我們看一個(gè)具體的例子

http2報(bào)文解析

新版本的curl有一個(gè)–http2參數(shù),可以直接指明使用http2進(jìn)行通訊。我們將客戶端命令修改如下:

curl --http2 -k  -I   -L https://10.96.79.14:8443

通過(guò)上邊的gdb跟蹤,我們看到http2初始化入口函數(shù)為ngx_http_v2_init,直接在此處打斷點(diǎn),繼續(xù)跟蹤代碼.跟蹤過(guò)程不再詳細(xì)描述,當(dāng)把報(bào)文讀取進(jìn)緩存之后,我們直接在gdb中bt查看調(diào)用路徑,如下:

#0  ngx_http_v2_state_preface (h2c=0x15a9310, pos=0x164b0b0 "PRI * HTTP/2.0

SM

", end=0x164b11e "")
    at src/http/v2/ngx_http_v2.c:713
#1  0x00000000004bca20 in ngx_http_v2_read_handler (rev=0x16141f0) at src/http/v2/ngx_http_v2.c:415
#2  0x00000000004bcf8a in ngx_http_v2_init (rev=0x16141f0) at src/http/v2/ngx_http_v2.c:328
#3  0x0000000000490a13 in ngx_http_ssl_handshake_handler (c=0x15da400) at src/http/ngx_http_request.c:821
#4  0x000000000047de24 in ngx_ssl_handshake_handler (ev=0x16141f0) at src/event/ngx_event_openssl.c:1390
#5  0x0000000000479637 in ngx_epoll_process_events (cycle=0x1597e30, timer=, flags=)
    at src/event/modules/ngx_epoll_module.c:902
#6  0x000000000046f9db in ngx_process_events_and_timers (cycle=0x1597e30) at src/event/ngx_event.c:242
#7  0x000000000047761c in ngx_worker_process_cycle (cycle=0x1597e30, data=) at src/os/unix/ngx_process_cycle.c:750
#8  0x0000000000475c50 in ngx_spawn_process (cycle=0x1597e30, proc=0x477589 , data=0x0,
    name=0x684922 "worker process", respawn=-3) at src/os/unix/ngx_process.c:199
#9  0x00000000004769aa in ngx_start_worker_processes (cycle=0x1597e30, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359
#10 0x0000000000477cb0 in ngx_master_process_cycle (cycle=0x1597e30) at src/os/unix/ngx_process_cycle.c:131
#11 0x0000000000450ea4 in main (argc=, argv=) at src/core/nginx.c:382

調(diào)用到ngx_http_v2_state_preface這個(gè)函數(shù)之后,開(kāi)始處理http2請(qǐng)求,我們將請(qǐng)求內(nèi)容打印出來(lái)看一下:

(gdb) p end-pos
$1 = 110
(gdb) p *pos@110
$2 = "PRI * HTTP/2.0

SM