diff --git a/proxy/capture.go b/proxy/capture.go index 5d5cf41..af1b937 100644 --- a/proxy/capture.go +++ b/proxy/capture.go @@ -13,7 +13,7 @@ import ( // readRequestBody 在转发前完整读取请求体,确保读取失败时不会向上游发送损坏请求。 func readRequestBody(r *http.Request, max int64) (captured []byte, truncated bool, err error) { - if r.Body == nil || r.ContentLength == 0 { + if r.Body == nil || r.Body == http.NoBody { return nil, false, nil } body, err := io.ReadAll(r.Body) diff --git a/tests/proxy/robustness_test.go b/tests/proxy/robustness_test.go index 5f91259..0bbe6f3 100644 --- a/tests/proxy/robustness_test.go +++ b/tests/proxy/robustness_test.go @@ -100,6 +100,30 @@ func TestRequestBodyReadFailureReturnsFixed400WithoutUpstream(t *testing.T) { } } +func TestRequestBodyReadFailureWithZeroContentLengthDoesNotReachUpstream(t *testing.T) { + var upstreamCalls atomic.Int32 + upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + upstreamCalls.Add(1) + w.WriteHeader(http.StatusOK) + })) + defer upstream.Close() + u, _ := url.Parse(upstream.URL) + h := newTestHandler(t, u, &captureSubmitter{}, proxy.Options{}) + + req := httptest.NewRequest(http.MethodPost, "http://proxy.test/v1/chat", nil) + req.Body = failingBody{err: errors.New("secret read failure")} + req.ContentLength = 0 + rec := httptest.NewRecorder() + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusBadRequest || rec.Body.String() != "bad request\n" { + t.Fatalf("response=(%d, %q), want fixed 400 bad request", rec.Code, rec.Body.String()) + } + if upstreamCalls.Load() != 0 { + t.Fatalf("upstream called %d times", upstreamCalls.Load()) + } +} + func TestRequestBodyReadFailureAfterCaptureLimitDoesNotReachUpstream(t *testing.T) { var upstreamCalls atomic.Int32 upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {