diff --git a/caddyhttp/httpserver/context.go b/caddyhttp/httpserver/context.go index 8a8cec93b..196337d90 100644 --- a/caddyhttp/httpserver/context.go +++ b/caddyhttp/httpserver/context.go @@ -84,6 +84,29 @@ func (c Context) IP() string { return ip } +// To mock the net.InterfaceAddrs from the test. +var networkInterfacesFn = net.InterfaceAddrs + +// ServerIP gets the (local) IP address of the server. +// TODO: The bind directive should be honored in this method (see PR #1474). +func (c Context) ServerIP() string { + addrs, err := networkInterfacesFn() + if err != nil { + return "" + } + + for _, address := range addrs { + // Validate the address and check if it's not a loopback + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil { + return ipnet.IP.String() + } + } + } + + return "" +} + // URI returns the raw, unprocessed request URI (including query // string and hash) obtained directly from the Request-Line of // the HTTP request. diff --git a/caddyhttp/httpserver/context_test.go b/caddyhttp/httpserver/context_test.go index fdf263684..b965e214b 100644 --- a/caddyhttp/httpserver/context_test.go +++ b/caddyhttp/httpserver/context_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io/ioutil" + "net" "net/http" "net/url" "os" @@ -302,6 +303,44 @@ func TestIP(t *testing.T) { } } +type myIP string + +func (ip myIP) mockInterfaces() ([]net.Addr, error) { + a := net.ParseIP(string(ip)) + + return []net.Addr{ + &net.IPNet{IP: a, Mask: nil}, + }, nil +} + +func TestServerIP(t *testing.T) { + context := getContextOrFail(t) + + tests := []string{ + // Test 0 - ipv4 + "1.1.1.1", + // Test 1 - ipv6 + "2001:db8:a0b:12f0::1", + } + + for i, expectedIP := range tests { + testPrefix := getTestPrefix(i) + + // Mock the network interface + ip := myIP(expectedIP) + networkInterfacesFn = ip.mockInterfaces + defer func() { + networkInterfacesFn = net.InterfaceAddrs + }() + + actualIP := context.ServerIP() + + if actualIP != expectedIP { + t.Errorf("%sExpected IP \"%s\", found \"%s\".", testPrefix, expectedIP, actualIP) + } + } +} + func TestURL(t *testing.T) { context := getContextOrFail(t)