首页 > 解决方案 > 尝试通过 python 套接字升级到 http2

问题描述

我正在尝试使用 python 套接字升级到 http/2.0。我已经尝试过像这样使用升级标头:Connection: Upgradethen 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。
我试过使用h2c http/2.0and h2
打印我得到的异常,结果是这样的:

[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,

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

标签: pythonsocketsssl

解决方案


我认为您的一些异常处理给了您错误的提示。如果您遇到异常,您会一直在尝试再次连接,并且在某些时候 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'

推荐阅读