How to bind an http.Client in Go to an IP Address

2019-02-05 08:14发布

I have a client machine with multiple NICs, how do I bind an http.Client in Go to a certain NIC or to a certain SRC IP Address?

Say you have some very basic http client code that looks like:

package main

import (
    "net/http"
)

func main() {
    webclient := &http.Client{}
    req, _ := http.NewRequest("GET", "http://www.google.com", nil)
    httpResponse, _ := webclient.Do(req)
    defer httpResponse.Body.Close()
}

Is there a way to bind to a certain NIC or IP?

标签: http go ip
2条回答
smile是对你的礼貌
2楼-- · 2019-02-05 08:37

Here is a fully working example that incorporates the answer from Tim. I also broke out all of the nested pieces to make it easier to read and learn from.

package main

import (
    "fmt"
    "io/ioutil"
    "net"
    "net/http"
    "time"
)

func main() {
    localAddr, err := net.ResolveIPAddr("ip", "10.128.64.219")
    if err != nil {
        panic(err)
    }

    localTCPAddr := net.TCPAddr{
        IP: localAddr.IP,
    }

    d := net.Dialer{
        LocalAddr: &localTCPAddr,
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
    }

    tr := &http.Transport{
        Proxy:               http.ProxyFromEnvironment,
        Dial:                d.Dial,
        TLSHandshakeTimeout: 10 * time.Second,
    }

    webclient := &http.Client{Transport: tr}

    // Use NewRequest so we can change the UserAgent string in the header
    req, err := http.NewRequest("GET", "http://www.google.com:80", nil)
    if err != nil {
        panic(err)
    }

    res, err := webclient.Do(req)
    if err != nil {
        panic(err)
    }

    fmt.Println("DEBUG", res)
    defer res.Body.Close()

    content, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%s", string(content))
}
查看更多
做个烂人
3楼-- · 2019-02-05 08:49

Similar to this question, you need to set the http.Client.Transport field. Setting it to an instance of net.Transport allows you to specify which net.Dialer you want to use. net.Dialer then allows you to specify the local address to make connections from.

Example:

localAddr, err := net.ResolveIPAddr("ip", "<my local address>")
if err != nil {
  panic(err)
}

// You also need to do this to make it work and not give you a 
// "mismatched local address type ip"
// This will make the ResolveIPAddr a TCPAddr without needing to 
// say what SRC port number to use.
localTCPAddr := net.TCPAddr{
    IP: localAddr.IP,
}


webclient := &http.Client{
    Transport: &http.Transport{
        Proxy: http.ProxyFromEnvironment,
        DialContext: (&net.Dialer{
            LocalAddr: &localTCPAddr,
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
            DualStack: true,
        }).DialContext,
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
}
查看更多
登录 后发表回答