尝试通过 python 套接字升级到 http2

Posted

技术标签:

【中文标题】尝试通过 python 套接字升级到 http2【英文标题】:trying to upgrade to http2 via python socket 【发布时间】:2021-06-29 17:26:29 【问题描述】:

我正在尝试使用 python 套接字升级到 http/2.0。我已经尝试过像这样使用升级标头:Connection: Upgrade 然后Upgrade: h2c。这使服务器响应 http/1.1 200 OK 响应。我现在正在尝试通过 ssl 模块使用 ALPN。 这是通过 ALPN 尝试的代码:

 def connect_socket(self):

        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        context = ssl.create_default_context()

        context.set_alpn_protocols(['h2c'])

        while True:
            try:
                sock.connect(("twitter.com", 443))
                sock = context.wrap_socket(sock)
                return sock
                
            except:
                pass

这只是挂在context.wrap_socket(sock)。如果我摆脱上下文并只使用ssl.wrap_socket() 一切正常,我可以使用 HTTP/1.1 很好。服务器确实支持它,因为使用 PyCurl 的详细模式我可以看到它使用 HTTP/2。 我试过使用h2chttp/2.0h2。 打印我得到的异常,结果是这样的:

[WinError 10056] A connect request was made on an already connected socket

仍未升级的最新代码:

def connect_socket(self):

        while True:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            context = ssl.create_default_context()

            context.set_alpn_protocols(['h2c'])

            context.check_hostname = False
            try:
                sock.connect(("twitter.com", 443))
                sock = context.wrap_socket(sock)
                return sock

            except Exception as e:
                sock.close()
                print(e)

这会返回一个没有错误的套接字,然后发送这个请求:

def test_send_data(self, sock):
        
        unencoded_string = 'GET /noah HTTP/1.1\r\nHost: twitter.com\r\nConnection: keep-alive\r\n\r\n'
        
        sock.send(unencoded_string.encode())

这发送很好,响应的重要部分是这样的:

HTTP/1.1 200 OK\r\ncache-control: no-cache, no-store, must-revalidate,

我的问题是:为什么它只是挂起,我该如何解决?

【问题讨论】:

'h2c' 相比,'h2' 有什么改变吗? 不,事实证明我遇到了异常。详情请查看已编辑的问题。 while 循环是怎么回事?我相信你首先遇到了一个不同的异常,它传递了异常,然后当你再次连接时你会发现你已经在套接字上,因为你处于一个循环中。 哎呀,在最近的评论之前不小心删除了我的评论,后人是“将server_hostname="twitter.com"传递给wrap_socket并使用h2作为协议。应该没问题。摆脱while循环并且不要传递你的异常。” 事实证明你是对的。使用 h2 而不是检查主机名对我来说会挂起,但传递 server_hostname 会起作用。在那之后,我的下一个错误实际上是在我正在寻找 \r\n\r\n 以打破读取循环并返回字节字符串的 recv 函数中。我刚刚意识到这不起作用,在打印套接字接收数据后如果你愿意,发布答案,我会接受+投票。 【参考方案1】:

我认为您的一些异常处理给了您错误的提示。如果您遇到异常,您会一直在尝试再次连接,并且在某些时候 Windows 会抱怨尝试连接到未关闭的套接字。

HTTP/2 的 ALPN 协议应该设置为h2,然后运行你的代码给我一个错误:

ValueError: check_hostname requires server_hostname

添加该参数后,我能够从服务器获得响应,Python 说连接是 H2:

import socket
import ssl


def connect_h2_socket(host):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    context = ssl.create_default_context()
    context.set_alpn_protocols(["h2"])
    sock.connect((host, 443))
    sock = context.wrap_socket(sock, server_hostname=host)
    return sock



s = connect_h2_socket("twitter.com")
print("Selected protocol:", s.selected_alpn_protocol())
print(s.recv())

运行它会为我产生以下结果:

Selected protocol: h2
b'\x00\x00\x06\x04\x00\x00\x00\x00\x00\x00\x04\x00\x01\x00\x00'

【讨论】:

你会不会碰巧知道如何通过 HPACK 解压缩响应?我似乎遇到了很多错误 @Noah 我不知道 HPACK 是什么,最好单独问一个问题!

以上是关于尝试通过 python 套接字升级到 http2的主要内容,如果未能解决你的问题,请参考以下文章

使用 SPDY/http2 的快速应用程序中的套接字挂断

如何将网站升级到 http2 php 应用程序

nginx版本升级到1.10

http2 客户端无法解析 HTTP 升级响应标头

HTTP2试用小记

具有多个套接字连接的 HTTP2 和 TLS