英文:
How do I find the remote IP address for a Go http.Response?
问题
http.Request结构体包含请求发送者的远程IP和端口:
// RemoteAddr允许HTTP服务器和其他软件记录发送请求的网络地址,通常用于日志记录。ReadRequest不会填充此字段,也没有定义的格式。此软件包中的HTTP服务器在调用处理程序之前将RemoteAddr设置为“IP:port”地址。
// HTTP客户端会忽略此字段。
RemoteAddr string
http.Response对象没有这样的字段。
我想知道响应我发送的请求的IP地址,即使我将其发送到DNS地址。
我认为net.LookupHost()可能有所帮助,但是1)它可以返回单个主机名的多个IP,2)它会忽略主机文件,除非cgo可用,而在我的情况下不可用。
是否可能检索http.Response的远程IP地址?
英文:
The http.Request struct includes the remote IP and port of the request's sender:
// 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**
The http.Response object has no such field.
I would like to know the IP address that responded to the request I sent, even when I sent it to a DNS address.
I thought that net.LookupHost() might be helpful, but 1) it can return multiple IPs for a single host name, and 2) it ignores the hosts file unless cgo is available, which it is not in my case.
Is it possible to retrieve the remote IP address for an http.Response?
答案1
得分: 7
使用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
英文:
Use the net/http/httptrace package and use the GotConnInfo
hook to capture the net.Conn
and its corresponding Conn.RemoteAddr()
.
This will give you the address the Transport
actually dialled, as opposed to what was resolved in 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)
}
}
Outputs:
~ go run ip.go
2017/02/18 19:38:11 resolved to: 104.16.xx.xxx:443
答案2
得分: -1
我提出的另一个解决方案是在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成功创建
*remoteAddressPtr = conn.RemoteAddr()
return conn, err
}
return hookedDialContext
}
然后,我们可以使用此函数创建一个写入输出参数的DialContext
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,
}
// 像通常使用HTTP客户端一样进行操作,它将设置remoteAddr为远程地址
fmt.Println(remoteAddr.String())
英文:
Another solution I came up with was the hook the DialContext
function in the http client transport. This is a specific solution that lets you modify the http.Client
instead of the request which may be useful.
We first create a function that returns a hooked dial context
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
}
We can then use this function to create a DialContext
that writes to an outparameter
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())
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论