Commit 66ac6b0

Eric Bower  ·  2026-04-22 11:00:24 -0400 EDT
parent acf6290
fix(pgs): dupe headers
2 files changed,  +29, -11
+15, -3
......@@ -62,6 +62,14 @@ func newProxyServe(logger *slog.Logger) *proxyServe {
6262 return &proxyServe{Logger: logger, transport: transport}
6363 }
6464
65+// Headers that should be stripped from upstream responses because the CDN's
66+// cache layer will add its own versions.
67+var stripHeaders = map[string]bool{
68+ "age": true,
69+ "cache-status": true,
70+ "date": true,
71+}
72+
6573 func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) {
6674 // Dial ash.pgs.sh but keep the original Host header so ash.pgs.sh
6775 // can route the request to the correct subdomain (zmx.sh, etc.).
......@@ -90,10 +98,13 @@ func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) {
9098 _ = resp.Body.Close()
9199 }()
92100
101+ // Copy headers from upstream, but strip cache-related headers that the
102+ // CDN's cache layer will regenerate
93103 for k, vals := range resp.Header {
94- for _, v := range vals {
95- w.Header().Set(k, v)
104+ if stripHeaders[strings.ToLower(k)] {
105+ continue
96106 }
107+ w.Header()[k] = vals
97108 }
98109 w.WriteHeader(resp.StatusCode)
99110 _, _ = io.Copy(w, resp.Body)
......@@ -121,11 +132,12 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
121132 if err != nil {
122133 c.Logger.Error("check request", "err", err)
123134 }
124- writer.WriteHeader(resp.StatusCode)
125135 defer func() {
126136 _ = resp.Body.Close()
127137 }()
138+ writer.WriteHeader(resp.StatusCode)
128139 _, _ = io.Copy(writer, resp.Body)
140+ return
129141 }
130142
131143 c.Cache.ServeHTTP(writer, req)
+14, -8
......@@ -95,9 +95,6 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
9595 return
9696 }
9797
98- log.Info("cache miss, requesting upstream", "err", err)
99- c.AddCacheMiss()
100-
10198 // RFC 9111 4.2.4 + 4.3.1/4.3.2: stale must-revalidate entries must be
10299 // revalidated with conditional headers derived from the stored response.
103100 // Preserve original client conditional headers so we can evaluate them
......@@ -120,6 +117,8 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
120117 }
121118 }
122119
120+ log.Info("cache miss, requesting upstream", "err", err)
121+ c.AddCacheMiss()
123122 wrapped := &responseWriter{ResponseWriter: w}
124123 c.Upstream.ServeHTTP(wrapped, r)
125124 c.AddUpstreamRequest()
......@@ -128,16 +127,18 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
128127 // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4
129128 // A 304 response updates header metadata but preserves the cached body.
130129 if wrapped.StatusCode() == http.StatusNotModified {
131- log.Info("304 not modified, updating cached headers")
132130 existingData, exists := c.Cache.Get(cacheKey)
133131 if !exists {
134132 // Cache entry vanished; forward the 304 as-is.
133+ log.Info("no cache entry found, forwarding 304 as-is")
135134 wrapped.Send()
136135 return
137136 }
138137
139138 var cacheValue CacheValue
140- if json.Unmarshal(existingData, &cacheValue) != nil {
139+ err = json.Unmarshal(existingData, &cacheValue)
140+ if err != nil {
141+ log.Error("json unmarshal", "err", err)
141142 wrapped.Send()
142143 return
143144 }
......@@ -153,6 +154,7 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
153154 // Revalidation refreshes the entry -- reset CreatedAt so it's fresh again.
154155 cacheValue.CreatedAt = time.Now()
155156 enc, _ := json.Marshal(cacheValue)
157+ log.Info("updating cached headers from 304 response")
156158 c.Cache.Remove(cacheKey)
157159 c.Cache.Add(cacheKey, enc)
158160 c.AddCacheItem(float64(len(enc)))
......@@ -169,12 +171,14 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
169171 hdr.Set("age", strconv.Itoa(int(ageDur.Seconds())+1))
170172 hdr.Set("cache-status", cacheStatusStale(cacheKey, wrapped.StatusCode()))
171173 w.WriteHeader(http.StatusNotModified)
174+ log.Info("client conditional headers match, returning 304")
172175 return
173176 }
174177 }
175178
176179 // Client request was unconditional (or conditional but no longer matches)
177180 // serve the full cached response.
181+ log.Info("serving full cached response to client")
178182 serveCache(w, c.Ttl, cacheKey, &cacheValue)
179183 return
180184 }
......@@ -215,9 +219,11 @@ func serveCache(w http.ResponseWriter, freshness time.Duration, cacheKey string,
215219 age := ageDur.Seconds()
216220 hdr.Set("age", strconv.Itoa(int(age)+1))
217221 hdr.Set("cache-status", cacheStatusHit(cacheKey, freshness.Seconds()))
218- if cacheValue.StatusCode != 0 && cacheValue.StatusCode != http.StatusOK {
219- w.WriteHeader(cacheValue.StatusCode)
222+ statusCode := cacheValue.StatusCode
223+ if statusCode == 0 {
224+ statusCode = http.StatusOK
220225 }
226+ w.WriteHeader(statusCode)
221227 _, _ = w.Write(cacheValue.Body)
222228 }
223229
......@@ -741,7 +747,7 @@ func stripForbiddenHeaders(w http.ResponseWriter, cacheValue *CacheValue) http.H
741747 if isForbiddenHeader(key) {
742748 continue
743749 }
744- hdr[key] = values
750+ hdr[http.CanonicalHeaderKey(key)] = values
745751 }
746752 return hdr
747753 }