From 4ee567cd337348795b3e307b915090ae50f4fad6 Mon Sep 17 00:00:00 2001 From: Katsuma Tanaka Date: Sat, 19 Oct 2024 22:08:10 +0900 Subject: [PATCH 1/4] Return http.StatusMethodNotAllowed for request method mismatch --- internal/test/chi/oapi_validate_test.go | 13 +++++++++++ internal/test/gorilla/oapi_validate_test.go | 15 ++++++++++++- internal/test/nethttp/oapi_validate_test.go | 25 ++++++++++++++++++++- oapi_validate.go | 3 +++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/internal/test/chi/oapi_validate_test.go b/internal/test/chi/oapi_validate_test.go index a2b03b2..9e7b561 100644 --- a/internal/test/chi/oapi_validate_test.go +++ b/internal/test/chi/oapi_validate_test.go @@ -371,6 +371,19 @@ func testRequestValidatorBasicFunctions(t *testing.T, r *chi.Mux) { called = false } + // Send a request with wrong HTTP method + { + body := struct { + Name string `json:"name"` + }{ + Name: "Marcin", + } + rec := doPost(t, r, "http://deepmap.ai/resource", body) + assert.Equal(t, http.StatusMethodNotAllowed, rec.Code) + assert.False(t, called, "Handler should not have been called") + called = false + } + // Add a handler for the POST message r.Post("/resource", func(w http.ResponseWriter, r *http.Request) { called = true diff --git a/internal/test/gorilla/oapi_validate_test.go b/internal/test/gorilla/oapi_validate_test.go index cd1502d..842e5a8 100644 --- a/internal/test/gorilla/oapi_validate_test.go +++ b/internal/test/gorilla/oapi_validate_test.go @@ -10,8 +10,8 @@ import ( "net/url" "testing" - "github.com/oapi-codegen/testutil" middleware "github.com/oapi-codegen/nethttp-middleware" + "github.com/oapi-codegen/testutil" "github.com/getkin/kin-openapi/openapi3" "github.com/getkin/kin-openapi/openapi3filter" @@ -371,6 +371,19 @@ func testRequestValidatorBasicFunctions(t *testing.T, r *mux.Router) { called = false } + // Send a request with wrong HTTP method + { + body := struct { + Name string `json:"name"` + }{ + Name: "Marcin", + } + rec := doPost(t, r, "http://deepmap.ai/resource", body) + assert.Equal(t, http.StatusMethodNotAllowed, rec.Code) + assert.False(t, called, "Handler should not have been called") + called = false + } + // Add a handler for the POST message r.HandleFunc("/resource", func(w http.ResponseWriter, r *http.Request) { called = true diff --git a/internal/test/nethttp/oapi_validate_test.go b/internal/test/nethttp/oapi_validate_test.go index 279969c..d4fc48d 100644 --- a/internal/test/nethttp/oapi_validate_test.go +++ b/internal/test/nethttp/oapi_validate_test.go @@ -10,8 +10,8 @@ import ( "net/url" "testing" - "github.com/oapi-codegen/testutil" middleware "github.com/oapi-codegen/nethttp-middleware" + "github.com/oapi-codegen/testutil" "github.com/getkin/kin-openapi/openapi3" "github.com/getkin/kin-openapi/openapi3filter" @@ -42,6 +42,16 @@ func doPost(t *testing.T, mux http.Handler, rawURL string, jsonBody interface{}) return response.Recorder } +func doPatch(t *testing.T, mux http.Handler, rawURL string, jsonBody interface{}) *httptest.ResponseRecorder { + u, err := url.Parse(rawURL) + if err != nil { + t.Fatalf("Invalid url: %s", rawURL) + } + + response := testutil.NewRequest().Patch(u.RequestURI()).WithHost(u.Host).WithJsonBody(jsonBody).GoWithHTTPHandler(t, mux) + return response.Recorder +} + // use wraps a given http.ServeMux with middleware for execution func use(r *http.ServeMux, mw func(next http.Handler) http.Handler) http.Handler { return mw(r) @@ -413,6 +423,19 @@ func testRequestValidatorBasicFunctions(t *testing.T, r *http.ServeMux, mw func( called = false } + // Send a request with wrong method + { + body := struct { + Name string `json:"name"` + }{ + Name: "Marcin", + } + rec := doPatch(t, server, "http://deepmap.ai/resource", body) + assert.Equal(t, http.StatusMethodNotAllowed, rec.Code) + assert.False(t, called, "Handler should not have been called") + called = false + } + called = false // Send a good request body { diff --git a/oapi_validate.go b/oapi_validate.go index 5bbce40..e51f296 100644 --- a/oapi_validate.go +++ b/oapi_validate.go @@ -74,6 +74,9 @@ func validateRequest(r *http.Request, router routers.Router, options *Options) ( // Find route route, pathParams, err := router.FindRoute(r) if err != nil { + if errors.Is(err, routers.ErrMethodNotAllowed) { + return http.StatusMethodNotAllowed, err + } return http.StatusNotFound, err // We failed to find a matching route for the request. } From a1a493460667a7dded014e1421d744385570c7d8 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Thu, 24 Apr 2025 16:46:20 +0100 Subject: [PATCH 2/4] sq --- internal/test/gorilla/oapi_validate_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/test/gorilla/oapi_validate_test.go b/internal/test/gorilla/oapi_validate_test.go index cc7687a..129c2d2 100644 --- a/internal/test/gorilla/oapi_validate_test.go +++ b/internal/test/gorilla/oapi_validate_test.go @@ -13,7 +13,6 @@ import ( "testing" middleware "github.com/oapi-codegen/nethttp-middleware" - "github.com/oapi-codegen/testutil" "github.com/getkin/kin-openapi/openapi3" "github.com/getkin/kin-openapi/openapi3filter" From 63e1a91118f667ea10e76f52829fb8a9395716c4 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Thu, 24 Apr 2025 16:47:12 +0100 Subject: [PATCH 3/4] sq --- internal/test/nethttp/oapi_validate_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/test/nethttp/oapi_validate_test.go b/internal/test/nethttp/oapi_validate_test.go index 796b3b5..591d88f 100644 --- a/internal/test/nethttp/oapi_validate_test.go +++ b/internal/test/nethttp/oapi_validate_test.go @@ -13,7 +13,6 @@ import ( "testing" middleware "github.com/oapi-codegen/nethttp-middleware" - "github.com/oapi-codegen/testutil" "github.com/getkin/kin-openapi/openapi3" "github.com/getkin/kin-openapi/openapi3filter" @@ -69,8 +68,19 @@ func doPatch(t *testing.T, mux http.Handler, rawURL string, jsonBody interface{} t.Fatalf("Invalid url: %s", rawURL) } - response := testutil.NewRequest().Patch(u.RequestURI()).WithHost(u.Host).WithJsonBody(jsonBody).GoWithHTTPHandler(t, mux) - return response.Recorder + data, err := json.Marshal(jsonBody) + require.NoError(t, err) + + req, err := http.NewRequest(http.MethodPatch, u.String(), bytes.NewReader(data)) + require.NoError(t, err) + + req.Header.Set("content-type", "application/json") + + rr := httptest.NewRecorder() + + mux.ServeHTTP(rr, req) + + return rr } // use wraps a given http.ServeMux with middleware for execution From 4cd164c18bb983e9840af07e05460d334d19d765 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Thu, 24 Apr 2025 16:50:52 +0100 Subject: [PATCH 4/4] sq --- oapi_validate.go | 1 + oapi_validate_example_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/oapi_validate.go b/oapi_validate.go index a0b8f89..eef83c2 100644 --- a/oapi_validate.go +++ b/oapi_validate.go @@ -90,6 +90,7 @@ func validateRequest(r *http.Request, router routers.Router, options *Options) ( if errors.Is(err, routers.ErrMethodNotAllowed) { return http.StatusMethodNotAllowed, err } + return http.StatusNotFound, err // We failed to find a matching route for the request. } diff --git a/oapi_validate_example_test.go b/oapi_validate_example_test.go index d0cab9a..3e2944f 100644 --- a/oapi_validate_example_test.go +++ b/oapi_validate_example_test.go @@ -163,6 +163,27 @@ components: logResponseBody(rr) fmt.Println() + // ================================================================================ + fmt.Println("# A request with an invalid HTTP method, to a valid path, is rejected with an HTTP 405 Method Not Allowed") + body = map[string]string{ + "invalid": "not expected", + } + + data, err = json.Marshal(body) + must(err) + + req, err = http.NewRequest(http.MethodPatch, "/resource", bytes.NewReader(data)) + must(err) + req.Header.Set("Content-Type", "application/json") + + rr = httptest.NewRecorder() + + server.ServeHTTP(rr, req) + + fmt.Printf("Received an HTTP %d response. Expected HTTP 405\n", rr.Code) + logResponseBody(rr) + fmt.Println() + // ================================================================================ fmt.Println("# A request that is well-formed is passed through to the Handler") body = map[string]string{ @@ -207,6 +228,10 @@ components: // Received an HTTP 400 response. Expected HTTP 400 // Response body: request body has an error: doesn't match schema: property "invalid" is unsupported // + // # A request with an invalid HTTP method, to a valid path, is rejected with an HTTP 405 Method Not Allowed + // Received an HTTP 405 response. Expected HTTP 405 + // Response body: method not allowed + // // # A request that is well-formed is passed through to the Handler // POST /resource was called // Received an HTTP 204 response. Expected HTTP 204