Răsfoiți Sursa

refactor(response): add Text response helper

Frédéric Guillot 2 săptămâni în urmă
părinte
comite
e9edda8ef6

+ 7 - 12
internal/googlereader/handler.go

@@ -204,10 +204,7 @@ func (h *handler) clientLoginHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	builder := response.New(w, r)
-	builder.WithHeader("Content-Type", "text/plain; charset=UTF-8")
-	builder.WithBodyAsString(result.String())
-	builder.Write()
+	response.Text(w, r, result.String())
 }
 
 func (h *handler) tokenHandler(w http.ResponseWriter, r *http.Request) {
@@ -245,9 +242,7 @@ func (h *handler) tokenHandler(w http.ResponseWriter, r *http.Request) {
 		slog.Int64("user_id", request.UserID(r)),
 	)
 
-	w.Header().Add("Content-Type", "text/plain; charset=UTF-8")
-	w.WriteHeader(http.StatusOK)
-	w.Write([]byte(token))
+	response.Text(w, r, token)
 }
 
 func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
@@ -384,7 +379,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	sendOkayResponse(w)
+	response.Text(w, r, "OK")
 }
 
 func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
@@ -665,7 +660,7 @@ func (h *handler) editSubscriptionHandler(w http.ResponseWriter, r *http.Request
 		return
 	}
 
-	sendOkayResponse(w)
+	response.Text(w, r, "OK")
 }
 
 func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Request) {
@@ -840,7 +835,7 @@ func (h *handler) disableTagHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	sendOkayResponse(w)
+	response.Text(w, r, "OK")
 }
 
 func (h *handler) renameTagHandler(w http.ResponseWriter, r *http.Request) {
@@ -907,7 +902,7 @@ func (h *handler) renameTagHandler(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	sendOkayResponse(w)
+	response.Text(w, r, "OK")
 }
 
 func (h *handler) tagListHandler(w http.ResponseWriter, r *http.Request) {
@@ -1292,5 +1287,5 @@ func (h *handler) markAllAsReadHandler(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	sendOkayResponse(w)
+	response.Text(w, r, "OK")
 }

+ 12 - 12
internal/googlereader/middleware.go

@@ -38,7 +38,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("user_agent", r.UserAgent()),
 					slog.Any("error", err),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 
@@ -49,7 +49,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("client_ip", clientIP),
 					slog.String("user_agent", r.UserAgent()),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 		} else {
@@ -61,7 +61,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("client_ip", clientIP),
 					slog.String("user_agent", r.UserAgent()),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 			fields := strings.Fields(authorization)
@@ -71,7 +71,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("client_ip", clientIP),
 					slog.String("user_agent", r.UserAgent()),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 			if fields[0] != "GoogleLogin" {
@@ -80,7 +80,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("client_ip", clientIP),
 					slog.String("user_agent", r.UserAgent()),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 			auths := strings.Split(fields[1], "=")
@@ -90,7 +90,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("client_ip", clientIP),
 					slog.String("user_agent", r.UserAgent()),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 			if auths[0] != "auth" {
@@ -99,7 +99,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 					slog.String("client_ip", clientIP),
 					slog.String("user_agent", r.UserAgent()),
 				)
-				sendUnauthorizedResponse(w)
+				sendUnauthorizedResponse(w, r)
 				return
 			}
 			token = auths[1]
@@ -113,7 +113,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.String("token", token),
 			)
-			sendUnauthorizedResponse(w)
+			sendUnauthorizedResponse(w, r)
 			return
 		}
 		var integration *model.Integration
@@ -126,7 +126,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.Any("error", err),
 			)
-			sendUnauthorizedResponse(w)
+			sendUnauthorizedResponse(w, r)
 			return
 		}
 		expectedToken := getAuthToken(integration.GoogleReaderUsername, integration.GoogleReaderPassword)
@@ -136,7 +136,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 				slog.String("client_ip", clientIP),
 				slog.String("user_agent", r.UserAgent()),
 			)
-			sendUnauthorizedResponse(w)
+			sendUnauthorizedResponse(w, r)
 			return
 		}
 		if user, err = m.store.UserByID(integration.UserID); err != nil {
@@ -146,7 +146,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.Any("error", err),
 			)
-			sendUnauthorizedResponse(w)
+			sendUnauthorizedResponse(w, r)
 			return
 		}
 
@@ -156,7 +156,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 				slog.String("client_ip", clientIP),
 				slog.String("user_agent", r.UserAgent()),
 			)
-			sendUnauthorizedResponse(w)
+			sendUnauthorizedResponse(w, r)
 			return
 		}
 

+ 9 - 11
internal/googlereader/response.go

@@ -6,6 +6,8 @@ package googlereader // import "miniflux.app/v2/internal/googlereader"
 import (
 	"fmt"
 	"net/http"
+
+	"miniflux.app/v2/internal/http/response"
 )
 
 type loginResponse struct {
@@ -117,15 +119,11 @@ type contentItemOrigin struct {
 	HTMLUrl  string `json:"htmlUrl"`
 }
 
-func sendUnauthorizedResponse(w http.ResponseWriter) {
-	w.Header().Set("Content-Type", "text/plain")
-	w.Header().Set("X-Reader-Google-Bad-Token", "true")
-	w.WriteHeader(http.StatusUnauthorized)
-	w.Write([]byte("Unauthorized"))
-}
-
-func sendOkayResponse(w http.ResponseWriter) {
-	w.Header().Set("Content-Type", "text/plain")
-	w.WriteHeader(http.StatusOK)
-	w.Write([]byte("OK"))
+func sendUnauthorizedResponse(w http.ResponseWriter, r *http.Request) {
+	builder := response.NewBuilder(w, r)
+	builder.WithStatus(http.StatusUnauthorized)
+	builder.WithHeader("X-Reader-Google-Bad-Token", "true")
+	builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
+	builder.WithBodyAsString("Unauthorized")
+	builder.Write()
 }

+ 5 - 5
internal/http/response/builder.go

@@ -27,6 +27,11 @@ type Builder struct {
 	body              any
 }
 
+// NewBuilder creates a new response builder.
+func NewBuilder(w http.ResponseWriter, r *http.Request) *Builder {
+	return &Builder{w: w, r: r, statusCode: http.StatusOK, headers: make(map[string]string), enableCompression: true}
+}
+
 // WithStatus uses the given status code to build the response.
 func (b *Builder) WithStatus(statusCode int) *Builder {
 	b.statusCode = statusCode
@@ -176,8 +181,3 @@ func ifNoneMatch(headerValue, etag string) bool {
 	// Weak ETag comparison: the opaque-tag (quoted string without W/ prefix) must match.
 	return strings.Contains(headerValue, strings.TrimPrefix(etag, `W/`))
 }
-
-// New creates a new response builder.
-func New(w http.ResponseWriter, r *http.Request) *Builder {
-	return &Builder{w: w, r: r, statusCode: http.StatusOK, headers: make(map[string]string), enableCompression: true}
-}

+ 14 - 14
internal/http/response/builder_test.go

@@ -21,7 +21,7 @@ func TestResponseHasCommonHeaders(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).Write()
+		NewBuilder(w, r).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -49,7 +49,7 @@ func TestBuildResponseWithCustomStatusCode(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithStatus(http.StatusNotAcceptable).Write()
+		NewBuilder(w, r).WithStatus(http.StatusNotAcceptable).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -70,7 +70,7 @@ func TestBuildResponseWithCustomHeader(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithHeader("X-My-Header", "Value").Write()
+		NewBuilder(w, r).WithHeader("X-My-Header", "Value").Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -92,7 +92,7 @@ func TestBuildResponseWithAttachment(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithAttachment("my_file.pdf").Write()
+		NewBuilder(w, r).WithAttachment("my_file.pdf").Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -114,7 +114,7 @@ func TestBuildResponseWithByteBody(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsBytes([]byte("body")).Write()
+		NewBuilder(w, r).WithBodyAsBytes([]byte("body")).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -135,7 +135,7 @@ func TestBuildResponseWithCachingEnabled(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
+		NewBuilder(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
 			b.WithBodyAsString("cached body")
 			b.Write()
 		})
@@ -195,7 +195,7 @@ func TestBuildResponseWithCachingAndIfNoneMatch(t *testing.T) {
 			w := httptest.NewRecorder()
 
 			handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-				New(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
+				NewBuilder(w, r).WithCaching("etag", 1*time.Minute, func(b *Builder) {
 					b.WithBodyAsString("cached body")
 					b.Write()
 				})
@@ -281,7 +281,7 @@ func TestBuildResponseWithBrotliCompression(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsString(body).Write()
+		NewBuilder(w, r).WithBodyAsString(body).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -305,7 +305,7 @@ func TestBuildResponseWithGzipCompression(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsString(body).Write()
+		NewBuilder(w, r).WithBodyAsString(body).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -329,7 +329,7 @@ func TestBuildResponseWithDeflateCompression(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsString(body).Write()
+		NewBuilder(w, r).WithBodyAsString(body).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -359,7 +359,7 @@ func TestBuildResponseWithCompressionDisabled(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsString(body).WithoutCompression().Write()
+		NewBuilder(w, r).WithBodyAsString(body).WithoutCompression().Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -389,7 +389,7 @@ func TestBuildResponseWithDeflateCompressionAndSmallPayload(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsString(body).Write()
+		NewBuilder(w, r).WithBodyAsString(body).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -418,7 +418,7 @@ func TestBuildResponseWithoutCompressionHeader(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsString(body).Write()
+		NewBuilder(w, r).WithBodyAsString(body).Write()
 	})
 
 	handler.ServeHTTP(w, r)
@@ -446,7 +446,7 @@ func TestBuildResponseWithReaderBody(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		New(w, r).WithBodyAsReader(bytes.NewBufferString("body")).Write()
+		NewBuilder(w, r).WithBodyAsReader(bytes.NewBufferString("body")).Write()
 	})
 
 	handler.ServeHTTP(w, r)

+ 6 - 6
internal/http/response/html.go

@@ -13,7 +13,7 @@ import (
 
 // HTML creates a new HTML response with a 200 status code.
 func HTML[T []byte | string](w http.ResponseWriter, r *http.Request, body T) {
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
 	switch v := any(body).(type) {
@@ -40,7 +40,7 @@ func HTMLServerError(w http.ResponseWriter, r *http.Request, err error) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusInternalServerError)
 	builder.WithHeader("Content-Security-Policy", ContentSecurityPolicyForUntrustedContent)
 	builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
@@ -64,7 +64,7 @@ func HTMLBadRequest(w http.ResponseWriter, r *http.Request, err error) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusBadRequest)
 	builder.WithHeader("Content-Security-Policy", ContentSecurityPolicyForUntrustedContent)
 	builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
@@ -87,7 +87,7 @@ func HTMLForbidden(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusForbidden)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
@@ -109,7 +109,7 @@ func HTMLNotFound(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusNotFound)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
@@ -136,7 +136,7 @@ func HTMLRequestedRangeNotSatisfiable(w http.ResponseWriter, r *http.Request, co
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusRequestedRangeNotSatisfiable)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")

+ 9 - 9
internal/http/response/json.go

@@ -22,7 +22,7 @@ func JSON(w http.ResponseWriter, r *http.Request, body any) {
 		return
 	}
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(responseBody)
 	builder.Write()
@@ -36,7 +36,7 @@ func JSONCreated(w http.ResponseWriter, r *http.Request, body any) {
 		return
 	}
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusCreated)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(responseBody)
@@ -45,7 +45,7 @@ func JSONCreated(w http.ResponseWriter, r *http.Request, body any) {
 
 // JSONNoContent sends a no content response to the client.
 func JSONNoContent(w http.ResponseWriter, r *http.Request) {
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusNoContent)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.Write()
@@ -53,7 +53,7 @@ func JSONNoContent(w http.ResponseWriter, r *http.Request) {
 
 // JSONAccepted sends an accepted response to the client.
 func JSONAccepted(w http.ResponseWriter, r *http.Request) {
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusAccepted)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.Write()
@@ -74,7 +74,7 @@ func JSONServerError(w http.ResponseWriter, r *http.Request, err error) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusInternalServerError)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(generateJSONError(err))
@@ -96,7 +96,7 @@ func JSONBadRequest(w http.ResponseWriter, r *http.Request, err error) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusBadRequest)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(generateJSONError(err))
@@ -117,7 +117,7 @@ func JSONUnauthorized(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusUnauthorized)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(generateJSONError(errors.New("access unauthorized")))
@@ -138,7 +138,7 @@ func JSONForbidden(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusForbidden)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(generateJSONError(errors.New("access forbidden")))
@@ -159,7 +159,7 @@ func JSONNotFound(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithStatus(http.StatusNotFound)
 	builder.WithHeader("Content-Type", jsonContentTypeHeader)
 	builder.WithBodyAsBytes(generateJSONError(errors.New("resource not found")))

+ 14 - 0
internal/http/response/text.go

@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+import "net/http"
+
+// Text writes a standard text response with a status 200 OK.
+func Text(w http.ResponseWriter, r *http.Request, body string) {
+	builder := NewBuilder(w, r)
+	builder.WithHeader("Content-Type", `text/plain; charset=utf-8`)
+	builder.WithBodyAsString(body)
+	builder.Write()
+}

+ 39 - 0
internal/http/response/text_test.go

@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestTextResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		Text(w, r, "Some plain text")
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusOK)
+	}
+
+	if actualBody := w.Body.String(); actualBody != "Some plain text" {
+		t.Fatalf(`Unexpected body, got %q instead of %q`, actualBody, "Some plain text")
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != "text/plain; charset=utf-8" {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, "text/plain; charset=utf-8")
+	}
+}

+ 2 - 2
internal/http/response/xml.go

@@ -7,7 +7,7 @@ import "net/http"
 
 // XML writes a standard XML response with a status 200 OK.
 func XML(w http.ResponseWriter, r *http.Request, body string) {
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithHeader("Content-Type", "text/xml; charset=utf-8")
 	builder.WithBodyAsString(body)
 	builder.Write()
@@ -15,7 +15,7 @@ func XML(w http.ResponseWriter, r *http.Request, body string) {
 
 // XMLAttachment forces the XML document to be downloaded by the web browser.
 func XMLAttachment(w http.ResponseWriter, r *http.Request, filename string, body string) {
-	builder := New(w, r)
+	builder := NewBuilder(w, r)
 	builder.WithHeader("Content-Type", "text/xml; charset=utf-8")
 	builder.WithAttachment(filename)
 	builder.WithBodyAsString(body)

+ 1 - 1
internal/ui/feed_icon.go

@@ -24,7 +24,7 @@ func (h *handler) showFeedIcon(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	response.New(w, r).WithCaching(icon.Hash, 72*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(icon.Hash, 72*time.Hour, func(b *response.Builder) {
 		b.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
 		b.WithHeader("Content-Type", icon.MimeType)
 		b.WithBodyAsBytes(icon.Content)

+ 1 - 1
internal/ui/proxy.go

@@ -144,7 +144,7 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
 
 	etag := crypto.HashFromBytes(decodedURL)
 
-	response.New(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
 		b.WithStatus(resp.StatusCode)
 		b.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
 		b.WithHeader("Content-Type", resp.Header.Get("Content-Type"))

+ 1 - 1
internal/ui/share.go

@@ -45,7 +45,7 @@ func (h *handler) sharedEntry(w http.ResponseWriter, r *http.Request) {
 	}
 
 	etag := shareCode
-	response.New(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
 		builder := storage.NewAnonymousQueryBuilder(h.store)
 		builder.WithShareCode(shareCode)
 

+ 1 - 1
internal/ui/static_app_icon.go

@@ -22,7 +22,7 @@ func (h *handler) showAppIcon(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	response.New(w, r).WithCaching(value.Checksum, 72*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(value.Checksum, 72*time.Hour, func(b *response.Builder) {
 		switch filepath.Ext(filename) {
 		case ".png":
 			b.WithoutCompression()

+ 1 - 1
internal/ui/static_favicon.go

@@ -19,7 +19,7 @@ func (h *handler) showFavicon(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	response.New(w, r).WithCaching(value.Checksum, 48*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(value.Checksum, 48*time.Hour, func(b *response.Builder) {
 		b.WithHeader("Content-Type", "image/x-icon")
 		b.WithoutCompression()
 		b.WithBodyAsBytes(value.Data)

+ 1 - 1
internal/ui/static_javascript.go

@@ -27,7 +27,7 @@ func (h *handler) showJavascript(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	response.New(w, r).WithCaching(js.Checksum, 48*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(js.Checksum, 48*time.Hour, func(b *response.Builder) {
 		contents := js.Data
 
 		if filename == "service-worker" {

+ 1 - 1
internal/ui/static_stylesheet.go

@@ -21,7 +21,7 @@ func (h *handler) showStylesheet(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	response.New(w, r).WithCaching(m.Checksum, 48*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(m.Checksum, 48*time.Hour, func(b *response.Builder) {
 		b.WithHeader("Content-Type", "text/css; charset=utf-8")
 		b.WithBodyAsBytes(m.Data)
 		b.Write()