Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Server's own address in a server using net/http?

Tags:

http

go

I would like to write a HTTP server using the Go's net/http package, with the reaction that would depend on server side IP address of the HTTP connection.

In other words, what I am looking for is equivalent of CGI's "SERVER_ADDR" variable.

The closest field in http.Request is "Host" - but since it will only be equal to an address if the request uses a literal address, I can not use it (the server might be used by name).

Looking at the sources at https://golang.org/src/net/http/server.go, seems the only way to get to the server's address is to Hijack() the connection within a handler and implement subsequent HTTP parsing for the subsequent requests on the same connection, but it looks very inelegant to say the least...

It seems like the ideal solution would be to have the http/request and http/server in the golang standard library modified as follows:

diff -u go-stock-library/request.go ./request.go
--- go-stock-library/request.go 2016-04-13 17:31:48.000000000 +0200
+++ ./request.go    2016-04-13 17:32:40.000000000 +0200
@@ -227,6 +227,15 @@
    // This field is ignored by the HTTP client.
    RemoteAddr string

+   // LocalAddr allows HTTP servers and other software to record
+   // the network address that the request was sent to, usually for
+   // logging. This field is not filled in by ReadRequest and
+   // has no defined format. The HTTP server in this package
+   // sets LocalAddr to an "IP:port" address before invoking a
+   // handler.
+   // This field is ignored by the HTTP client.
+   LocalAddr string
+
    // RequestURI is the unmodified Request-URI of the
    // Request-Line (RFC 2616, Section 5.1) as sent by the client
    // to a server. Usually the URL field should be used instead.
diff -u go-stock-library/server.go ./server.go
--- go-stock-library/server.go  2016-04-13 17:29:19.000000000 +0200
+++ ./server.go 2016-04-13 17:31:38.000000000 +0200
@@ -161,6 +161,13 @@
    // This is the value of a Handler's (*Request).RemoteAddr.
    remoteAddr string

+   // serverAddr is rwc.LocalAddr().String(). It is not populated synchronously
+   // inside the Listener's Accept goroutine, as some implementations block.
+   // It is populated immediately inside the (*conn).serve goroutine.
+   // This is the value of a Handler's (*Request).LocalAddr.
+   localAddr string
+   
+
    // tlsState is the TLS connection state when using TLS.
    // nil means not TLS.
    tlsState *tls.ConnectionState
@@ -736,6 +743,7 @@
    delete(req.Header, "Host")

    req.RemoteAddr = c.remoteAddr
+   req.LocalAddr = c.localAddr
    req.TLS = c.tlsState
    if body, ok := req.Body.(*body); ok {
        body.doEarlyClose = true
@@ -1382,6 +1390,7 @@
 // Serve a new connection.
 func (c *conn) serve() {
    c.remoteAddr = c.rwc.RemoteAddr().String()
+   c.localAddr = c.rwc.LocalAddr().String()
    defer func() {
        if err := recover(); err != nil {
            const size = 64 << 10

and then use the new LocalAddr in the code in a nice and clean way.

Is there a cleaner way to do this?

like image 220
Andrew Y Avatar asked Nov 08 '22 16:11

Andrew Y


1 Answers

I personally wouldn't modify anything in the standard library for something I could get other ways. Is there some advantage to parsing it out of every connection?

There is probably a simpler way, but I have the following.

func getMyInterfaceAddr() (net.IP, error) {


    ifaces, err := net.Interfaces()
    if err != nil {
        return nil, err
    }
    addresses := []net.IP{}
    for _, iface := range ifaces {

        if iface.Flags&net.FlagUp == 0 {
            continue // interface down
        }
        if iface.Flags&net.FlagLoopback != 0 {
            continue // loopback interface
        }
        addrs, err := iface.Addrs()
        if err != nil {
            continue
        }

        for _, addr := range addrs {
            var ip net.IP
            switch v := addr.(type) {
            case *net.IPNet:
                ip = v.IP
            case *net.IPAddr:
                ip = v.IP
            }
            if ip == nil || ip.IsLoopback() {
                continue
            }
            ip = ip.To4()
            if ip == nil {
                continue // not an ipv4 address
            }
            addresses = append(addresses, ip)
        }
    }
    if len(addresses) == 0 {
        return nil, fmt.Errorf("no address Found, net.InterfaceAddrs: %v", addresses)
    }
    //only need first
    return addresses[0], nil
}
like image 155
matt.s Avatar answered Nov 15 '22 06:11

matt.s