diff --git a/httpbin/handlers.go b/httpbin/handlers.go index ae9c585..df67256 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -166,6 +166,13 @@ func (h *HTTPBin) Headers(w http.ResponseWriter, r *http.Request) { }) } +// ServerIP echoes the IP address of the server handling the request +func (h *HTTPBin) ServerIP(w http.ResponseWriter, _ *http.Request) { + writeJSON(http.StatusOK, w, &serverIPResponse{ + ServerIP: getServerIP(), + }) +} + type statusCase struct { headers map[string]string body []byte diff --git a/httpbin/helpers.go b/httpbin/helpers.go index 0c09e6e..c79ffdc 100644 --- a/httpbin/helpers.go +++ b/httpbin/helpers.go @@ -106,6 +106,38 @@ func getURL(r *http.Request) *url.URL { } } +// getServerIP attempts to determine the current server's primary IP address +// (e.g., Pod IP in Kubernetes). It prefers non-loopback IPv4 addresses, then +// falls back to non-loopback IPv6 if necessary. +func getServerIP() string { + // Prefer non-loopback IPv4 from interface addresses + if addrs, err := net.InterfaceAddrs(); err == nil { + var v6Fallback string + for _, a := range addrs { + if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + ip := ipnet.IP + if v4 := ip.To4(); v4 != nil { + return v4.String() + } + if v6Fallback == "" { + v6Fallback = ip.String() + } + } + } + if v6Fallback != "" { + return v6Fallback + } + } + // As a last resort, infer via a UDP dial (no packets actually sent) + if conn, err := net.Dial("udp", "8.8.8.8:80"); err == nil { + defer conn.Close() + if udp, ok := conn.LocalAddr().(*net.UDPAddr); ok && udp.IP != nil { + return udp.IP.String() + } + } + return "" +} + func writeResponse(w http.ResponseWriter, status int, contentType string, body []byte) { w.Header().Set("Content-Type", contentType) w.WriteHeader(status) diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index b1da615..6132b43 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -181,6 +181,7 @@ func (h *HTTPBin) Handler() http.Handler { mux.HandleFunc("/image", h.ImageAccept) mux.HandleFunc("/image/{kind}", h.Image) mux.HandleFunc("/ip", h.IP) + mux.HandleFunc("/server-ip", h.ServerIP) mux.HandleFunc("/json", h.JSON) mux.HandleFunc("/links/{numLinks}", h.Links) mux.HandleFunc("/links/{numLinks}/{offset}", h.Links) diff --git a/httpbin/responses.go b/httpbin/responses.go index e280a1d..d229923 100644 --- a/httpbin/responses.go +++ b/httpbin/responses.go @@ -87,6 +87,10 @@ type hostnameResponse struct { Hostname string `json:"hostname"` } +type serverIPResponse struct { + ServerIP string `json:"server_ip"` +} + type errorRespnose struct { StatusCode int `json:"status_code"` Error string `json:"error"`