This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / spindle / engines / microvm / read_cache_proxy_test.go
5.8 kB 171 lines
1package microvm 2 3import ( 4 "io" 5 "log/slog" 6 "net/http" 7 "net/http/httptest" 8 "strings" 9 "testing" 10 "time" 11) 12 13func TestCacheProxyFallsBackOnNotFound(t *testing.T) { 14 first := httptest.NewServer(http.NotFoundHandler()) 15 defer first.Close() 16 second := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 17 if req.URL.Path != "/abc.narinfo" { 18 t.Fatalf("path: got %q, want /abc.narinfo", req.URL.Path) 19 } 20 _, _ = io.WriteString(w, "ok") 21 })) 22 defer second.Close() 23 24 upstreams, err := parseCacheUpstreams([]string{first.URL, second.URL}) 25 if err != nil { 26 t.Fatal(err) 27 } 28 29 req := httptest.NewRequest(http.MethodGet, "http://guest/abc.narinfo", nil) 30 rec := httptest.NewRecorder() 31 cacheProxyHandler(mergeCacheUpstreams(upstreams, nil), slog.Default()).ServeHTTP(rec, req) 32 33 if rec.Code != http.StatusOK { 34 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String()) 35 } 36 if got := rec.Body.String(); got != "ok" { 37 t.Fatalf("body: got %q, want ok", got) 38 } 39} 40 41func TestCacheProxyServesNixCacheInfoItself(t *testing.T) { 42 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 43 t.Errorf("upstream should not be hit, got request for %q", req.URL.Path) 44 })) 45 defer upstream.Close() 46 47 upstreams, err := parseCacheUpstreams([]string{upstream.URL}) 48 if err != nil { 49 t.Fatal(err) 50 } 51 52 req := httptest.NewRequest(http.MethodGet, "http://guest/nix-cache-info", nil) 53 rec := httptest.NewRecorder() 54 cacheProxyHandler(mergeCacheUpstreams(upstreams, nil), slog.Default()).ServeHTTP(rec, req) 55 56 if rec.Code != http.StatusOK { 57 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String()) 58 } 59 if got := rec.Body.String(); got != nixCacheInfo { 60 t.Fatalf("body: got %q, want %q", got, nixCacheInfo) 61 } 62} 63 64func TestCacheProxyErrorStatusDoesNotWinRace(t *testing.T) { 65 erroring := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 66 http.Error(w, "misdirected", http.StatusMisdirectedRequest) 67 })) 68 defer erroring.Close() 69 healthy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 70 time.Sleep(50 * time.Millisecond) // lose the race to the erroring upstream 71 _, _ = io.WriteString(w, "ok") 72 })) 73 defer healthy.Close() 74 75 upstreams, err := parseCacheUpstreams([]string{erroring.URL, healthy.URL}) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 req := httptest.NewRequest(http.MethodGet, "http://guest/abc.narinfo", nil) 81 rec := httptest.NewRecorder() 82 cacheProxyHandler(mergeCacheUpstreams(upstreams, nil), slog.Default()).ServeHTTP(rec, req) 83 84 if rec.Code != http.StatusOK { 85 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String()) 86 } 87 if got := rec.Body.String(); got != "ok" { 88 t.Fatalf("body: got %q, want ok", got) 89 } 90} 91 92func TestCacheProxyJoinsSubpathQueryAndAuth(t *testing.T) { 93 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 94 if req.URL.Path != "/sub/cache/abc.narinfo" { 95 t.Errorf("path: got %q, want /sub/cache/abc.narinfo", req.URL.Path) 96 } 97 if got := req.URL.Query().Get("token"); got != "s3cret" { 98 t.Errorf("token: got %q, want s3cret", got) 99 } 100 if user, pass, ok := req.BasicAuth(); !ok || user != "dawn" || pass != "woof" { 101 t.Errorf("basic auth: got %q/%q/%v, want dawn/woof/true", user, pass, ok) 102 } 103 _, _ = io.WriteString(w, "ok") 104 })) 105 defer upstream.Close() 106 107 upstreamURL := "http://dawn:woof@" + strings.TrimPrefix(upstream.URL, "http://") + "/sub/cache/?token=s3cret" 108 upstreams, err := parseCacheUpstreams([]string{upstreamURL}) 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 req := httptest.NewRequest(http.MethodGet, "http://guest/abc.narinfo", nil) 114 rec := httptest.NewRecorder() 115 cacheProxyHandler(mergeCacheUpstreams(upstreams, nil), slog.Default()).ServeHTTP(rec, req) 116 117 if rec.Code != http.StatusOK { 118 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String()) 119 } 120 if got := rec.Body.String(); got != "ok" { 121 t.Fatalf("body: got %q, want ok", got) 122 } 123} 124 125func TestCacheProxyGuardedUpstreamCannotReachBlockedRanges(t *testing.T) { 126 // httptest listens on 127.0.0.1, which is in the blocked ranges; reaching 127 // it would mean a workflow-defined cache can hit the host's loopback 128 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 129 t.Errorf("guarded upstream on loopback should not be reachable, got request for %q", req.URL.Path) 130 })) 131 defer upstream.Close() 132 133 upstreams, err := parseCacheUpstreams([]string{upstream.URL}) 134 if err != nil { 135 t.Fatal(err) 136 } 137 138 req := httptest.NewRequest(http.MethodGet, "http://guest/abc.narinfo", nil) 139 rec := httptest.NewRecorder() 140 cacheProxyHandler(mergeCacheUpstreams(nil, upstreams), slog.Default()).ServeHTTP(rec, req) 141 142 if rec.Code != http.StatusBadGateway { 143 t.Fatalf("status: got %d, want 502; body=%q", rec.Code, rec.Body.String()) 144 } 145} 146 147func TestCacheProxyRewritesHostHeader(t *testing.T) { 148 var upstreamHost string 149 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 150 if req.Host != upstreamHost { 151 t.Errorf("host: got %q, want %q", req.Host, upstreamHost) 152 } 153 _, _ = io.WriteString(w, "ok") 154 })) 155 defer upstream.Close() 156 upstreamHost = strings.TrimPrefix(upstream.URL, "http://") 157 158 upstreams, err := parseCacheUpstreams([]string{upstream.URL}) 159 if err != nil { 160 t.Fatal(err) 161 } 162 163 req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:10500/abc.narinfo", nil) 164 req.Host = "127.0.0.1:10500" 165 rec := httptest.NewRecorder() 166 cacheProxyHandler(mergeCacheUpstreams(upstreams, nil), slog.Default()).ServeHTTP(rec, req) 167 168 if rec.Code != http.StatusOK { 169 t.Fatalf("status: got %d, want 200; body=%q", rec.Code, rec.Body.String()) 170 } 171}