Skip to content

Commit 7fa2015

Browse files
committed
fix: support gzip-encoded responses from the server
1 parent 067637c commit 7fa2015

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

e2e/proxy_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"encoding/json"
88
"fmt"
9+
"strings"
910
"time"
1011

1112
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
@@ -1197,6 +1198,62 @@ var _ = Describe("Proxy", func() {
11971198
})
11981199
})
11991200
})
1201+
1202+
When("getting a large configmap through the proxy", func() {
1203+
It("succeeds even when the upstream response is gzip-encoded", func(ctx context.Context) {
1204+
// Allow configmap gets unconditionally (no SpiceDB check needed).
1205+
// The point of this test is to verify the proxy correctly handles
1206+
// gzip-encoded responses from the kube API server.
1207+
//
1208+
// We use GET (not CREATE) because the kube API server only gzip-encodes
1209+
// *responses* above ~128KB. A GET of a pre-existing large object reliably
1210+
// triggers gzip encoding without going through the workflow engine.
1211+
// Without the Accept-Encoding fix in the proxy's Director, FilterResp
1212+
// would receive a gzip-compressed body and fail to decode it as JSON.
1213+
configMapRule := proxyrule.Config{
1214+
Spec: proxyrule.Spec{
1215+
Matches: []proxyrule.Match{{
1216+
GroupVersion: "v1",
1217+
Resource: "configmaps",
1218+
Verbs: []string{"get"},
1219+
}},
1220+
// No Checks: unconditional allow for matched requests.
1221+
},
1222+
}
1223+
matcher, err := rules.NewMapMatcher([]proxyrule.Config{configMapRule})
1224+
Expect(err).To(Succeed())
1225+
*proxySrv.Matcher = matcher
1226+
1227+
// Create the namespace and configmap directly via adminClient (bypasses
1228+
// the proxy entirely). The proxy is only involved in the GET below.
1229+
Expect(CreateNamespace(ctx, adminClient, paulNamespace)).To(Succeed())
1230+
const dataSize = 300 * 1024 // 300KB — large enough to trigger kube's gzip encoding
1231+
cmName := names.SimpleNameGenerator.GenerateName("large-cm-")
1232+
_, err = adminClient.CoreV1().ConfigMaps(paulNamespace).Create(ctx,
1233+
&corev1.ConfigMap{
1234+
ObjectMeta: metav1.ObjectMeta{
1235+
Name: cmName,
1236+
Namespace: paulNamespace,
1237+
},
1238+
Data: map[string]string{
1239+
"payload": strings.Repeat("x", dataSize),
1240+
},
1241+
}, metav1.CreateOptions{})
1242+
Expect(err).To(Succeed())
1243+
1244+
// GET the large configmap through the proxy.
1245+
// kube will gzip-encode the ~300KB response because paulClient sends
1246+
// Accept-Encoding: gzip. The proxy strips this header in its Director
1247+
// so its own transport takes ownership of gzip negotiation and
1248+
// auto-decompresses before FilterResp runs — ensuring FilterResp always
1249+
// sees plain JSON regardless of response size.
1250+
cm, err := paulClient.CoreV1().ConfigMaps(paulNamespace).Get(ctx, cmName, metav1.GetOptions{})
1251+
Expect(err).To(Succeed())
1252+
Expect(cm.Name).To(Equal(cmName))
1253+
// Verify the full payload came back intact (not truncated or corrupted).
1254+
Expect(cm.Data["payload"]).To(HaveLen(dataSize))
1255+
})
1256+
})
12001257
})
12011258
})
12021259

pkg/proxy/server.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ func NewServer(ctx context.Context, c *CompletedConfig) (*Server, error) {
9999
host := strings.TrimPrefix(clusterHost, "https://")
100100
req.URL.Host = strings.TrimSuffix(host, "/")
101101
req.URL.Scheme = "https"
102+
// Remove Accept-Encoding so the proxy's transport owns gzip negotiation.
103+
// When the transport adds Accept-Encoding: gzip itself, it also auto-decompresses
104+
// the response and strips Content-Encoding: gzip before ModifyResponse/FilterResp
105+
// runs. This ensures FilterResp always receives uncompressed bytes regardless of
106+
// response size.
107+
req.Header.Del("Accept-Encoding")
102108
},
103109
ModifyResponse: func(response *http.Response) error {
104110
klog.V(3).InfoSDepth(1, "upstream Kubernetes API response",

0 commit comments

Comments
 (0)