From 0b80fd370b3b1b993d0cdb4955a49c1b561794d4 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Thu, 5 May 2022 16:27:57 +0200 Subject: [PATCH 1/4] test for invalid backend response in backend_responses; and for status in backend log --- server/http_error_handler_test.go | 61 +++++++++++++++++++ .../integration/error_handler/06_couper.hcl | 27 ++++++++ 2 files changed, 88 insertions(+) create mode 100644 server/testdata/integration/error_handler/06_couper.hcl diff --git a/server/http_error_handler_test.go b/server/http_error_handler_test.go index e7cb7d802..0178fa549 100644 --- a/server/http_error_handler_test.go +++ b/server/http_error_handler_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + logrustest "github.com/sirupsen/logrus/hooks/test" "github.com/zclconf/go-cty/cty" "github.com/avenga/couper/accesscontrol/jwt" @@ -177,6 +178,66 @@ func TestErrorHandler_Backend(t *testing.T) { } } +func Test_StoreInvalidBackendResponse(t *testing.T) { + client := test.NewHTTPClient() + + shutdown, hook := newCouper("testdata/integration/error_handler/06_couper.hcl", test.New(t)) + defer shutdown() + + type testcase struct { + path string + expBody string + expStatus int + expBackendStatus int + expValidation string + } + + for _, tc := range []testcase{ + {"/anything", `{"req_path":"/anything","resp_ct":"application/json","resp_json_body_query":{},"resp_status":200}`, 418, 200, "status is not supported"}, + } { + t.Run(tc.path, func(st *testing.T) { + helper := test.New(st) + hook.Reset() + + req, err := http.NewRequest(http.MethodGet, "http://anyserver:8080"+tc.path, nil) + helper.Must(err) + + res, err := client.Do(req) + helper.Must(err) + + if res.StatusCode != tc.expStatus { + st.Errorf("status code want: %d, got: %d", tc.expStatus, res.StatusCode) + } + + resBytes, err := io.ReadAll(res.Body) + defer res.Body.Close() + helper.Must(err) + + if !bytes.Contains(resBytes, []byte(tc.expBody)) { + st.Errorf("body\nwant: %s,\ngot: %s", tc.expBody, resBytes) + } + + backendStatus, validation := getBackendLogStatusAndValidation(hook) + if backendStatus != tc.expBackendStatus { + st.Errorf("backend status want: %d, got: %d", tc.expBackendStatus, backendStatus) + } + if validation != tc.expValidation { + st.Errorf("validation want: %s, got: %s", tc.expValidation, validation) + } + }) + } +} + +func getBackendLogStatusAndValidation(hook *logrustest.Hook) (int, string) { + for _, entry := range hook.AllEntries() { + if entry.Data["type"] == "couper_backend" { + return entry.Data["status"].(int), entry.Data["validation"].([]string)[0] + } + } + + return -1, "" +} + func TestAccessControl_ErrorHandler_Permissions(t *testing.T) { client := test.NewHTTPClient() diff --git a/server/testdata/integration/error_handler/06_couper.hcl b/server/testdata/integration/error_handler/06_couper.hcl new file mode 100644 index 000000000..f74b7dfb7 --- /dev/null +++ b/server/testdata/integration/error_handler/06_couper.hcl @@ -0,0 +1,27 @@ +server { + api { + endpoint "/anything" { + proxy { + backend { + origin = "${env.COUPER_TEST_BACKEND_ADDR}" + + openapi { + file = "02_schema.yaml" + } + } + } + } + + error_handler "backend_openapi_validation" { + response { + status = 418 + json_body = { + req_path = backend_requests.default.path + resp_status = backend_responses.default.status + resp_json_body_query = backend_responses.default.json_body.Query + resp_ct = backend_responses.default.headers.content-type + } + } + } + } +} From 5bf93c3b9ffd00261c63e4b4e244dfabcceb2cf4 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Wed, 22 Dec 2021 11:03:25 +0100 Subject: [PATCH 2/4] return invalid response instead of nil --- handler/transport/backend.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/transport/backend.go b/handler/transport/backend.go index 5441eb490..20e012b14 100644 --- a/handler/transport/backend.go +++ b/handler/transport/backend.go @@ -225,7 +225,7 @@ func (b *Backend) openAPIValidate(req *http.Request, tc *Config, deadlineErr <-c } if err = b.openAPIValidator.ValidateResponse(beresp, requestValidationInput); err != nil { - return nil, errors.BackendOpenapiValidation.Label(b.name).With(err).Status(http.StatusBadGateway) + return beresp, errors.BackendOpenapiValidation.Label(b.name).With(err).Status(http.StatusBadGateway) } return beresp, nil From c75d9c8e3dac344785e7a01797dff8e561735a43 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Wed, 22 Dec 2021 11:04:39 +0100 Subject: [PATCH 3/4] create error beresp only for missing real beresp --- handler/transport/backend.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/handler/transport/backend.go b/handler/transport/backend.go index 20e012b14..5dff3505a 100644 --- a/handler/transport/backend.go +++ b/handler/transport/backend.go @@ -171,13 +171,15 @@ func (b *Backend) RoundTrip(req *http.Request) (*http.Response, error) { } if err != nil { - berespErr := &http.Response{ - Request: req, - } // provide outreq (variable) on error cases + if beresp == nil { + beresp = &http.Response{ + Request: req, + } // provide outreq (variable) on error cases + } if varSync, ok := req.Context().Value(request.ContextVariablesSynced).(*eval.SyncedVariables); ok { - varSync.Set(berespErr) + varSync.Set(beresp) } - return berespErr, err + return beresp, err } if retry, rerr := b.withRetryTokenRequest(req, beresp); rerr != nil { From 69432162464d8dc92fe89442359127c2220c751e Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Thu, 5 May 2022 16:59:00 +0200 Subject: [PATCH 4/4] changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdfe7c930..35dfb1a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Unreleased changes are available as `avenga/couper:edge` container. * Request methods are treated case-insensitively when comparing them to methods in the `allowed_methods` attribute of [`api`](./docs/REFERENCE.md#api-block) or [`endpoint`](./docs/REFERENCE.md#endpoint-block) blocks ([#478](https://github.com/avenga/couper/pull/478)) * Do not allow multiple `backend` blocks in `proxy` and `request` blocks ([#483](https://github.com/avenga/couper/pull/483)) * Panic if an [`error_handler` block](./docs/REFERENCE.md#error-handler-block) following another `error_handler` block has no label ([#486](https://github.com/avenga/couper/pull/486)) + * Invalid (by [OpenAPI validation](./docs/REFERENCE.md#openapi-block)) backend response missing in [`backend_responses`](./docs/REFERENCE.md#backend_responses) ([#501](https://github.com/avenga/couper/pull/501)) * **Removed** * support for `beta_oidc` block (use [`oidc` block](./docs/REFERENCE.md#oidc-block) instead) ([#475](https://github.com/avenga/couper/pull/475))