如何要求客户端将HTTP2连接降级到HTTP1.1

如何要求客户端将HTTP2连接降级到HTTP1.1,http,http2,Http,Http2,这主要是出于好奇;假设我有一台兼容HTTP1.1的服务器。我没有资源/无法向该服务器添加完整的HTTP2支持 我仍然希望能够通过告诉他们使用HTTP1.1来服务(PRI*HTTP/2.0\r\n\r\nSM\r\n\r\n)。请注意,这是关于不使用带有升级头的HTTP1.1请求启动连接的客户端 这是我想要实现的行为: CLIENT SERVER | ------ http2 GET --> | | <----need http1 ---- |

这主要是出于好奇;假设我有一台兼容HTTP1.1的服务器。我没有资源/无法向该服务器添加完整的HTTP2支持

我仍然希望能够通过告诉他们使用HTTP1.1来服务(
PRI*HTTP/2.0\r\n\r\nSM\r\n\r\n
)。请注意,这是关于不使用带有
升级
头的HTTP1.1请求启动连接的客户端

这是我想要实现的行为:

CLIENT                 SERVER
  | ------ http2 GET --> |
  | <----need http1 ---- |
  | ------ http1 GET --> |
  ... (normal http1 processing)
在一个小测试程序中,使用上面的和
fwrite(frame,sizeof(frame),1,stdout)
创建要发送到客户端的帧(
/a.out>frame.bin
)。然后我启动我的“服务器”:

cat emptysettings.bin rststream.bin goaway.bin-| netcat-l-p 8888-s 127.0.0.1
使用http2客户端,我得到

#这是在发送空设置和RST#U流帧时发生的
nghttp-vhttp://127.0.0.1:8888
[0.000]已连接
[0.000]recv设置帧
(niv=0)
[0.000][无效;错误=协议错误]recv RST_流帧
(错误代码=需要HTTP_1_1_(0x0d))
[0.000]发送设置帧
(niv=2)
[设置\u最大\u并发\u流(0x03):100]
[设置\初始\窗口大小(0x04):65535]
[0.000]发送采空区框架
(最后一个\u流\u id=0,错误\u代码=协议\u错误(0x01),不透明\u数据(26)=[RST\u流:流处于空闲状态])
[错误]请求http://127.0.0.1:8888 失败:不允许请求标头
有些请求没有得到处理。总计=1,已处理=0
。。。或者使用卷曲

curl-v--http2先验知识http://127.0.0.1:8888
*正在尝试127.0.0.1:8888。。。
*TCP_节点集
*连接到127.0.0.1(127.0.0.1)端口8888(#0)
*使用HTTP2,服务器支持多用途
*连接状态已更改(HTTP/2已确认)
*升级后正在将流缓冲区中的HTTP/2数据复制到连接缓冲区:len=0
*使用流ID:1(易处理0x16887f0)
>GET/HTTP/2
>主持人:127.0.0.1:8888
>用户代理:curl/7.66.0
>接受:*/*
> 
*连接状态已更改(最大并发流==4294967295)!
*停止暂停流!
*0到主机127.0.0.1的连接保持不变
curl:(16)HTTP2帧层中的错误
。。。因此:我需要向启动http2连接的客户端发送什么响应,以便他们将连接降级到HTTP1.1?

发送
HTTP/1.1505 hmmm\n\n
(505=)也没有帮助

我需要向启动http2连接的客户端发送什么响应,以便他们将连接降级到HTTP1.1

我认为你想做的没有意义。首先,您为什么要接受HTTP/2连接?只要预先拒绝就行了。为什么实现HTTP/2只是为了说你不支持HTTP/2

但是,如果确实要这样做,则需要使用
GOAWAY
帧和
HTTP\u 1\u REQUIRED
错误代码关闭连接。您不能降级连接

为了给这个答案提供更多的背景知识,我们考虑了很多方法来确保在不支持HTTP/2的情况下不建立HTTP/2连接。基本上有3种建立HTTP/2连接的方法:

  • HTTPS-作为使用ALPN扩展的TLS协商的一部分。只有当服务器实际播发HTTP/2支持时,该操作才会成功
  • HTTP—作为升级舞蹈的一部分—只有在服务器实际宣传HTTP/2支持时才会成功
  • HTTP-作为直接连接的一部分。这似乎是你的建议。这假设HTTP/2支持,因此风险更大,因此只有在很可能知道服务器支持HTTP/2(例如,您同时拥有客户机和服务器)时才应该这样做,并且客户机应该准备好处理Prefore连接失败的情况,以防发现错误
  • HTTPS是HTTP/2用例的绝大多数(当然是来自不使用HTTP时不支持HTTP/2的浏览器),但所有三种方法都有显式检查作为连接建立的一部分。因此,不可能有一个只支持HTTP/1.1的HTTP/2连接(此时,我很难理解我写的最后一句话有什么意义!)。但是奇怪的事情发生了

    因此,忽略这一点,现在继续,在
    RST\u流
    帧上使用错误代码
    HTTP\u 1\u REQUIRED
    ,旨在拒绝单个流,而不是整个连接。因此,需要WebSocket的客户端证书(HTTP/2-不支持)的请求(最初在HTTP/2下不支持,但)应该拒绝带有此错误代码、带有
    RST\u流和该错误代码的请求。连接应该保持在原位,以便可以发出任何其他兼容HTTP/2的请求。理论上,您可以用相同的方式拒绝每个流请求,但如果只为了对发送的每个请求说“请使用HTTP/1.1”,而费心建立HTTP/2连接,这似乎有点愚蠢和低效

    如果要拒绝整个连接,则应不要使用
    RST\u流
    frame(),而是使用
    GOAWAY
    frame()。此
    GOAWAY
    消息可以使用错误代码
    HTTP\u 1\u REQUIRED
    。这将关闭整个连接,客户端将需要重新连接-在这一点上,如上所述,您不应该接受HTTP/2连接。无法降级连接。从技术上讲,它可以使用升级方法来实现这一点,但这只是一个建议,而不是命令

    现在,让我们看看您的代码和错误:

       0x3, // Type: RST_STREAM
       0, // Flags
       0 & 0x7F, 0, 0, 1, // Stream identifier 1 ; also tried with 0
    
    首先,您不能在流0上使用
    RST\u STREAM
    ,因此您应该只在流id>0上发送此消息:

    RST_流帧必须是asso
    [INVALID; error=Protocol error] recv RST_STREAM frame <length=4, flags=0x00, stream_id=1>
          (error_code=HTTP_1_1_REQUIRED(0x0d))