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

如何在Golang内置HTTP客户端中获取HTTP代理响应头?

问题

我正在使用Golang默认的net/http库通过HTTP代理发送简单的HTTP GET请求,希望读取代理对CONNECT方法请求的回复内容(也就是第一个代理回复)。

代理回复的明文格式如下:

HTTP/1.1 200 OK
Request-Uid: <some id>
<another header>: <another value>

我的现有代码如下:

...
proxyUrlParsed, errUrl := url.Parse(proxyUrl)
tr := &http.Transport{
   Proxy:   http.ProxyURL(proxyUrlParsed),
}
client := &http.Client{
   Transport: tr,
}
request, errReq := http.NewRequest("GET", targetUrl, nil)
response, errDo := client.Do(request)
// 这里的Response只包含目标资源的响应头,没有中间代理的响应头

我曾通过DialContext部分解决该问题,但需要手动实现部分协议内容,后续维护成本较高且不够便捷。请问是否有更简便巧妙的实现方式?


解决方案

可以通过手动发起CONNECT请求建立代理连接的方式,直接捕获代理的响应,同时复用该连接发送后续的GET请求,无需深入修改http.Transport的底层逻辑,代码更易维护:

package main

import (
	"bufio"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strings"
)

func main() {
	proxyUrl := "http://your-proxy:port"
	targetUrl := "https://example.com"

	// 解析代理和目标URL
	proxyParsed, err := url.Parse(proxyUrl)
	if err != nil {
		panic(err)
	}
	targetParsed, err := url.Parse(targetUrl)
	if err != nil {
		panic(err)
	}

	// 1. 建立到代理的TCP连接
	conn, err := net.Dial("tcp", proxyParsed.Host)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// 2. 发送CONNECT请求到代理
	connectReq := fmt.Sprintf("CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", targetParsed.Host, targetParsed.Host)
	_, err = conn.Write([]byte(connectReq))
	if err != nil {
		panic(err)
	}

	// 3. 读取并解析代理的CONNECT响应
	respReader := bufio.NewReader(conn)
	proxyResp, err := http.ReadResponse(respReader, nil)
	if err != nil {
		panic(err)
	}
	defer proxyResp.Body.Close()

	// 这里可以获取代理响应的Header,比如Request-Uid
	fmt.Println("代理响应头:")
	for k, v := range proxyResp.Header {
		fmt.Printf("%s: %s\n", k, strings.Join(v, ", "))
	}
	if proxyResp.StatusCode != http.StatusOK {
		panic(fmt.Sprintf("代理CONNECT请求失败:%s", proxyResp.Status))
	}

	// 4. 复用该连接,创建自定义Transport发送GET请求到目标服务器
	tr := &http.Transport{
		DialContext: func(ctx net.Context, network, addr string) (net.Conn, error) {
			// 直接返回已经建立好的代理连接
			return conn, nil
		},
	}
	client := &http.Client{Transport: tr}
	getReq, err := http.NewRequest("GET", targetUrl, nil)
	if err != nil {
		panic(err)
	}

	// 发送GET请求并处理目标响应
	targetResp, err := client.Do(getReq)
	if err != nil {
		panic(err)
	}
	defer targetResp.Body.Close()

	fmt.Println("\n目标服务器响应状态:", targetResp.Status)
	// 后续处理目标响应内容...
}

方案优势

  • 无需手动实现完整的HTTP代理协议,直接复用标准库的http.ReadResponse解析代理响应,减少维护成本
  • 逻辑清晰:先处理代理连接,再复用连接发送业务请求,步骤明确
  • 可以完整获取代理的响应头和状态码,满足需求

如果需要更通用的封装,还可以把上述逻辑包装成自定义的http.RoundTripper,方便在多个请求中复用。

内容的提问来源于stack exchange,提问作者greggyNapalm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:35:14