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

huangapple go评论70阅读模式
英文:

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

问题

我想使用Go的net/http包编写一个HTTP服务器,其反应取决于HTTP连接的服务器端IP地址。

换句话说,我要找的是CGI的"SERVER_ADDR"变量的等效物。

在http.Request中最接近的字段是"Host" - 但由于只有在请求使用字面地址时它才会等于地址,所以我不能使用它(服务器可能通过名称使用)。

查看https://golang.org/src/net/http/server.go中的源代码,似乎唯一获取服务器地址的方法是在处理程序中劫持(Hijack())连接,并为同一连接上的后续请求实现后续的HTTP解析,但这看起来非常不优雅...

似乎理想的解决方案是修改golang标准库中的http/request和http/server,如下所示:

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

然后以一种漂亮而干净的方式在代码中使用新的LocalAddr。

有没有更简洁的方法来做到这一点?

英文:

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 &quot;IP:port&quot; 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&#39;s (*Request).RemoteAddr.
 	remoteAddr string
 
+	// serverAddr is rwc.LocalAddr().String(). It is not populated synchronously
+	// inside the Listener&#39;s Accept goroutine, as some implementations block.
+	// It is populated immediately inside the (*conn).serve goroutine.
+	// This is the value of a Handler&#39;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, &quot;Host&quot;)
 
 	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 &lt;&lt; 10

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

Is there a cleaner way to do this?

答案1

得分: 1

我个人不会修改标准库中的任何内容,如果我可以通过其他方式获取它。从每个连接中解析它是否有优势呢?

可能有更简单的方法,但我有以下代码。

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
}
英文:

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&amp;net.FlagUp == 0 {
	    	continue // interface down
	    }
		if iface.Flags&amp;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(&quot;no address Found, net.InterfaceAddrs: %v&quot;, addresses)
	}
	//only need first
	return addresses[0], nil
}

huangapple
  • 本文由 发表于 2016年4月13日 23:40:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/36603347.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定