[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

posted on 2020-07-31 16:09  toong  阅读(3672)  评论(0编辑  收藏  举报