Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 613
The Coder Avatar asked Jan 29 '23 03:01

The Coder


2 Answers

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, &slashFix{httpMux})
like image 95
Bayta Darell Avatar answered Feb 02 '23 08:02

Bayta Darell


Accepeted answer solved the problem

One more way is to use Gorilla mux and setting SkipClean(true). But be sure to know about the side effects in its doc

SkipClean defines the path cleaning behaviour for new routes. The initial value is false. Users should be careful about which routes are not cleaned. When true, if the route path is "/path//to", it will remain with the double slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/

When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will become /fetch/http/xkcd.com/534

func (r *Router) SkipClean(value bool) *Router {
    r.skipClean = value
    return r
}
like image 30
The Coder Avatar answered Feb 02 '23 10:02

The Coder