- commit
- 66ac6b0
- parent
- acf6290
- author
- Eric Bower
- date
- 2026-04-22 11:00:24 -0400 EDT
fix(pgs): dupe headers
2 files changed,
+29,
-11
+15,
-3
1@@ -62,6 +62,14 @@ func newProxyServe(logger *slog.Logger) *proxyServe {
2 return &proxyServe{Logger: logger, transport: transport}
3 }
4
5+// Headers that should be stripped from upstream responses because the CDN's
6+// cache layer will add its own versions.
7+var stripHeaders = map[string]bool{
8+ "age": true,
9+ "cache-status": true,
10+ "date": true,
11+}
12+
13 func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) {
14 // Dial ash.pgs.sh but keep the original Host header so ash.pgs.sh
15 // can route the request to the correct subdomain (zmx.sh, etc.).
16@@ -90,10 +98,13 @@ func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) {
17 _ = resp.Body.Close()
18 }()
19
20+ // Copy headers from upstream, but strip cache-related headers that the
21+ // CDN's cache layer will regenerate
22 for k, vals := range resp.Header {
23- for _, v := range vals {
24- w.Header().Set(k, v)
25+ if stripHeaders[strings.ToLower(k)] {
26+ continue
27 }
28+ w.Header()[k] = vals
29 }
30 w.WriteHeader(resp.StatusCode)
31 _, _ = io.Copy(w, resp.Body)
32@@ -121,11 +132,12 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
33 if err != nil {
34 c.Logger.Error("check request", "err", err)
35 }
36- writer.WriteHeader(resp.StatusCode)
37 defer func() {
38 _ = resp.Body.Close()
39 }()
40+ writer.WriteHeader(resp.StatusCode)
41 _, _ = io.Copy(writer, resp.Body)
42+ return
43 }
44
45 c.Cache.ServeHTTP(writer, req)
+14,
-8
1@@ -95,9 +95,6 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2 return
3 }
4
5- log.Info("cache miss, requesting upstream", "err", err)
6- c.AddCacheMiss()
7-
8 // RFC 9111 4.2.4 + 4.3.1/4.3.2: stale must-revalidate entries must be
9 // revalidated with conditional headers derived from the stored response.
10 // Preserve original client conditional headers so we can evaluate them
11@@ -120,6 +117,8 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
12 }
13 }
14
15+ log.Info("cache miss, requesting upstream", "err", err)
16+ c.AddCacheMiss()
17 wrapped := &responseWriter{ResponseWriter: w}
18 c.Upstream.ServeHTTP(wrapped, r)
19 c.AddUpstreamRequest()
20@@ -128,16 +127,18 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
21 // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4
22 // A 304 response updates header metadata but preserves the cached body.
23 if wrapped.StatusCode() == http.StatusNotModified {
24- log.Info("304 not modified, updating cached headers")
25 existingData, exists := c.Cache.Get(cacheKey)
26 if !exists {
27 // Cache entry vanished; forward the 304 as-is.
28+ log.Info("no cache entry found, forwarding 304 as-is")
29 wrapped.Send()
30 return
31 }
32
33 var cacheValue CacheValue
34- if json.Unmarshal(existingData, &cacheValue) != nil {
35+ err = json.Unmarshal(existingData, &cacheValue)
36+ if err != nil {
37+ log.Error("json unmarshal", "err", err)
38 wrapped.Send()
39 return
40 }
41@@ -153,6 +154,7 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
42 // Revalidation refreshes the entry -- reset CreatedAt so it's fresh again.
43 cacheValue.CreatedAt = time.Now()
44 enc, _ := json.Marshal(cacheValue)
45+ log.Info("updating cached headers from 304 response")
46 c.Cache.Remove(cacheKey)
47 c.Cache.Add(cacheKey, enc)
48 c.AddCacheItem(float64(len(enc)))
49@@ -169,12 +171,14 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
50 hdr.Set("age", strconv.Itoa(int(ageDur.Seconds())+1))
51 hdr.Set("cache-status", cacheStatusStale(cacheKey, wrapped.StatusCode()))
52 w.WriteHeader(http.StatusNotModified)
53+ log.Info("client conditional headers match, returning 304")
54 return
55 }
56 }
57
58 // Client request was unconditional (or conditional but no longer matches)
59 // serve the full cached response.
60+ log.Info("serving full cached response to client")
61 serveCache(w, c.Ttl, cacheKey, &cacheValue)
62 return
63 }
64@@ -215,9 +219,11 @@ func serveCache(w http.ResponseWriter, freshness time.Duration, cacheKey string,
65 age := ageDur.Seconds()
66 hdr.Set("age", strconv.Itoa(int(age)+1))
67 hdr.Set("cache-status", cacheStatusHit(cacheKey, freshness.Seconds()))
68- if cacheValue.StatusCode != 0 && cacheValue.StatusCode != http.StatusOK {
69- w.WriteHeader(cacheValue.StatusCode)
70+ statusCode := cacheValue.StatusCode
71+ if statusCode == 0 {
72+ statusCode = http.StatusOK
73 }
74+ w.WriteHeader(statusCode)
75 _, _ = w.Write(cacheValue.Body)
76 }
77
78@@ -741,7 +747,7 @@ func stripForbiddenHeaders(w http.ResponseWriter, cacheValue *CacheValue) http.H
79 if isForbiddenHeader(key) {
80 continue
81 }
82- hdr[key] = values
83+ hdr[http.CanonicalHeaderKey(key)] = values
84 }
85 return hdr
86 }