diff --git a/cmd/xurls/main.go b/cmd/xurls/main.go index 46edbac..93cd1b1 100644 --- a/cmd/xurls/main.go +++ b/cmd/xurls/main.go @@ -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 @@ -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() diff --git a/cmd/xurls/main_test.go b/cmd/xurls/main_test.go index 52f9b3f..649937c 100644 --- a/cmd/xurls/main_test.go +++ b/cmd/xurls/main_test.go @@ -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 diff --git a/cmd/xurls/testdata/script/fix.txtar b/cmd/xurls/testdata/script/fix.txtar index 7b3874f..a46f6d4 100644 --- a/cmd/xurls/testdata/script/fix.txtar +++ b/cmd/xurls/testdata/script/fix.txtar @@ -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 @@ -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/