Merge pull request #1522 from soilSpoon/feature/canceled

feature(proxy): Adds special handling for client cancellations in proxy error handler
This commit is contained in:
Luis Pater
2026-02-22 22:02:59 +08:00
committed by GitHub
2 changed files with 30 additions and 0 deletions

View File

@@ -3,6 +3,8 @@ package amp
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"context"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -188,6 +190,10 @@ func createReverseProxy(upstreamURL string, secretSource SecretSource) (*httputi
// Error handler for proxy failures // Error handler for proxy failures
proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) { proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {
// Client-side cancellations are common during polling; suppress logging in this case
if errors.Is(err, context.Canceled) {
return
}
log.Errorf("amp upstream proxy error for %s %s: %v", req.Method, req.URL.Path, err) log.Errorf("amp upstream proxy error for %s %s: %v", req.Method, req.URL.Path, err)
rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(http.StatusBadGateway) rw.WriteHeader(http.StatusBadGateway)

View File

@@ -493,6 +493,30 @@ func TestReverseProxy_ErrorHandler(t *testing.T) {
} }
} }
func TestReverseProxy_ErrorHandler_ContextCanceled(t *testing.T) {
// Test that context.Canceled errors return 499 without generic error response
proxy, err := createReverseProxy("http://example.com", NewStaticSecretSource(""))
if err != nil {
t.Fatal(err)
}
// Create a canceled context to trigger the cancellation path
ctx, cancel := context.WithCancel(context.Background())
cancel() // Cancel immediately
req := httptest.NewRequest(http.MethodGet, "/test", nil).WithContext(ctx)
rr := httptest.NewRecorder()
// Directly invoke the ErrorHandler with context.Canceled
proxy.ErrorHandler(rr, req, context.Canceled)
// Body should be empty for canceled requests (no JSON error response)
body := rr.Body.Bytes()
if len(body) > 0 {
t.Fatalf("expected empty body for canceled context, got: %s", body)
}
}
func TestReverseProxy_FullRoundTrip_Gzip(t *testing.T) { func TestReverseProxy_FullRoundTrip_Gzip(t *testing.T) {
// Upstream returns gzipped JSON without Content-Encoding header // Upstream returns gzipped JSON without Content-Encoding header
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {