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

如何获取http.Request的发送字节数?HTTPS连接是否适用?

统计HTTP客户端发送的字节数(支持HTTPS)

方法一:统计HTTP协议层面的请求字节数

这种方法统计的是HTTP请求本身的总大小,包括请求行、请求头和请求体,适用于大多数需要管控HTTP请求大小的场景,且完美支持HTTPS。

核心思路是通过自定义RoundTripper包装默认的HTTP传输层,在发送请求前复制请求体避免被消耗,同时统计请求体和请求头/行的字节数:

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
)

// CountingTransport 包装http.RoundTripper,用于统计请求发送的字节数
type CountingTransport struct {
    Transport    http.RoundTripper
    OnRequestSent func(totalBytes int64) // 请求发送完成后的回调,返回总字节数
}

// RoundTrip 实现http.RoundTripper接口
func (ct *CountingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
    var bodyBytes int64
    var clonedBody io.ReadCloser

    // 处理请求体:复制原始内容,避免读取后无法发送
    if req.Body != nil {
        buf := &bytes.Buffer{}
        if _, err := io.Copy(buf, req.Body); err != nil {
            return nil, err
        }
        req.Body.Close()

        // 使用TeeReader在读取请求体时统计字节数
        bodyReader := bytes.NewReader(buf.Bytes())
        teeReader := io.TeeReader(bodyReader, &countingWriter{count: &bodyBytes})
        clonedBody = io.NopCloser(teeReader)
        req.Body = clonedBody
    }

    // 计算请求行和请求头的字节数
    headerBuf := &bytes.Buffer{}
    // 写入请求行(如GET /path HTTP/1.1\r\n)
    fmt.Fprintf(headerBuf, "%s %s %s\r\n", req.Method, req.URL.RequestURI(), req.Proto)
    // 写入请求头
    if err := req.Header.Write(headerBuf); err != nil {
        return nil, err
    }
    // 写入请求头与体之间的空行
    headerBuf.WriteString("\r\n")
    headerSize := int64(headerBuf.Len())

    // 实际发送请求
    resp, err := ct.Transport.RoundTrip(req)
    if err != nil {
        return resp, err
    }

    // 计算总发送字节数并触发回调
    totalBytes := headerSize + bodyBytes
    if ct.OnRequestSent != nil {
        ct.OnRequestSent(totalBytes)
    }

    return resp, nil
}

// countingWriter 用于统计写入的字节数
type countingWriter struct {
    count *int64
}

func (cw *countingWriter) Write(p []byte) (n int, err error) {
    n = len(p)
    *cw.count += int64(n)
    return n, nil
}

使用示例

func main() {
    client := &http.Client{
        Transport: &CountingTransport{
            Transport: http.DefaultTransport,
            OnRequestSent: func(total int64) {
                fmt.Printf("HTTP请求总大小:%d 字节\n", total)
            },
        },
    }

    // 发送HTTPS请求也能正常统计
    resp, err := client.Get("https://example.com")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
}

方法二:统计实际网络传输的字节数(含TLS加密)

如果需要统计实际通过网卡发送的所有字节(包括TLS握手数据、加密后的HTTP内容),可以通过自定义net.Conn来拦截所有写入操作:

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "crypto/tls"
)

// CountingConn 包装net.Conn,统计发送的字节数
type CountingConn struct {
    net.Conn
    bytesSent int64
    onWrite   func(int64)
}

func (cc *CountingConn) Write(b []byte) (n int, err error) {
    n, err = cc.Conn.Write(b)
    cc.bytesSent += int64(n)
    if cc.onWrite != nil {
        cc.onWrite(cc.bytesSent)
    }
    return n, err
}

// CountingDialer 自定义Dialer,返回带统计功能的Conn
type CountingDialer struct {
    Dialer net.Dialer
    OnSent func(int64)
}

func (cd *CountingDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
    conn, err := cd.Dialer.DialContext(ctx, network, addr)
    if err != nil {
        return nil, err
    }
    return &CountingConn{
        Conn:  conn,
        onWrite: cd.OnSent,
    }, nil
}

// NewCountingTransport 创建带字节统计的HTTP Transport
func NewCountingTransport(onSent func(int64)) *http.Transport {
    dialer := &CountingDialer{
        Dialer: net.Dialer{},
        OnSent: onSent,
    }
    return &http.Transport{
        DialContext: dialer.DialContext,
        TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, // 根据需求配置
    }
}

使用示例

func main() {
    transport := NewCountingTransport(func(total int64) {
        fmt.Printf("实际网络发送字节数(含TLS):%d 字节\n", total)
    })
    client := &http.Client{Transport: transport}

    resp, err := client.Post("https://example.com", "application/json", bytes.NewBufferString(`{"key": "value"}`))
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
}

关键说明

  • 方法一统计的是HTTP协议层面的请求大小,不包含TLS加密开销,适合管控业务请求的大小阈值。
  • 方法二统计的是底层网络传输的所有字节,包括TLS握手、加密后的内容,适合精确统计网络流量。
  • 两种方法都完全支持HTTPS请求,因为都是基于Go标准库的传输层扩展,不影响TLS握手和加密流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 14:56:18