Skip to content

Commit de04164

Browse files
committed
fix: use Content-Type header to select response parser
Address review feedback from @shmuelk: instead of trying JSON parse and falling back to SSE on failure, check the Content-Type response header upfront to select the correct parser. - text/event-stream → SSE parser (parseSSEResponseBody) - anything else → JSON parser (json.Unmarshal) Also fix streaming tests: - JSON body tests now use content-type: application/json (not text/event-stream) - Tests receive response header ack before sending body chunks - Tests receive chunk acks for non-final streaming chunks
1 parent de9e2cf commit de04164

2 files changed

Lines changed: 28 additions & 9 deletions

File tree

pkg/handlers/response.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"encoding/json"
2323
"fmt"
2424
"strconv"
25+
"strings"
2526
"time"
2627

2728
eppb "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
@@ -76,14 +77,20 @@ func (s *Server) HandleResponseBody(ctx context.Context, reqCtx *RequestContext,
7677
return s.generateEmptyResponseBodyResponse(responseBodyBytes), nil
7778
}
7879

79-
if err := json.Unmarshal(responseBodyBytes, &reqCtx.Response.Body); err != nil {
80-
// Try parsing as SSE (Server-Sent Events) — streaming responses from providers
81-
// like Anthropic use SSE format which isn't valid JSON.
82-
if sseBody, sseErr := parseSSEResponseBody(responseBodyBytes); sseErr == nil && sseBody != nil {
83-
reqCtx.Response.Body = sseBody
84-
logger.V(logutil.VERBOSE).Info("parsed SSE response body for response plugins")
85-
} else {
86-
logger.Error(err, "Failed to parse response body as JSON or SSE, skipping response plugins")
80+
contentType := reqCtx.Response.Headers["content-type"]
81+
isSSE := strings.Contains(contentType, "text/event-stream")
82+
83+
if isSSE {
84+
sseBody, err := parseSSEResponseBody(responseBodyBytes)
85+
if err != nil || sseBody == nil {
86+
logger.Error(err, "failed to parse SSE response body, skipping response plugins")
87+
return s.generateEmptyResponseBodyResponse(responseBodyBytes), nil
88+
}
89+
reqCtx.Response.Body = sseBody
90+
logger.V(logutil.VERBOSE).Info("parsed SSE response body for response plugins")
91+
} else {
92+
if err := json.Unmarshal(responseBodyBytes, &reqCtx.Response.Body); err != nil {
93+
logger.Error(err, "failed to parse response body as JSON, skipping response plugins")
8794
return s.generateEmptyResponseBodyResponse(responseBodyBytes), nil
8895
}
8996
}

pkg/handlers/server_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func TestHandleResponseBody_Streaming(t *testing.T) {
210210
respHeaders := utils.BuildEnvoyGRPCHeaders(map[string]string{
211211
"x-test": "body",
212212
":method": "POST",
213-
"content-type": "text/event-stream",
213+
"content-type": "application/json",
214214
}, true)
215215
request = &extProcPb.ProcessingRequest{
216216
Request: &extProcPb.ProcessingRequest_ResponseHeaders{
@@ -221,6 +221,12 @@ func TestHandleResponseBody_Streaming(t *testing.T) {
221221
t.Fatalf("send response headers: %v", err)
222222
}
223223

224+
// Receive the response header ack before sending body chunks.
225+
// HandleResponseHeaders always responds immediately so Envoy proceeds.
226+
if _, err := process.Recv(); err != nil {
227+
t.Fatalf("recv response headers ack: %v", err)
228+
}
229+
224230
for _, c := range tc.chunks {
225231
request = &extProcPb.ProcessingRequest{
226232
Request: &extProcPb.ProcessingRequest_ResponseBody{
@@ -233,6 +239,12 @@ func TestHandleResponseBody_Streaming(t *testing.T) {
233239
if err := process.Send(request); err != nil {
234240
t.Fatalf("send response body chunk: %v", err)
235241
}
242+
// For non-final chunks, receive the streaming ack
243+
if !c.endOfStream {
244+
if _, err := process.Recv(); err != nil {
245+
t.Fatalf("recv chunk ack: %v", err)
246+
}
247+
}
236248
}
237249

238250
got := make([]*extProcPb.ProcessingResponse, 0, len(want))

0 commit comments

Comments
 (0)