internal/httpd/trailingslash_test.go
65 lines · 2289 bytes
1package httpd
2
3import (
4 "bufio"
5 "net/http"
6 "net/http/httptest"
7 "strings"
8 "testing"
9)
10
11// A path that only misses because of a trailing slash redirects to the
12// path without it, query intact; one that misses either way is 404, and
13// a POST is never redirected (#233).
14func TestTrailingSlashRedirects(t *testing.T) {
15 h := plainServer().Handler()
16 for from, to := range map[string]string{
17 "/cmc/": "/cmc",
18 "/cmc/ccleberg/": "/cmc/ccleberg",
19 "/cmc/-/snippets/": "/cmc/-/snippets",
20 "/krz/gitbay/mrs/12/": "/krz/gitbay/mrs/12",
21 "/krz/gitbay/issues/?q=x": "/krz/gitbay/issues?q=x",
22 } {
23 w := get(t, h, from, nil)
24 if w.Code != http.StatusMovedPermanently || w.Header().Get("Location") != to {
25 t.Errorf("%s: %d %q, want 301 %q", from, w.Code, w.Header().Get("Location"), to)
26 }
27 }
28 for _, p := range []string{"/", "/krz/gitbay/nothing/"} {
29 if w := get(t, h, p, nil); p != "/" && w.Code != 404 {
30 t.Errorf("%s: status %d, want 404", p, w.Code)
31 }
32 }
33 // A Location must not leave the site: the mux cleans a leading //
34 // before the fallback runs, and a backslash is escaped (#153).
35 for _, p := range []string{"//evil.example/", "/\\evil.example/"} {
36 w := get(t, h, p, nil)
37 if loc := w.Header().Get("Location"); strings.HasPrefix(loc, "//") || strings.Contains(loc, "\\") {
38 t.Errorf("%s: Location %q leaves the site", p, loc)
39 }
40 }
41 // An absolute-form request line, which RFC 7230 requires a server to
42 // accept, fills URL.Scheme and URL.Host. Neither may reach Location.
43 // ReadRequest also takes Host from the URL; it is pinned back to the
44 // site so this stays a test of the forge handler rather than of the
45 // pages router.
46 raw := "GET http://evil.example/cmc/ HTTP/1.1\r\nHost: forge.test\r\n\r\n"
47 abs, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw)))
48 if err != nil {
49 t.Fatal(err)
50 }
51 abs.Host = "forge.test"
52 aw := httptest.NewRecorder()
53 h.ServeHTTP(aw, abs)
54 if loc := aw.Header().Get("Location"); loc != "/cmc" {
55 t.Errorf("absolute-form request: Location %q, want %q", loc, "/cmc")
56 }
57
58 r := httptest.NewRequest("POST", "/cmc/", nil)
59 r.Host = "forge.test"
60 w := httptest.NewRecorder()
61 h.ServeHTTP(w, r)
62 if w.Code != 404 {
63 t.Errorf("POST /cmc/: status %d, want 404", w.Code)
64 }
65}