Golang使用utls搭配fhttp时出现http2握手失败该如何解决?
修复方案
错误根因
报错是因为TLS协商阶段没有正确声明支持HTTP2协议,导致HTTP/1.x传输层收到了HTTP2的二进制帧无法解析,最终触发握手失败。utls配置不需要放到HTTP2传输层t2中,保留在t1的DialTLSContext中即可,原有代码的问题在于缺少ALPN协议声明和存在冗余配置。
核心修改点
- 给utls的
Config新增NextProtos字段,显式声明支持h2(HTTP2)和http/1.1协议,保证服务端返回对应协议的响应 - 移除手动赋值
t1.H2transport = t2的冗余代码,http2.ConfigureTransports方法会自动完成HTTP2传输层的注入,重复赋值会导致逻辑冲突
修改后完整代码
package main import ( "context" "fmt" "io/ioutil" "log" "net" tls "github.com/refraction-networking/utls" http "github.com/useflyent/fhttp" "github.com/useflyent/fhttp/http2" ) func main() { // 创建fhttp传输层 t1 := &http.Transport{ DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { tcpConn, err := (&net.Dialer{}).DialContext(ctx, network, addr) if err != nil { return nil, err } // 新增NextProtos字段,显式声明支持的协议 config := tls.Config{ ServerName: "google.com", NextProtos: []string{"h2", "http/1.1"}, } tlsConn := tls.UClient(tcpConn, &config, tls.HelloChrome_Auto) err = tlsConn.Handshake() if err != nil { return nil, fmt.Errorf("uTlsConn.Handshake() error: %w", err) } return tlsConn, nil }, ForceAttemptHTTP2: true, } // 配置HTTP2传输层,方法会自动将生成的t2注入到t1中,无需手动赋值 _, err := http2.ConfigureTransports(t1) if err != nil { log.Fatalf(err.Error()) } client := &http.Client{ Transport: t1, } req, err := http.NewRequest("GET", "https://www.google.com/", nil) req.Header = http.Header{ "sec-ch-ua": {"\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\", \"Google Chrome\";v=\"90\""}, "sec-ch-ua-mobile": {"?0"}, "upgrade-insecure-requests": {"1"}, "user-agent": {"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"}, "accept": {"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, "sec-fetch-site": {"none"}, "sec-fetch-mode": {"navigate"}, "sec-fetch-user": {"?1"}, "sec-fetch-dest": {"document"}, "accept-encoding": {"gzip, deflate, br"}, } resp, err := client.Do(req) if err != nil { fmt.Println(err) } else { fmt.Println(resp.StatusCode) body, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(body)) } }
内容的提问来源于stack exchange,提问作者JJ Cauton
相关产品推荐
相关产品推荐

