| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "fmt" |
| 6 | "net/http" |
| 7 | "net/http/httptest" |
| 8 | "strings" |
| 9 | "sync/atomic" |
| 10 | "testing" |
| 11 | "time" |
| 12 | ) |
| 13 | |
| 14 | func TestServeClientRequiresLoopbackHTTP(t *testing.T) { |
| 15 | for _, base := range []string{ |
| 16 | "https://127.0.0.1:1234", |
| 17 | "http://example.com:1234", |
| 18 | "http://user@127.0.0.1:1234", |
| 19 | } { |
| 20 | if _, err := newServeHTTPClient(base); err == nil { |
| 21 | t.Fatalf("newServeHTTPClient(%q) succeeded", base) |
| 22 | } |
| 23 | } |
| 24 | if _, err := newServeHTTPClient("http://127.0.0.1:1234"); err != nil { |
| 25 | t.Fatalf("loopback client rejected: %v", err) |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | func TestServeHandshakeDoesNotFollowRedirectWithToken(t *testing.T) { |
| 30 | var leaked atomic.Bool |
| 31 | sink := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { |
| 32 | leaked.Store(true) |
| 33 | })) |
| 34 | defer sink.Close() |
| 35 | redirector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 36 | http.Redirect(w, r, sink.URL, http.StatusTemporaryRedirect) |
| 37 | })) |
| 38 | defer redirector.Close() |
| 39 | |
| 40 | client, err := newServeHTTPClient(redirector.URL) |
| 41 | if err != nil { |
| 42 | t.Fatal(err) |
| 43 | } |
| 44 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) |
| 45 | defer cancel() |
| 46 | if err := serveHandshake(ctx, client, redirector.URL, "secret-token"); err == nil { |
| 47 | t.Fatal("redirecting handshake succeeded") |
| 48 | } |
| 49 | if leaked.Load() { |
| 50 | t.Fatal("handshake followed the redirect and exposed its token body") |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | func TestServeGetAcceptsHistoryLargerThanLegacyOneMiBLimit(t *testing.T) { |
| 55 | payload := fmt.Sprintf(`{"history":"%s"}`, strings.Repeat("x", (2<<20))) |
| 56 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { |
| 57 | w.Header().Set("Content-Type", "application/json") |
| 58 | _, _ = w.Write([]byte(payload)) |
| 59 | })) |
| 60 | defer server.Close() |
| 61 | client, err := newServeHTTPClient(server.URL) |
| 62 | if err != nil { |
| 63 | t.Fatal(err) |
| 64 | } |
| 65 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
| 66 | defer cancel() |
| 67 | got, err := serveGet(ctx, client, server.URL) |
| 68 | if err != nil { |
| 69 | t.Fatal(err) |
| 70 | } |
| 71 | if len(got) != len(payload) { |
| 72 | t.Fatalf("history length = %d, want %d", len(got), len(payload)) |
| 73 | } |
| 74 | } |
| 75 |