diff --git a/bouncer.go b/bouncer.go index d3837b4..e55a425 100644 --- a/bouncer.go +++ b/bouncer.go @@ -746,7 +746,7 @@ func isBodyUnreadable(httpReq *http.Request) bool { // isMethodWithBody used only when isBodyUnreadable returns true but the request method can't have body. func isMethodWithBody(method string) bool { switch method { - case http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete: + case http.MethodPost, http.MethodPut, http.MethodPatch: return true default: return false diff --git a/bouncer_test.go b/bouncer_test.go index 00b2f10..a64873d 100644 --- a/bouncer_test.go +++ b/bouncer_test.go @@ -600,3 +600,66 @@ func Test_appsecQuery_reusesConnection(t *testing.T) { }) } } + +func newUnreadableRequest(method string, done <-chan struct{}) *http.Request { + req, _ := http.NewRequest(method, "http://localhost/api/admin/reservations/8fff14a2", blockingBody{done: done}) + req.ProtoMajor = 3 + req.ContentLength = -1 + return req +} + +func Test_appsecQuery_unreadableBodyMethods(t *testing.T) { + appsecServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + })) + defer appsecServer.Close() + + appsecURL, _ := url.Parse(appsecServer.URL) + + tests := []struct { + method string + wantDropped bool + }{ + {method: http.MethodGet, wantDropped: false}, + {method: http.MethodHead, wantDropped: false}, + {method: http.MethodOptions, wantDropped: false}, + {method: http.MethodDelete, wantDropped: false}, + {method: http.MethodPost, wantDropped: true}, + {method: http.MethodPut, wantDropped: true}, + {method: http.MethodPatch, wantDropped: true}, + } + + for _, tt := range tests { + t.Run(tt.method, func(t *testing.T) { + bouncer := &Bouncer{ + appsecScheme: appsecURL.Scheme, + appsecHost: appsecURL.Host, + appsecPath: "/", + appsecBodyLimit: 10485760, + appsecUnreadableBodyBlock: true, + httpAppsecClient: appsecServer.Client(), + log: logger.New("INFO", ""), + } + + done := make(chan struct{}) + defer close(done) + + finished := make(chan error, 1) + go func() { + finished <- appsecQuery(bouncer, "1.2.3.4", newUnreadableRequest(tt.method, done)) + }() + + select { + case err := <-finished: + if tt.wantDropped && err == nil { + t.Errorf("appsecQuery() on an unreadable-body %s: expected the request to be dropped, got nil", tt.method) + } + if !tt.wantDropped && err != nil { + t.Errorf("appsecQuery() on a bodyless HTTP/3 %s returned error: %v", tt.method, err) + } + case <-time.After(2 * time.Second): + t.Fatalf("appsecQuery() blocked on an unreadable %s body", tt.method) + } + }) + } +}