douyong1908 2016-04-13 15:40
浏览 332

如何使用golang net / http在服务器中获取服务器自身的地址?

I would like to write a HTTP server using the Golang'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.

Am I missing something, is there a cleaner way to do this ?

  • 写回答

1条回答 默认 最新

  • dongwu8050 2016-04-13 16:31
    关注

    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
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 基于卷积神经网络的声纹识别
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值
  • ¥15 我想咨询一下路面纹理三维点云数据处理的一些问题,上传的坐标文件里是怎么对无序点进行编号的,以及xy坐标在处理的时候是进行整体模型分片处理的吗
  • ¥15 CSAPPattacklab
  • ¥15 一直显示正在等待HID—ISP