如何在Go中模拟curl的--http2-prior-knowledge标志配置HTTP客户端?
在Go中模拟curl的--http2-prior-knowledge行为
问题说明
对接一台不受控服务器时,带负载的POST/PUT/PATCH请求无法触发HTTP/1.1到HTTP/2的协议升级,但使用curl的--http2-prior-knowledge标志直接发起HTTP/2请求可成功;Go默认客户端即使设置ForceAttemptHTTP2: true,请求仍会停留在HTTP/1.1协议。
可行方案
Go的golang.org/x/net/http2包支持直接创建HTTP/2客户端,跳过HTTP/1.1的Upgrade协商流程,完全匹配--http2-prior-knowledge的逻辑。
代码实现
package main import ( "bytes" "fmt" "net" "net/http" "crypto/tls" "golang.org/x/net/http2" ) func main() { // 初始化HTTP/2 Transport,配置为允许HTTP(非HTTPS)直连 transport := &http2.Transport{ AllowHTTP: true, // 自定义DialTLS,HTTP场景下直接建立普通连接并包装为TLS兼容形式 DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { conn, err := net.Dial(network, addr) if err != nil { return nil, err } // 跳过TLS验证(仅HTTP场景使用) return tls.Client(conn, &tls.Config{InsecureSkipVerify: true}), nil }, } defer transport.CloseIdleConnections() // 创建使用HTTP/2 Transport的客户端 client := &http.Client{Transport: transport} // 构造PUT请求 reqBody := bytes.NewBufferString(`{"fields": {"docid": "123"}}`) req, err := http.NewRequest(http.MethodPut, "http://localhost:8081/document/v1/foo", reqBody) if err != nil { fmt.Printf("Failed to create request: %v\n", err) return } req.Header.Set("Content-Type", "application/json") // 发送请求并检查结果 resp, err := client.Do(req) if err != nil { fmt.Printf("Request failed: %v\n", err) return } defer resp.Body.Close() fmt.Printf("Protocol: %s\n", resp.Proto) fmt.Printf("Status Code: %d\n", resp.StatusCode) }
关键说明
- 针对HTTP场景:必须设置
AllowHTTP: true,并自定义DialTLS建立普通TCP连接(因为HTTP2 Transport默认要求TLS) - 针对HTTPS场景:无需
AllowHTTP和自定义DialTLS,直接使用默认http2.Transport即可,它会自动通过TLS ALPN协商直接使用HTTP/2 - 该方式完全绕过HTTP/1.1的升级流程,和curl的
--http2-prior-knowledge行为一致
内容的提问来源于stack exchange,提问作者Bartek
相关产品推荐
相关产品推荐

