详解为新版Apache服务器开启HTTP2⽀持的⽅法
IETF会让所有互联⽹通路默认选择的⽅式来引⼊加密,互联⽹专家们将新⼀代加密协议称为“HTTP 2.0”。
协议⽬标:
异步连接多路复⽤;
头部压缩;
请求/响应管线化;
保持与HTTP 1.1语义的向后兼容性也是该版本的⼀个关键⽬标。SPDY是⼀种HTTP兼容协议,由Google发起,Chrome、Opera、Firefox以及Amazon Silk等浏览器均已提供⽀持。HTTP实现的瓶颈之⼀是其并发要依赖于多重连接。HTTP管线化技术可以缓解这个问题,但也只能做到部分多路复⽤。此外,已经证实,由于存在中间⼲扰,浏览器⽆法采⽤管线化技术。SPDY在单个连接之上增加了⼀个帧层,⽤以多路复⽤多个并发流。帧层针对HTTP类的请求响应流进⾏了优化,因此运⾏在HTTP之上的应⽤,对应⽤开发者⽽⾔只要很⼩的修改甚⾄⽆需修改就可以运⾏在SPDY之上。SPDY对当前的HTTP协议有4个改进:
多路复⽤请求;
对请求划分优先级;
压缩HTTP头;
服务器推送流(即Server Push技术);
SPDY试图保留HTTP的现有语义,所以cookies、ETags等特性都是可⽤的。[3]
如何在 Apache 中启⽤ HTTP/2
刚发布的 Apache httpd 2.4.17 终于⽀持 HTTP/2 了。这个页⾯给出了⼀些如何构建/部署/配置的建议。⽬的是为了⼤家发现 bugs 时能升级它,或者给⼀些能更好⼯作的建议。
最后,这会归并回到官⽅ Apache ⽂档,这⾥只会留下⼀个到那⾥的链接。暂时我们还没做到。
编译⽀持 HTTP/2
在你编译版本之前,你要进⾏⼀些配置。这⾥有成千上万的选项。和 HTTP/2 相关的是:
--enable-http2
启⽤在 Apache 服务器内部实现该协议的 ‘http2' 模块。
--with-nghttp2=<dir>
指定 http2 模块需要的 libnghttp2 模块的⾮默认位置。如果 nghttp2 是在默认的位置,配置过程会⾃动采⽤。
--enable-nghttp2-staticlib-deps
很少⽤到的选项,你可能想将 nghttp2 库静态链接到服务器⾥。在⼤部分平台上,只有在不到共享 nghttp2 库时才有⽤。
如果你想⾃⼰编译 nghttp2,你可以到 查看⽂档。最新的 Fedora 以及其它版本已经附带了这个库。
ALPN ⽤来协商negotiate服务器和客户端之间的协议。如果你服务器上 TLS 库还没有实现 ALPN,客户端只能通过 HTTP/1.1 通信。那么,可以和 Apache 链接并⽀持它的是什么库呢?
OpenSSL 1.0.2 及其以后。
(别的我也不知道了)
如果你的 OpenSSL 库是 Linux 版本⾃带的,这⾥使⽤的版本号可能和官⽅ OpenSSL 版本的不同。如果不确定的话检查⼀下你的 Linux 版本吧。
配置
另⼀个给服务器的好建议是为 http2 模块设置合适的⽇志等级。添加下⾯的配置:
# 放在某个地⽅的这样⼀⾏
LoadModule http2_module modules/mod_http2.so
<IfModule http2_module>
LogLevel http2:info
</IfModule>
当你启动服务器的时候,你可以在错误⽇志中看来类似的⼀⾏:
[timestamp] [http2:info] [pid XXXXX:tid numbers]
mod_http2 (v1.0.0, nghttp2 1.3.4),
协议
那么,假设你已经编译部署好了服务器, TLS 库也是最新的,你启动了你的服务器,打开了浏览器。。。你怎么知道它在⼯作呢?
如果除此之外你没有添加其它的服务器配置,很可能它没有⼯作。
你需要告诉服务器在哪⾥使⽤该协议。默认情况下,你的服务器并没有启动 HTTP/2 协议。因为这样⽐较安全,也许才能让你已有的部署可以继续⼯作。
你可以⽤新的 Protocols 指令启⽤ HTTP/2 协议:
# 对于 https 服务器
Protocols h2 http/1.1
...
# 对于 http 服务器
Protocols h2c http/1.1
你可以给整个服务器或者指定的 vhosts 添加这个配置。
# 对于 http 服务器
Protocols h2c http/1.1
这⾥有⼀些⽀持 h2c 的客户端(和客户端库)。我会在下⾯介绍:
curl
Daniel Stenberg 维护的⽤于访问⽹络资源的命令⾏客户端 curl 当然⽀持。如果你的系统上有 curl,有⼀个简单的⽅法检查它是否⽀持 http/2:
sh> curl -V
curl 7.43.0 (x86_64-apple-darwin15.0) libcurl/7.43.0 SecureTransport zlib/1.2.5
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixS
ockets
不好了。这些功能中没有 'HTTP2'。你想要的是下⾯这样:
sh> curl -V
url 7.45.0 (x86_64-apple-darwin15.0.0) libcurl/7.45.0 OpenSSL/1.0.2d zlib/1.2.8 nghttp2/1.3.4
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets
如果你的 curl ⽀持 HTTP2 功能,你可以⽤⼀些简单的命令检查你的服务器:
sh> curl -v --http2 <yourserver>/
...
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
curl是什么命令
> HTTP2-Settings: AAMAAABkAAQAAP__
>
< HTTP/1.1 101 Switching Protocols
< Upgrade: h2c
< Connection: Upgrade
* Received 101
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
...
<the resource>
恭喜,如果看到了有 ... 的⾏就表⽰它正在⼯作!
有⼀些情况不会发⽣ HTTP/2 的升级切换Upgrade。如果你的第⼀个请求有请求数据body,例如你上传⼀个⽂件时,就不会触发升级切换。h2c 限制部分有详细的解释。
nghttp
nghttp2 可以⼀同编译它⾃⼰的客户端和服务器。如果你的系统中有该客户端,你可以简单地通过获取⼀个资源来验证你的安装:
sh> nghttp -uv <yourserver>/
[ 0.001] Connected
[ 0.001] HTTP Upgrade request
...
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAAQAAP__
...
[ 0.005] HTTP Upgrade response
HTTP/1.1 101 Switching Protocols
Upgrade: h2c
Connection: Upgrade
[ 0.006] HTTP Upgrade success
...
这和我们上⾯ curl 例⼦中看到的 Upgrade 输出很相似。
有另外⼀种在命令⾏参数中不⽤ -u 参数⽽使⽤ h2c 的⽅法。这个参数会指⽰ nghttp 进⾏ HTTP/1 升级切换过程。但如果我们不使⽤呢?
sh> nghttp -v <yourserver>/
[ 0.002] Connected
[ 0.002] send SETTINGS frame
...
[ 0.002] send HEADERS frame
; END_STREAM | END_HEADERS | PRIORITY
(padlen=0, dep_stream_id=11, weight=16, exclusive=0)
; Open new stream
:method: GET
:path: /
:scheme: http
...
连接马上使⽤了 HTTP/2!这就是协议中所谓的直接direct模式,当客户端发送⼀些特殊的 24 字节到服务器时就会发⽣:
0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
⽤ ASCII 表⽰是:
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
⽀持 h2c 的服务器在⼀个新的连接中看到这些信息就会马上切换到 HTTP/2。HTTP/1.1 服务器则认为是⼀个可笑的请求,响应并关闭连接。
因此,直接模式只适合于那些确定服务器⽀持 HTTP/2 的客户端。例如,当前⼀个升级切换过程成功了的时候。
直接模式的魅⼒是零开销,它⽀持所有请求,即使带有请求数据部分(查看h2c 限制)。
对于 2.4.17 版本,明⽂连接时默认启⽤ H2Direct 。但是有⼀些模块和这不兼容。因此,在下⼀版本中,默认会设置为off,如果你希望你的服务器⽀持它,你需要设置它为:
H2Direct on
连接 (h2)
当你的 mod_h[ttp]2 可以⽀持 h2c 连接时,那就可以⼀同启⽤ h2 兄弟了,现在的浏览器仅⽀持它和 https: ⼀同使⽤。
HTTP/2 标准对 https:(TLS)连接增加了⼀些额外的要求。上⾯已经提到了 ALNP 扩展。另外的⼀个要求是不能使⽤特定⿊名单中的加密算法。
尽管现在版本的 mod_h[ttp]2 不增强这些算法(以后可能会),但⼤部分客户端会这么做。如果让你的浏览器使⽤不恰当的算法打开 h2 服务器,你会看到不明确的警告 INADEQUATE_SECURITY,浏
览器会拒接连接。
⼀个可⾏的 Apache SSL 配置类似:
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM SSLProtocol All -SSLv2 -SSLv3
...
(是的,这确实很长。)
这⾥还有⼀些应该调整,但不是必须调整的 SSL 配置参数:SSLSessionCache, SSLUseStapling 等,其它地⽅也有介绍这些。例如 Ilya Grigorik 写的⼀篇超赞的博客:⾼性能浏览器⽹络。
curl
再次回到 shell 使⽤ curl(查看上⾯的“curl h2c”章节了解要求),你也可以通过 curl ⽤简单的命令检测你的服务器:
sh> curl -v --http2 <yourserver>/
...
* ALPN, offering h2
* ALPN, offering http/1.1
...
* ALPN, server accepted to use h2
...
<the resource>
恭喜你,能正常⼯作啦!如果还不能,可能原因是:
你的 curl 不⽀持 HTTP/2。查看上⾯的“检测 curl”⼀节。
你的 openssl 版本太低不⽀持 ALPN。
不能验证你的证书,或者不接受你的算法配置。尝试添加命令⾏选项 -k 停⽤ curl 中的这些检查。如果可以⼯作,就重新配置你的 SSL 和证书。
nghttp
我们已经在 h2c 讨论过 nghttp。如果你⽤它来进⾏ https: 连接,你会看到类似下⾯的信息:
sh> nghttp <yourserver>/
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
这有两种可能,你可以通过添加 -v 来检查。如果是:
sh> nghttp -v <yourserver>/
[ 0.034] Connected
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
这意味着你服务器使⽤的 TLS 库没有实现 ALPN。有时候正确安装有点困难。多看看 Stackoverflow 吧。
你看到的也可能是:
sh> nghttp -v <yourserver>/
[ 0.034] Connected
The negotiated protocol: http/1.1
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)
这表⽰ ALPN 能正常⼯作,但并没有⽤ h2 协议。你需要像上⾯介绍的那样检查你服务器上的 Protocols 配置。如果⼀开始在 vhost 部分设置不能正常⼯作,试着在通⽤部分设置它。
Firefox
你可以在 Firefox 浏览器中打开开发者⼯具,在那⾥的⽹络标签页查看 HTTP/2 连接。当你打开了 HTTP/2 并重新刷新 html 页⾯时,你会看到类似下⾯的东西:
在响应头中,你可以看到奇怪的 X-Firefox-Spdy 条⽬中列出了 “h2”。这表⽰在这个 https: 连接中使⽤了 HTTP/2。
Google Chrome
在 Google Chrome 中,你在开发者⼯具中看不到 HTTP/2 指⽰器。相反,Chrome ⽤特殊的地址 chrome://net-internals/#http2 给出了相关信息。(LCTT 译注:Chrome 已经有⼀个 “HTTP/2 and SPDY indicator” 可以很好的在地址栏识别 HTTP/2 连接)
如果你打开了⼀个服务器的页⾯,可以在 Chrome 中查看那个 net-internals 页⾯,你可以看到类似下⾯这样:
如果你的服务器在上⾯的列表中,就表⽰它正在⼯作。
Microsoft Edge
Windows 10 中 Internet Explorer 的继任者 Edge 也⽀持 HTTP/2。你也可以在开发者⼯具的⽹络标签页看到 HTTP/2 协议。
Safari
在 Apple 的 Safari 中,打开开发者⼯具,那⾥有个⽹络标签页。重新加载你的服务器上的页⾯,并在开发者⼯具中选择显⽰了加载的那⾏。如果你启⽤了在右边显⽰详细视图,看 Status 部分。那⾥显⽰了 HTTP/2.0 200,像这样:
重新协商
https:连接重新协商是指正在运⾏的连接中特定的 TLS 参数会发⽣变化。在 Apache httpd 中,你可以在 directory 配置中改变 TLS 参数。如果进来⼀个获取特定位置资源的请求,配置的 TLS 参数会和当前的 TLS 参数进⾏对⽐。如果它们不相同,就会触发重新协商。
这种最常见的情形是算法变化和客户端证书。你可以要求客户访问特定位置时需要通过验证,或者对于特定资源,你可以使⽤更安全的、对 CPU 压⼒更⼤的算法。
但不管你的想法有多么好,HTTP/2 中都不可以发⽣重新协商。在同⼀个连接上会有 100 多个请求,那么重新协商该什么时候做呢?
对于这种配置,现有的 mod_h[ttp]2 还没有办法。如果你有⼀个站点使⽤了 TLS 重新协商,别在上⾯启⽤ h2!
当然,我们会在后⾯的版本中解决这个问题,然后你就可以安全地启⽤了。
限制
⾮ HTTP 协议
实现除 HTTP 之外协议的模块可能和 mod_http2 不兼容。这在其它协议要求服务器⾸先发送数据时⽆疑会发⽣。
NNTP 就是这种协议的⼀个例⼦。如果你在服务器中配置了 mod_nntp_like_ssl,那么就不要加载 mod_http2。等待下⼀个版本。
h2c 限制
h2c 的实现还有⼀些限制,你应该注意:
在虚拟主机中拒绝 h2c
你不能对指定的虚拟主机拒绝 h2c 直连。连接建⽴⽽没有看到请求时会触发直连,这使得不可能预先知道 Apache 需要查哪个虚拟主机。
有请求数据时的升级切换
对于有数据的请求,h2c 升级切换不能正常⼯作。那些是 PUT 和 POST 请求(⽤于提交和上传)。如果你写了⼀个客户端,你可能会⽤⼀个简单的 GET 或者 OPTIONS * 来处理那些请求以触发升级切换。
原因从技术层⾯来看显⽽易见,但如果你想知道:在升级切换过程中,连接处于半疯状态。请求按照 HTTP/1.1 的格式,⽽响应使⽤ HTTP/2 帧。如果请求有⼀个数据部分,服务器在发送响应之前需要读取整个数据。因为响应可能需要从客户端处得到应答⽤于流控制及其它东西。但如果仍在发送 HTTP/1.1 请求,客户端就仍然不能以 HTTP/2 连接。
为了使⾏为可预测,⼏个服务器在实现上决定不在任何带有请求数据的请求中进⾏升级切换,即使请求数据很⼩。
302 时的升级切换
有重定向发⽣时,当前的 h2c 升级切换也不能⼯作。看起来 mod_http2 之前的重写有可能发⽣。这当然不会导致断路,但你测试这样的站点也许会让你迷惑。
h2 限制
这⾥有⼀些你应该意识到的 h2 实现限制:
连接重⽤
HTTP/2 协议允许在特定条件下重⽤ TLS 连接:如果你有带通配符的证书或者多个 AltSubject 名称,浏览器可能会重⽤现有的连接。例如:
在重新打开⼀个新的连接之前,浏览器看到它有⼀个到 的连接并且证书对于 也可⽤。因此,它在第⼀个连接上⾯发送第⼆个标签页的请求。
这种连接重⽤是刻意设计的,它使得使⽤了 HTTP/1 切分sharding来提⾼效率的站点能够不需要太多变化就能利⽤ HTTP/2。
Apache mod_h[ttp]2 还没有完全实现这点。如果 和 是不同的虚拟主机, Apache 不会允许这样的连接重⽤,并会告知浏览器状态码 421 Misdirected Request。浏览器会意识到它需要重新打开⼀个到 的连接。这仍然能⼯作,只是会降低⼀些效率。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。