package appview import ( "compress/gzip" "io" "net/http" "net/http/httptest" "strings" "testing" ) // handlerWriting returns a handler that writes n bytes of compressible body // under the given content type. func handlerWriting(contentType string, n int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", contentType) _, _ = w.Write([]byte(strings.Repeat("a", n))) }) } func TestCompressUIResponses(t *testing.T) { tests := []struct { name string path string contentType string size int wantGzip bool }{ {"html is compressed", "/", "text/html; charset=utf-8", 4096, true}, {"css is compressed", "/css/style.css", "text/css; charset=utf-8", 4096, true}, {"js is compressed", "/js/bundle.min.js", "application/javascript", 4096, true}, {"svg is compressed", "/icons.svg", "image/svg+xml", 4096, true}, {"ui json is compressed", "/api/thing", "application/json", 4096, true}, // The registry must stay out of the compression path entirely: OCI // layers are already gzipped tarballs. {"registry blob untouched", "/v2/u/i/blobs/sha256:abc", "application/octet-stream", 4096, false}, {"registry json untouched", "/v2/u/i/tags/list", "application/json", 4096, false}, {"registry root untouched", "/v2/", "application/json", 4096, false}, {"registry bare untouched", "/v2", "application/json", 4096, false}, {"binary not compressed", "/static/img.png", "image/png", 4096, false}, {"tiny body not compressed", "/", "text/html; charset=utf-8", 16, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { h := compressUIResponses(handlerWriting(tt.contentType, tt.size)) req := httptest.NewRequest(http.MethodGet, tt.path, nil) req.Header.Set("Accept-Encoding", "gzip") rec := httptest.NewRecorder() h.ServeHTTP(rec, req) gotGzip := rec.Header().Get("Content-Encoding") == "gzip" if gotGzip != tt.wantGzip { t.Fatalf("Content-Encoding gzip = %v, want %v (header %q)", gotGzip, tt.wantGzip, rec.Header().Get("Content-Encoding")) } body := rec.Body.Bytes() if gotGzip { zr, err := gzip.NewReader(rec.Body) if err != nil { t.Fatalf("response not valid gzip: %v", err) } defer func() { _ = zr.Close() }() body, err = io.ReadAll(zr) if err != nil { t.Fatalf("gzip decode: %v", err) } if len(rec.Body.Bytes()) >= tt.size { t.Errorf("compressed body %d bytes, not smaller than raw %d", len(rec.Body.Bytes()), tt.size) } } if len(body) != tt.size { t.Errorf("decoded body = %d bytes, want %d", len(body), tt.size) } }) } } // A client that does not advertise gzip must still get a usable response. func TestCompressUIResponsesWithoutAcceptEncoding(t *testing.T) { h := compressUIResponses(handlerWriting("text/html", 4096)) req := httptest.NewRequest(http.MethodGet, "/", nil) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if enc := rec.Header().Get("Content-Encoding"); enc != "" { t.Fatalf("Content-Encoding = %q, want empty for a client that did not ask", enc) } if rec.Body.Len() != 4096 { t.Errorf("body = %d bytes, want 4096", rec.Body.Len()) } }