|
6 | 6 | "context" |
7 | 7 | "encoding/json" |
8 | 8 | "fmt" |
| 9 | + "strings" |
9 | 10 | "time" |
10 | 11 |
|
11 | 12 | v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" |
@@ -1197,6 +1198,62 @@ var _ = Describe("Proxy", func() { |
1197 | 1198 | }) |
1198 | 1199 | }) |
1199 | 1200 | }) |
| 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 | + }) |
1200 | 1257 | }) |
1201 | 1258 | }) |
1202 | 1259 |
|
|
0 commit comments