You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 23:25:35