The Go documentation states the following on http.Request.RemoteAddr:
The HTTP server in this package sets RemoteAddr to an "IP:port" address before invoking a handler.
Therefore, you could expect that a code like this is correct:
package main
import (
"fmt"
"net"
"net/http"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
)
func main() {
r := chi.NewMux()
r.Use(middleware.RealIP)
r.Get("/test", func(rw http.ResponseWriter, r *http.Request) {
host, port, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
fmt.Fprintf(rw, "Error: %v\n", err)
} else {
fmt.Fprintf(rw, "Host: %s\nPort: %s\n", host, port)
}
})
http.ListenAndServe(":8000", r)
}
However, the RealIP middleware is just copying the X-Forwarded-For value into r.RemoteAddr, which usually does not contain a port, making the code fail:
[dirbaio@mars]$ curl localhost:8000/test
Host: ::1
Port: 40188
[dirbaio@mars]$ curl localhost:8000/test -H 'X-Forwarded-For: 1.2.3.4'
Error: address 1.2.3.4: missing port in address
[dirbaio@mars]$ curl localhost:8000/test -H 'X-Forwarded-For: ::1'
Error: address ::1: too many colons in address
Perhaps RealIP should try to parse X-Forwarded-For for a host:port, and if it isn't, add a port? Maybe 0 to denote the port is unknown, like 1.2.3.4 -> 1.2.3.4:0?
This is particularly frustrating with IPv6 addresses because they have colons but they're not the host:port colon, making it hard to parse the RemoteAddr in user code.
The Go documentation states the following on
http.Request.RemoteAddr:Therefore, you could expect that a code like this is correct:
However, the RealIP middleware is just copying the
X-Forwarded-Forvalue intor.RemoteAddr, which usually does not contain a port, making the code fail:Perhaps RealIP should try to parse
X-Forwarded-Forfor ahost:port, and if it isn't, add a port? Maybe0to denote the port is unknown, like1.2.3.4->1.2.3.4:0?This is particularly frustrating with IPv6 addresses because they have colons but they're not the
host:portcolon, making it hard to parse the RemoteAddr in user code.