如何找到Go http.Response的远程IP地址?

edqdpe6u  于 2023-01-06  发布在  Go
关注(0)|答案(2)|浏览(128)

http.request结构体包含请求发送方的远程IP和端口:

// RemoteAddr allows HTTP servers and other software to record
    // the network address that sent the request, usually for
    // logging. This field is not filled in by ReadRequest and
    // has no defined format. The HTTP server in this package
    // sets RemoteAddr to an "IP:port" address before invoking a
    // handler.
    // This field is ignored by the HTTP client.
    **RemoteAddr string**

http.response对象没有这样的字段。
我想知道响应我发送的请求的IP地址,即使我将其发送到DNS地址。
我认为net.lookupHost()可能会有帮助,但是1)它可以为一个主机名返回多个IP,2)除非cgo可用,否则它会忽略hosts文件,而在我的情况下cgo不可用。
是否可以检索远程IP地址以获取http.response?

jw5wzhpr

jw5wzhpr1#

使用net/http/httptrace包和GotConnInfo钩子来捕获net.Conn及其对应的Conn.RemoteAddr()
这将为您提供Transport实际拨打的地址,而不是在DNSDoneInfo中解析的地址:

package main

import (
    "log"
    "net/http"
    "net/http/httptrace"
)

func main() {
    req, err := http.NewRequest("GET", "https://example.com/", nil)
    if err != nil {
        log.Fatal(err)
    }

    trace := &httptrace.ClientTrace{
        GotConn: func(connInfo httptrace.GotConnInfo) {
            log.Printf("resolved to: %s", connInfo.Conn.RemoteAddr())
        },
    }

    req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))

    client := &http.Client{}
    _, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
}

输出:

~ go run ip.go
2017/02/18 19:38:11 resolved to: 104.16.xx.xxx:443
yc0p9oo0

yc0p9oo02#

我想到的另一个解决方案是在http客户端传输中使用DialContext函数的钩子,这是一个特定的解决方案,它允许你修改http.Client而不是请求,这可能是有用的。
我们首先创建一个返回挂接拨号上下文的函数

func remoteAddressDialHook(remoteAddressPtr *net.Addr) func(ctx context.Context, network string, address string) (net.Conn, error) {
    hookedDialContext := func(ctx context.Context, network, address string) (net.Conn, error) {
        originalDialer := &net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }

        conn, err := originalDialer.DialContext(ctx, network, address)
        if err != nil {
            return nil, err
        }

        // conn was successfully created
        *remoteAddressPtr = conn.RemoteAddr()
        return conn, err
    }

    return hookedDialContext
}

然后,我们可以使用此函数创建一个DialContext,它写入一个out参数

var remoteAddr net.Addr
    customTransport := &http.Transport{
        Proxy:                 http.ProxyFromEnvironment,
        DialContext:           remoteAddressDialHook(&remoteAddr),
        ForceAttemptHTTP2:     true,
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
    customHttpClient := http.Client{
        Transport: customTransport,
    }

    // do what you normally would with a http client, it will then set the remoteAddr to be the remote address
    fmt.Println(remoteAddr.String())

相关问题