[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答复。