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 { | |
| 62 | 62 | return &proxyServe{Logger: logger, transport: transport} | |
| 63 | 63 | } | |
| 64 | 64 | ||
| 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 | + | ||
| 65 | 73 | func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) { | |
| 66 | 74 | // Dial ash.pgs.sh but keep the original Host header so ash.pgs.sh | |
| 67 | 75 | // 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) { | |
| 90 | 98 | _ = resp.Body.Close() | |
| 91 | 99 | }() | |
| 92 | 100 | ||
| 101 | + | // Copy headers from upstream, but strip cache-related headers that the | |
| 102 | + | // CDN's cache layer will regenerate | |
| 93 | 103 | 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 | |
| 96 | 106 | } | |
| 107 | + | w.Header()[k] = vals | |
| 97 | 108 | } | |
| 98 | 109 | w.WriteHeader(resp.StatusCode) | |
| 99 | 110 | _, _ = io.Copy(w, resp.Body) |
| ... | ... | @@ -121,11 +132,12 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) { | |
| 121 | 132 | if err != nil { | |
| 122 | 133 | c.Logger.Error("check request", "err", err) | |
| 123 | 134 | } | |
| 124 | - | writer.WriteHeader(resp.StatusCode) | |
| 125 | 135 | defer func() { | |
| 126 | 136 | _ = resp.Body.Close() | |
| 127 | 137 | }() | |
| 138 | + | writer.WriteHeader(resp.StatusCode) | |
| 128 | 139 | _, _ = io.Copy(writer, resp.Body) | |
| 140 | + | return | |
| 129 | 141 | } | |
| 130 | 142 | ||
| 131 | 143 | c.Cache.ServeHTTP(writer, req) |
+14,
-8
| ... | ... | @@ -95,9 +95,6 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 95 | 95 | return | |
| 96 | 96 | } | |
| 97 | 97 | ||
| 98 | - | log.Info("cache miss, requesting upstream", "err", err) | |
| 99 | - | c.AddCacheMiss() | |
| 100 | - | ||
| 101 | 98 | // RFC 9111 4.2.4 + 4.3.1/4.3.2: stale must-revalidate entries must be | |
| 102 | 99 | // revalidated with conditional headers derived from the stored response. | |
| 103 | 100 | // Preserve original client conditional headers so we can evaluate them |
| ... | ... | @@ -120,6 +117,8 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 120 | 117 | } | |
| 121 | 118 | } | |
| 122 | 119 | ||
| 120 | + | log.Info("cache miss, requesting upstream", "err", err) | |
| 121 | + | c.AddCacheMiss() | |
| 123 | 122 | wrapped := &responseWriter{ResponseWriter: w} | |
| 124 | 123 | c.Upstream.ServeHTTP(wrapped, r) | |
| 125 | 124 | c.AddUpstreamRequest() |
| ... | ... | @@ -128,16 +127,18 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 128 | 127 | // https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4 | |
| 129 | 128 | // A 304 response updates header metadata but preserves the cached body. | |
| 130 | 129 | if wrapped.StatusCode() == http.StatusNotModified { | |
| 131 | - | log.Info("304 not modified, updating cached headers") | |
| 132 | 130 | existingData, exists := c.Cache.Get(cacheKey) | |
| 133 | 131 | if !exists { | |
| 134 | 132 | // Cache entry vanished; forward the 304 as-is. | |
| 133 | + | log.Info("no cache entry found, forwarding 304 as-is") | |
| 135 | 134 | wrapped.Send() | |
| 136 | 135 | return | |
| 137 | 136 | } | |
| 138 | 137 | ||
| 139 | 138 | 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) | |
| 141 | 142 | wrapped.Send() | |
| 142 | 143 | return | |
| 143 | 144 | } |
| ... | ... | @@ -153,6 +154,7 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 153 | 154 | // Revalidation refreshes the entry -- reset CreatedAt so it's fresh again. | |
| 154 | 155 | cacheValue.CreatedAt = time.Now() | |
| 155 | 156 | enc, _ := json.Marshal(cacheValue) | |
| 157 | + | log.Info("updating cached headers from 304 response") | |
| 156 | 158 | c.Cache.Remove(cacheKey) | |
| 157 | 159 | c.Cache.Add(cacheKey, enc) | |
| 158 | 160 | c.AddCacheItem(float64(len(enc))) |
| ... | ... | @@ -169,12 +171,14 @@ func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
| 169 | 171 | hdr.Set("age", strconv.Itoa(int(ageDur.Seconds())+1)) | |
| 170 | 172 | hdr.Set("cache-status", cacheStatusStale(cacheKey, wrapped.StatusCode())) | |
| 171 | 173 | w.WriteHeader(http.StatusNotModified) | |
| 174 | + | log.Info("client conditional headers match, returning 304") | |
| 172 | 175 | return | |
| 173 | 176 | } | |
| 174 | 177 | } | |
| 175 | 178 | ||
| 176 | 179 | // Client request was unconditional (or conditional but no longer matches) | |
| 177 | 180 | // serve the full cached response. | |
| 181 | + | log.Info("serving full cached response to client") | |
| 178 | 182 | serveCache(w, c.Ttl, cacheKey, &cacheValue) | |
| 179 | 183 | return | |
| 180 | 184 | } |
| ... | ... | @@ -215,9 +219,11 @@ func serveCache(w http.ResponseWriter, freshness time.Duration, cacheKey string, | |
| 215 | 219 | age := ageDur.Seconds() | |
| 216 | 220 | hdr.Set("age", strconv.Itoa(int(age)+1)) | |
| 217 | 221 | 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 | |
| 220 | 225 | } | |
| 226 | + | w.WriteHeader(statusCode) | |
| 221 | 227 | _, _ = w.Write(cacheValue.Body) | |
| 222 | 228 | } | |
| 223 | 229 |