The Coder The Coder - 6 months ago 46
HTTP Question

Golang "301 Moved Permanently" if request path contains additional slash

I have been using golang's default

http.ServeMux
for http route handling.

wrap := func(h func(t *MyStruct, w http.ResponseWriter, r *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
h(t, w, r)
}
}

// Register handlers with default mux
httpMux := http.NewServeMux()
httpMux.HandleFunc("/", wrap(payloadHandler))


Assume this server is accessible via
http://example.com/


Very few of my client's requests were of path
http://example.com/api//module
(note the extra slash) which is redirected as
301 Moved Permanently
. Exploring inside golang's http
ServeMux.Handler(r *Request)
function, seems it's intended.

path := cleanPath(r.URL.Path)
// if there is any change between actual and cleaned path, the request is 301 ed
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}


I've looked into other similar issue.

go-web-server-is-automatically-redirecting-post-requests

Above qn has problem with redundant
/
in register pattern itself, but my use case is not with register pattern (in some nested path which is irrelevent to register pattern)

Problem is, since my client's requests are
POST
, browsers handle 301 with new
GET
request with exact query params and POST body. But change in the HTTP method causes the request to fail.

I have already instructed client to fix the redundant
/
in url, but the fix might take few (?) weeks to be deployed in all client locations.

Also these redundant
/
are handled fine in
Apache Tomcat
, but fails only in golang server. So is this the intended behaviour in my use case (redundant
/
in nested path) with golang or possible bug?

I am thinking of way to override the
Handler
func of
ServeMux
, but it won't be useful since
Handler
calls are made internally. Looking to disable this 301 behaviour, help would be appreciated.

Relevant links

http-post-method-is-actally-sending-a-get

Answer Source

The clean and redirect is intended behavior.

Wrap the mux with a handler that removes the double slashes:

type slashFix struct {
    mux http.Handler
}

func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1)
    h.mux.ServeHTTP(w, r)
}

Use it like this:

httpMux := http.NewServeMux()
httpMux.HandleFunc("/", wrap(payloadHandler))
http.ListenAndServe(addr, &fixSlash{httpMux})
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download