[http] http版本的選擇/http2的啟動


 

前言

http2可以承載在TCP上或者TLS上,分別簡稱h2和h2c

當訪問一個服務的時候,到底用http1.1還是http2,client和server是可以協商的。

[https://www.cnblogs.com/hugetong/p/13410276.html]

TLS承載HTTP2

RFC規定,TLS承載HTTP2時,TLS必須實現ALPN功能。

implementations that support HTTP/2 over TLS MUST use protocol
   negotiation in TLS 

ALPN同時配置上http1和http2讓server選. 見wiki: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation

    Handshake Type: Client Hello (1)
    Length: 141
    Version: TLS 1.2 (0x0303)
    Random: dd67b5943e5efd0740519f38071008b59efbd68ab3114587...
    Session ID Length: 0
    Cipher Suites Length: 10
    Cipher Suites (5 suites)
    Compression Methods Length: 1
    Compression Methods (1 method)
    Extensions Length: 90
    [other extensions omitted]
    Extension: application_layer_protocol_negotiation (len=14)
        Type: application_layer_protocol_negotiation (16)
        Length: 14
        ALPN Extension Length: 12
        ALPN Protocol
            ALPN string length: 2
            ALPN Next Protocol: h2
            ALPN string length: 8
            ALPN Next Protocol: http/1.1

 

nginx里可以這樣配置:這樣配置之后,5000端口可以同時為http2與http1.1提供服務。

        server {
                listen 0.0.0.0:5000 http2 ssl;
                ssl_certificate /data/sni/sni_test3.cer;
                ssl_certificate_key /data/sni/sni_test3.key;
                location / {
                        proxy_pass http://httpt7/;
                }
        }

使用curl測試如下:

http2

╰─>$ curl -v -k  https://t9:5000/
*   Trying 192.168.7.9:5000...
* Connected to t9 (192.168.7.9) port 5000 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=CN; ST=BeiJing; L=BeiJing; O=tong.com; OU=tong; CN=caotong_test3; emailAddress=tong@local
*  start date: Sep 24 10:00:10 2019 GMT
*  expire date: Sep 21 10:00:10 2029 GMT
*  issuer: C=CN; ST=BeiJing; L=BeiJing; O=Tartaglia; CN=TTTrust; emailAddress=ca@tartaglia.org
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x5635fa55b8b0)
> GET / HTTP/2
> Host: t9:5000
> user-agent: curl/7.70.0
> accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200 
< server: tong.localhost
< date: Fri, 31 Jul 2020 07:41:11 GMT
< content-type: text/html
< content-length: 660
< last-modified: Thu, 02 Apr 2020 08:45:48 GMT
< etag: "5e85a63c-294"
< accept-ranges: bytes
< 
<!DOCTYPE html>
<html>
xxx
</html>
* Connection #0 to host t9 left intact

http1

╰─>$ curl -v -k --http1.1 https://t9:5000/
*   Trying 192.168.7.9:5000...
* Connected to t9 (192.168.7.9) port 5000 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=CN; ST=BeiJing; L=BeiJing; O=tong.com; OU=tong; CN=caotong_test3; emailAddress=tong@local
*  start date: Sep 24 10:00:10 2019 GMT
*  expire date: Sep 21 10:00:10 2029 GMT
*  issuer: C=CN; ST=BeiJing; L=BeiJing; O=Tartaglia; CN=TTTrust; emailAddress=ca@tartaglia.org
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
> GET / HTTP/1.1
> Host: t9:5000
> User-Agent: curl/7.70.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: TONG
< Date: Fri, 31 Jul 2020 07:45:34 GMT
< Content-Type: text/html
< Content-Length: 660
< Connection: keep-alive
< Last-Modified: Thu, 02 Apr 2020 08:45:48 GMT
< ETag: "5e85a63c-294"
< Accept-Ranges: bytes
< 
<!DOCTYPE html>
<html>
xxx
</html>
* Connection #0 to host t9 left intact

 

TCP承載HTTP2

nginx配置:

        server {
                listen 0.0.0.0:5000 http2;
                location / {
                        proxy_pass http://httpt7/;
                }
        }

 

curl訪問,必須采用顯示約定的方式進行訪問。顯示約定是指client事先知道了對方是http2,curl的話,要參數 http2-prior-knowledge指定

╰─>$ curl -v -k --http2-prior-knowledge http://t9:5000/
*   Trying 192.168.7.9:5000...
* Connected to t9 (192.168.7.9) port 5000 (#0)
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55f343ece8b0)
> GET / HTTP/2
> Host: t9:5000
> user-agent: curl/7.70.0
> accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200 
< server: TONG
< date: Fri, 31 Jul 2020 07:48:36 GMT
< content-type: text/html
< content-length: 660
< last-modified: Thu, 02 Apr 2020 08:45:48 GMT
< etag: "5e85a63c-294"
< accept-ranges: bytes
< 
<!DOCTYPE html>
<html>
xxx
</html>
* Connection #0 to host t9 left intact

 

一般來說,server應該有能力與client協商版本。但是很遺憾目前nginx還不支持。這個feature已經被提出,還沒有被開發。見:https://trac.nginx.org/nginx/ticket/816

 

h2c的時候怎么協商?

首先client發一個http1.1的請求過去,並攜帶着header 希望upgrade到http2,如果server願意,將回一個101的response

如下例子: (這個例子是nignx不支持,拒絕的情況)

╰─>$ curl -v -k --http2 http://t9:5000/
*   Trying 192.168.7.9:5000...
* Connected to t9 (192.168.7.9) port 5000 (#0)
> GET / HTTP/1.1
> Host: t9:5000
> User-Agent: curl/7.70.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQCAAAAAAIAAAAA
> 
* Received HTTP/0.9 when not allowed

* Closing connection 0
curl: (1) Received HTTP/0.9 when not allowed

 (假如不拒絕,將回復如下:)

     HTTP/1.1 101 Switching Protocols
     Connection: Upgrade
     Upgrade: h2c

     [ HTTP/2 connection ...

 

當server只支持http1時,試圖升到http2的client會回落至http1正常工作,如下:

╰─>$ curl -v -k --http2 http://t9:5000/
*   Trying 192.168.7.9:5000...
* Connected to t9 (192.168.7.9) port 5000 (#0)
> GET / HTTP/1.1
> Host: t9:5000
> User-Agent: curl/7.70.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQCAAAAAAIAAAAA
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: TONG
< Date: Fri, 31 Jul 2020 08:01:12 GMT
< Content-Type: text/html
< Content-Length: 660
< Connection: keep-alive
< Last-Modified: Thu, 02 Apr 2020 08:45:48 GMT
< ETag: "5e85a63c-294"
< Accept-Ranges: bytes
< 
<!DOCTYPE html>
<html>
xxx
</html>
* Connection #0 to host t9 left intact

 

總結一下

client訪問http2 server的時候,分兩種情況,一種是自適應版本的協商方式。一種是顯式指定版本為http2的方式。

自適應的方式分兩種情況tls承載與tcp承載。

tls承載時,使用ALPN。tcp承載時,client使用Upgrade header嘗試http2,serveri接受便回復101開始http2,server不接受就正常用http1答復。

 

參考:https://tools.ietf.org/html/rfc7540#section-3


免責聲明!

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



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