Skip to content

Commit

Permalink
cmd/xurls: retry with GET if HEAD results in a 405
Browse files Browse the repository at this point in the history
When using `xurls -fix` in another project,
HEAD on https://pkgs.alpinelinux.org/packages?name=shfmt resulted in a
405 "Method Not Allowed", but GET still succeeds.
  • Loading branch information
mvdan committed Nov 22, 2022
1 parent f6175bc commit e87b85f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 35 deletions.
9 changes: 8 additions & 1 deletion cmd/xurls/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ func scanPath(re *regexp.Regexp, path string) error {
return nil
},
}
req, err := http.NewRequest(http.MethodHead, fixed, nil)
method := http.MethodHead
retry:
req, err := http.NewRequest(method, fixed, nil)
if err != nil {
r.appendBroken(match, err.Error())
continue
Expand All @@ -139,6 +141,11 @@ func scanPath(re *regexp.Regexp, path string) error {
continue
}
if code := resp.StatusCode; code >= 400 {
if code == http.StatusMethodNotAllowed {
method = http.MethodGet
resp.Body.Close()
goto retry
}
r.appendBroken(match, fmt.Sprintf("%d %s", code, http.StatusText(code)))
}
resp.Body.Close()
Expand Down
57 changes: 34 additions & 23 deletions cmd/xurls/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,54 +29,65 @@ func TestScript(t *testing.T) {
RequireExplicitExec: true,
Setup: func(env *testscript.Env) error {
mux := http.NewServeMux()
handle := func(pattern string, handler func(http.ResponseWriter, *http.Request)) {
handle := func(method, pattern string, handler func(http.ResponseWriter, *http.Request)) {
mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodHead {
t.Errorf("expected all requests to be %q, got %q", http.MethodHead, r.Method)
if r.Method != method {
t.Errorf("expected all requests to be %q, got %q", method, r.Method)
}
handler(w, r)
})
}
handle("/plain", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "plaintext")
handle("HEAD", "/plain-head", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
handle("/redir-1", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain", http.StatusMovedPermanently)
handle("HEAD", "/redir-1", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain-head", http.StatusMovedPermanently)
})
handle("/redir-2", func(w http.ResponseWriter, r *http.Request) {
handle("HEAD", "/redir-2", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/redir-1", http.StatusMovedPermanently)
})

handle("/redir-longer", func(w http.ResponseWriter, r *http.Request) {
handle("HEAD", "/redir-longer", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/redir-longtarget", http.StatusMovedPermanently)
})
handle("/redir-longtarget", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "long target")
handle("HEAD", "/redir-longtarget", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
handle("/redir-fragment", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain#bar", http.StatusMovedPermanently)
handle("HEAD", "/redir-fragment", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain-head#bar", http.StatusMovedPermanently)
})

handle("/redir-301", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain", 301)
handle("HEAD", "/redir-301", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain-head", 301)
})
handle("/redir-302", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain", 302)
handle("HEAD", "/redir-302", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain-head", 302)
})
handle("/redir-307", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain", 307)
handle("HEAD", "/redir-307", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain-head", 307)
})
handle("/redir-308", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain", 308)
handle("HEAD", "/redir-308", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/plain-head", 308)
})

handle("/404", func(w http.ResponseWriter, r *http.Request) {
handle("HEAD", "/404", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "", 404)
})
handle("/500", func(w http.ResponseWriter, r *http.Request) {
handle("HEAD", "/500", func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "", 500)
})

handle("GET", "/plain-get", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "plaintext")
})
mux.HandleFunc("/get-only", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
http.Redirect(w, r, "/plain-get", 301)
} else {
http.Error(w, "", 405)
}
})

ln, err := net.Listen("tcp", ":0")
if err != nil {
return err
Expand Down
26 changes: 15 additions & 11 deletions cmd/xurls/testdata/script/fix.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ stderr -count=1 'totallydoesnotexist.localhost/ - Head .* dial tcp'
cmp broken broken.golden

-- nothing --
No redirect: ${SERVER}/plain
No redirect: ${SERVER}/plain-head
-- redirects --
No redirect: ${SERVER}/plain
No redirect: ${SERVER}/plain-head
One redirect: ${SERVER}/redir-1
Two redirects: ${SERVER}/redir-2
Redirect inherits fragment: ${SERVER}/redir-1#foo
Expand All @@ -53,31 +53,35 @@ Permanent redirect codes:
Temporary redirect codes:
* ${SERVER}/redir-302
* ${SERVER}/redir-307

Only GET allowed, HEAD fails: ${SERVER}/get-only
-- redirects.golden --
No redirect: ${SERVER}/plain
One redirect: ${SERVER}/plain
Two redirects: ${SERVER}/plain
Redirect inherits fragment: ${SERVER}/plain#foo
Redirect replaces fragment: ${SERVER}/plain#bar
No redirect: ${SERVER}/plain-head
One redirect: ${SERVER}/plain-head
Two redirects: ${SERVER}/plain-head
Redirect inherits fragment: ${SERVER}/plain-head#foo
Redirect replaces fragment: ${SERVER}/plain-head#bar

Three links in one line: ${SERVER}/plain + ${SERVER}/plain + ${SERVER}/plain
Three links in one line: ${SERVER}/plain-head + ${SERVER}/plain-head + ${SERVER}/plain-head

Redirect to a longer path ${SERVER}/redir-longtarget with trailing text

Permanent redirect codes:
* ${SERVER}/plain
* ${SERVER}/plain
* ${SERVER}/plain-head
* ${SERVER}/plain-head

Temporary redirect codes:
* ${SERVER}/redir-302
* ${SERVER}/redir-307

Only GET allowed, HEAD fails: ${SERVER}/plain-get
-- broken --
One redirect: ${SERVER}/redir-1
404 errors: ${SERVER}/404 ${SERVER}/404
500 errors: ${SERVER}/500 ${SERVER}/500
Dial error: http://totallydoesnotexist.localhost/
-- broken.golden --
One redirect: ${SERVER}/plain
One redirect: ${SERVER}/plain-head
404 errors: ${SERVER}/404 ${SERVER}/404
500 errors: ${SERVER}/500 ${SERVER}/500
Dial error: http://totallydoesnotexist.localhost/

0 comments on commit e87b85f

Please sign in to comment.