فهرست منبع

refactor(response): use http.Header for header map

http.Header is built for headers, so use it for its intended purpose
gudvinr 1 روز پیش
والد
کامیت
9a774083ab
1فایلهای تغییر یافته به همراه17 افزوده شده و 18 حذف شده
  1. 17 18
      internal/http/response/builder.go

+ 17 - 18
internal/http/response/builder.go

@@ -8,6 +8,7 @@ import (
 	"compress/gzip"
 	"io"
 	"log/slog"
+	"maps"
 	"mime"
 	"net/http"
 	"strings"
@@ -23,14 +24,14 @@ type Builder struct {
 	w                 http.ResponseWriter
 	r                 *http.Request
 	statusCode        int
-	headers           map[string]string
+	headers           http.Header
 	enableCompression bool
 	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}
+	return &Builder{w: w, r: r, statusCode: http.StatusOK, headers: make(http.Header), enableCompression: true}
 }
 
 // WithStatus uses the given status code to build the response.
@@ -41,7 +42,7 @@ func (b *Builder) WithStatus(statusCode int) *Builder {
 
 // WithHeader adds the given HTTP header to the response.
 func (b *Builder) WithHeader(key, value string) *Builder {
-	b.headers[key] = value
+	b.headers.Set(key, value)
 	return b
 }
 
@@ -65,13 +66,13 @@ func (b *Builder) WithBodyAsReader(body io.Reader) *Builder {
 
 // WithAttachment forces the document to be downloaded by the web browser.
 func (b *Builder) WithAttachment(filename string) *Builder {
-	b.headers["Content-Disposition"] = formatContentDisposition("attachment", filename)
+	b.headers.Set("Content-Disposition", formatContentDisposition("attachment", filename))
 	return b
 }
 
 // WithInline suggests an inline filename for the current response.
 func (b *Builder) WithInline(filename string) *Builder {
-	b.headers["Content-Disposition"] = formatContentDisposition("inline", filename)
+	b.headers.Set("Content-Disposition", formatContentDisposition("inline", filename))
 	return b
 }
 
@@ -84,9 +85,9 @@ func (b *Builder) WithoutCompression() *Builder {
 // WithCaching adds caching headers to the response.
 func (b *Builder) WithCaching(etag string, duration time.Duration, callback func(*Builder)) {
 	etag = normalizeETag(etag)
-	b.headers["ETag"] = etag
-	b.headers["Cache-Control"] = "public, immutable"
-	b.headers["Expires"] = time.Now().Add(duration).UTC().Format(http.TimeFormat)
+	b.headers.Set("ETag", etag)
+	b.headers.Set("Cache-Control", "public, immutable")
+	b.headers.Set("Expires", time.Now().Add(duration).UTC().Format(http.TimeFormat))
 
 	if ifNoneMatch(b.r.Header.Get("If-None-Match"), etag) {
 		b.statusCode = http.StatusNotModified
@@ -120,24 +121,22 @@ func (b *Builder) Write() {
 }
 
 func (b *Builder) writeHeaders() {
-	b.headers["X-Content-Type-Options"] = "nosniff"
-	b.headers["X-Frame-Options"] = "DENY"
-	b.headers["Referrer-Policy"] = "no-referrer"
+	b.headers.Set("X-Content-Type-Options", "nosniff")
+	b.headers.Set("X-Frame-Options", "DENY")
+	b.headers.Set("Referrer-Policy", "no-referrer")
 
-	for key, value := range b.headers {
-		b.w.Header().Set(key, value)
-	}
+	maps.Copy(b.w.Header(), b.headers)
 
 	b.w.WriteHeader(b.statusCode)
 }
 
 func (b *Builder) compress(data []byte) {
 	if b.enableCompression && len(data) > compressionThreshold {
-		b.headers["Vary"] = "Accept-Encoding"
+		b.headers.Set("Vary", "Accept-Encoding")
 		acceptEncoding := b.r.Header.Get("Accept-Encoding")
 		switch {
 		case strings.Contains(acceptEncoding, "br"):
-			b.headers["Content-Encoding"] = "br"
+			b.headers.Set("Content-Encoding", "br")
 			b.writeHeaders()
 
 			brotliWriter := brotli.NewWriterV2(b.w, brotli.DefaultCompression)
@@ -145,7 +144,7 @@ func (b *Builder) compress(data []byte) {
 			brotliWriter.Close()
 			return
 		case strings.Contains(acceptEncoding, "gzip"):
-			b.headers["Content-Encoding"] = "gzip"
+			b.headers.Set("Content-Encoding", "gzip")
 			b.writeHeaders()
 
 			gzipWriter := gzip.NewWriter(b.w)
@@ -153,7 +152,7 @@ func (b *Builder) compress(data []byte) {
 			gzipWriter.Close()
 			return
 		case strings.Contains(acceptEncoding, "deflate"):
-			b.headers["Content-Encoding"] = "deflate"
+			b.headers.Set("Content-Encoding", "deflate")
 			b.writeHeaders()
 
 			flateWriter, _ := flate.NewWriter(b.w, -1)