From 8437577dbb1d0af765fff82a6f1aaeda57ca05a2 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Wed, 1 Jun 2022 18:40:46 +0200 Subject: [PATCH 1/9] remove path attribute from endpoint (and proxy) block --- config/backend.go | 1 + config/meta/attributes.go | 3 - docs/REFERENCE.md | 1 - eval/http.go | 24 ++++-- server/http_backend_test.go | 9 +- server/http_integration_test.go | 86 ------------------- server/testdata/integration/api/06_couper.hcl | 9 -- server/testdata/integration/api/07_couper.hcl | 1 - server/testdata/integration/api/08_couper.hcl | 12 --- server/testdata/integration/api/09_couper.hcl | 1 - server/testdata/integration/api/10_couper.hcl | 1 - server/testdata/integration/api/11_couper.hcl | 1 - server/testdata/integration/api/12_couper.hcl | 1 - .../integration/backends/01_couper.hcl | 9 -- .../integration/backends/05_couper.hcl | 3 +- .../integration/endpoint_eval/01_couper.hcl | 1 - .../integration/endpoint_eval/02_couper.hcl | 47 ---------- .../integration/endpoint_eval/03_couper.hcl | 46 ---------- .../integration/endpoint_eval/05_couper.hcl | 1 - .../integration/endpoint_eval/06_couper.hcl | 1 - .../integration/endpoint_eval/11_couper.hcl | 1 - .../integration/files_spa_api/01_couper.hcl | 1 - .../testdata/integration/vhosts/01_couper.hcl | 2 - 23 files changed, 25 insertions(+), 237 deletions(-) delete mode 100644 server/testdata/integration/api/08_couper.hcl delete mode 100644 server/testdata/integration/endpoint_eval/02_couper.hcl delete mode 100644 server/testdata/integration/endpoint_eval/03_couper.hcl diff --git a/config/backend.go b/config/backend.go index 5e418e26e..296f9f9b3 100644 --- a/config/backend.go +++ b/config/backend.go @@ -48,6 +48,7 @@ func (b Backend) Inline() interface{} { Hostname string `hcl:"hostname,optional"` LogFields map[string]hcl.Expression `hcl:"custom_log_fields,optional"` Origin string `hcl:"origin,optional"` + Path string `hcl:"path,optional"` PathPrefix string `hcl:"path_prefix,optional"` ProxyURL string `hcl:"proxy,optional"` ResponseStatus *uint8 `hcl:"set_response_status,optional"` diff --git a/config/meta/attributes.go b/config/meta/attributes.go index 305e09e88..eda70150b 100644 --- a/config/meta/attributes.go +++ b/config/meta/attributes.go @@ -29,9 +29,6 @@ type Attributes struct { AddResponseHeaders map[string]string `hcl:"add_response_headers,optional"` DelResponseHeaders []string `hcl:"remove_response_headers,optional"` SetResponseHeaders map[string]string `hcl:"set_response_headers,optional"` - - // Other - Path string `hcl:"path,optional"` } func SchemaWithAttributes(schema *hcl.BodySchema) *hcl.BodySchema { diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md index 7fecfa42b..00c197492 100644 --- a/docs/REFERENCE.md +++ b/docs/REFERENCE.md @@ -141,7 +141,6 @@ produce an explicit or implicit client response. | Attribute(s) | Type | Default | Description | Characteristic(s) | Example | |:------------------------|:-----------------|:--------|:------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------| | `request_body_limit` | string | `"64MiB"` | Configures the maximum buffer size while accessing `request.form_body` or `request.json_body` content. | ⚠ Valid units are: `KiB, MiB, GiB` | `request_body_limit = "200KiB"` | -| `path` | string | - | Changeable part of the upstream URL. Changes the path suffix of the outgoing request. | - | - | | `access_control` | tuple (string) | - | Sets predefined [Access Control](#access-control) for `endpoint` block context. | - | `access_control = ["foo"]` | | `disable_access_control` | tuple (string) | - | Disables access controls by name. | - | `disable_access_control = ["foo"]` | | `allowed_methods` | tuple (string) | `["*"]` == `["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]` | Sets allowed methods _overriding_ a default set in the containing `api` block. Requests with a method that is not allowed result in an error response with a `405 Method Not Allowed` status. | The default value `"*"` can be combined with additional methods. Methods are matched case-insensitively. `Access-Control-Allow-Methods` is only sent in response to a [CORS](#cors-block) preflight request, if the method requested by `Access-Control-Request-Method` is an allowed method. | `allowed_methods = ["GET", "POST"]` or `allowed_methods = ["*", "BREW"]` | diff --git a/eval/http.go b/eval/http.go index 61a6664da..d8835d9f4 100644 --- a/eval/http.go +++ b/eval/http.go @@ -100,6 +100,14 @@ func SetBody(req *http.Request, body []byte) { parseForm(req) } +func getPathAttribute(body hcl.Body) (*hcl.Attribute, error) { + bodyContent, _, diags := body.PartialContent(config.BackendInlineSchema) + if diags.HasErrors() { + return nil, errors.Evaluation.With(diags) + } + return bodyContent.Attributes[attrPath], nil +} + func ApplyRequestContext(httpCtx *hcl.EvalContext, body hcl.Body, req *http.Request) error { if req == nil { return nil @@ -107,12 +115,19 @@ func ApplyRequestContext(httpCtx *hcl.EvalContext, body hcl.Body, req *http.Requ headerCtx := req.Header - attrs, err := getAllAttributes(body) + pathAttr, err := getPathAttribute(body) if err != nil { return err } - if err = evalPathAttr(req, attrs, httpCtx); err != nil { + if pathAttr != nil { + if err = evalPathAttr(req, pathAttr, httpCtx); err != nil { + return err + } + } + + attrs, err := getAllAttributes(body) + if err != nil { return err } @@ -264,9 +279,8 @@ func getFormParams(ctx *hcl.EvalContext, req *http.Request, attrs map[string]*hc return nil } -func evalPathAttr(req *http.Request, attrs map[string]*hcl.Attribute, httpCtx *hcl.EvalContext) error { - pathAttr, ok := attrs[attrPath] - if !ok { +func evalPathAttr(req *http.Request, pathAttr *hcl.Attribute, httpCtx *hcl.EvalContext) error { + if pathAttr == nil { return nil } path := req.URL.Path diff --git a/server/http_backend_test.go b/server/http_backend_test.go index a902f18ec..3ddeca5cc 100644 --- a/server/http_backend_test.go +++ b/server/http_backend_test.go @@ -142,11 +142,10 @@ func TestBackend_WithoutOrigin(t *testing.T) { path string message string }{ - {"/proxy/path", `configuration error: anonymous_6_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, - {"/proxy/backend-path", `configuration error: anonymous_15_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, - {"/proxy/url", `configuration error: anonymous_24_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, - {"/request/backend-path", `configuration error: anonymous_37_15: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, - {"/request/url", `configuration error: anonymous_46_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, + {"/proxy/backend-path", `configuration error: anonymous_6_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, + {"/proxy/url", `configuration error: anonymous_15_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, + {"/request/backend-path", `configuration error: anonymous_28_15: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, + {"/request/url", `configuration error: anonymous_37_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, } { t.Run(tc.path, func(st *testing.T) { hook.Reset() diff --git a/server/http_integration_test.go b/server/http_integration_test.go index 30cf5c814..d9b532be7 100644 --- a/server/http_integration_test.go +++ b/server/http_integration_test.go @@ -784,9 +784,6 @@ func TestHTTPServer_PathPrefix(t *testing.T) { {"/v1", expectation{ Path: "/xxx/xxx/v1", }}, - {"/v1/uuu/foo", expectation{ - Path: "/xxx/xxx/api/foo", - }}, {"/v1/vvv/foo", expectation{ Path: "/xxx/xxx/api/foo", }}, @@ -851,25 +848,6 @@ func TestHTTPServer_BackendLogPath(t *testing.T) { } } -func TestHTTPServer_BackendLogPathInEndpoint(t *testing.T) { - client := newClient() - helper := test.New(t) - - shutdown, hook := newCouper("testdata/integration/api/08_couper.hcl", helper) - defer shutdown() - - req, err := http.NewRequest(http.MethodGet, "http://example.com:8080/abc?query#fragment", nil) - helper.Must(err) - - hook.Reset() - _, err = client.Do(req) - helper.Must(err) - - if p := hook.AllEntries()[0].Data["request"].(logging.Fields)["path"]; p != "/new/path/abc?query" { - t.Errorf("Unexpected path given: %s", p) - } -} - func TestHTTPServer_BackendLogRequestProto(t *testing.T) { client := newClient() helper := test.New(t) @@ -3015,70 +2993,6 @@ func TestHTTPServer_request_variables(t *testing.T) { } } -func TestHTTPServer_Endpoint_Evaluation_Inheritance(t *testing.T) { - client := newClient() - - for _, confFile := range []string{"02_couper.hcl", "03_couper.hcl"} { - confPath := path.Join("testdata/integration/endpoint_eval", confFile) - - type expectation struct { - Path string - ResponseStatus int - } - - type testCase struct { - reqPath string - exp expectation - } - - for _, tc := range []testCase{ - {"/endpoint1", expectation{ - Path: "/anything", - ResponseStatus: http.StatusOK, - }}, - {"/endpoint2", expectation{ - Path: "/anything", - ResponseStatus: http.StatusOK, - }}, - {"/endpoint3", expectation{ - Path: "/unset/by/endpoint", - ResponseStatus: http.StatusNotFound, - }}, - {"/endpoint4", expectation{ - Path: "/anything", - ResponseStatus: http.StatusOK, - }}, - } { - t.Run(confFile+"_"+tc.reqPath, func(subT *testing.T) { - helper := test.New(subT) - shutdown, _ := newCouper(confPath, helper) - defer shutdown() - - req, err := http.NewRequest(http.MethodGet, "http://example.com:8080"+tc.reqPath, nil) - helper.Must(err) - - res, err := client.Do(req) - helper.Must(err) - - resBytes, err := io.ReadAll(res.Body) - helper.Must(err) - - _ = res.Body.Close() - - var jsonResult expectation - err = json.Unmarshal(resBytes, &jsonResult) - if err != nil { - subT.Errorf("unmarshal json: %v: got:\n%s", err, string(resBytes)) - } - - if !reflect.DeepEqual(jsonResult, tc.exp) { - subT.Errorf("%q: %q:\nwant:\t%#v\ngot:\t%#v\npayload:\n%s", confFile, tc.reqPath, tc.exp, jsonResult, string(resBytes)) - } - }) - } - } -} - func TestOpenAPIValidateConcurrentRequests(t *testing.T) { helper := test.New(t) client := newClient() diff --git a/server/testdata/integration/api/06_couper.hcl b/server/testdata/integration/api/06_couper.hcl index 007267e71..cd74a6489 100644 --- a/server/testdata/integration/api/06_couper.hcl +++ b/server/testdata/integration/api/06_couper.hcl @@ -10,15 +10,6 @@ server "multi-api" { } } } - endpoint "/uuu/**" { - path = "/api/**" - proxy { - backend { - origin = env.COUPER_TEST_BACKEND_ADDR - path_prefix = "/${request.headers.x-val}/xxx/" - } - } - } endpoint "/vvv/**" { proxy { backend { diff --git a/server/testdata/integration/api/07_couper.hcl b/server/testdata/integration/api/07_couper.hcl index a23e5986d..5d8416e4f 100644 --- a/server/testdata/integration/api/07_couper.hcl +++ b/server/testdata/integration/api/07_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/**" { - path = "/new/path/**" proxy { backend { origin = "${env.COUPER_TEST_BACKEND_ADDR}" diff --git a/server/testdata/integration/api/08_couper.hcl b/server/testdata/integration/api/08_couper.hcl deleted file mode 100644 index 12ba4d893..000000000 --- a/server/testdata/integration/api/08_couper.hcl +++ /dev/null @@ -1,12 +0,0 @@ -server "api" { - api { - endpoint "/**" { - path = "/new/path/**" - proxy { - backend { - origin = "${env.COUPER_TEST_BACKEND_ADDR}" - } - } - } - } -} diff --git a/server/testdata/integration/api/09_couper.hcl b/server/testdata/integration/api/09_couper.hcl index 63648070f..e8c734630 100644 --- a/server/testdata/integration/api/09_couper.hcl +++ b/server/testdata/integration/api/09_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/**" { - path = "/new/path/**" proxy { backend { origin = "${env.COUPER_TEST_BACKEND_ADDR}" diff --git a/server/testdata/integration/api/10_couper.hcl b/server/testdata/integration/api/10_couper.hcl index ba1880cf8..6444f6167 100644 --- a/server/testdata/integration/api/10_couper.hcl +++ b/server/testdata/integration/api/10_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/**" { - path = "/new/path/**" proxy { backend { origin = "${env.COUPER_TEST_BACKEND_ADDR}" diff --git a/server/testdata/integration/api/11_couper.hcl b/server/testdata/integration/api/11_couper.hcl index 9baee0f0d..3d16dc7a9 100644 --- a/server/testdata/integration/api/11_couper.hcl +++ b/server/testdata/integration/api/11_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/**" { - path = "/new/path/**" proxy { backend { origin = "${env.COUPER_TEST_BACKEND_ADDR}" diff --git a/server/testdata/integration/api/12_couper.hcl b/server/testdata/integration/api/12_couper.hcl index 4911c7da6..ea51dda25 100644 --- a/server/testdata/integration/api/12_couper.hcl +++ b/server/testdata/integration/api/12_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/**" { - path = "/new/path/**" proxy { backend { origin = "${env.COUPER_TEST_BACKEND_ADDR}" diff --git a/server/testdata/integration/backends/01_couper.hcl b/server/testdata/integration/backends/01_couper.hcl index e06c21a70..656f86f10 100644 --- a/server/testdata/integration/backends/01_couper.hcl +++ b/server/testdata/integration/backends/01_couper.hcl @@ -2,15 +2,6 @@ server { api { base_path = "/proxy" - endpoint "/path" { - proxy { - path = "/my-path" - backend { - # no origin - } - } - } - endpoint "/backend-path" { proxy { backend { diff --git a/server/testdata/integration/backends/05_couper.hcl b/server/testdata/integration/backends/05_couper.hcl index b71904113..3db6edd93 100644 --- a/server/testdata/integration/backends/05_couper.hcl +++ b/server/testdata/integration/backends/05_couper.hcl @@ -1,8 +1,7 @@ server { endpoint "/**" { proxy { - path = "/**" - url = "{{ .origin }}/" + url = "{{ .origin }}/**" } } } diff --git a/server/testdata/integration/endpoint_eval/01_couper.hcl b/server/testdata/integration/endpoint_eval/01_couper.hcl index 0723dee5f..6623890a0 100644 --- a/server/testdata/integration/endpoint_eval/01_couper.hcl +++ b/server/testdata/integration/endpoint_eval/01_couper.hcl @@ -5,7 +5,6 @@ server "api" { error_file = "./../api_error.json" endpoint "/{path}/{hostname}/{origin}" { - path = "/set/by/endpoint/unset/by/backend" proxy { backend "anything" { path = "/anything" diff --git a/server/testdata/integration/endpoint_eval/02_couper.hcl b/server/testdata/integration/endpoint_eval/02_couper.hcl deleted file mode 100644 index 0a68b29db..000000000 --- a/server/testdata/integration/endpoint_eval/02_couper.hcl +++ /dev/null @@ -1,47 +0,0 @@ -server "api" { - api { - endpoint "/endpoint1" { - path = "/123" - proxy { - backend "anything" { - path = "/anything" - } - } - } - - endpoint "/endpoint2" { - path = "/unset/by/local-backend" - proxy { - backend "anything" { - path = "/anything" - } - } - } - - # don't override path - endpoint "/endpoint3" { - path = "/backend/wins" - proxy { - backend = "anything" - } - } - - endpoint "/endpoint4" { - path = "/anything" - proxy { - backend { - origin = env.COUPER_TEST_BACKEND_ADDR != "" ? env.COUPER_TEST_BACKEND_ADDR : "http://127.0.0.1:1" - } - } - } - - } -} - -definitions { - # backend origin within a definition block gets replaced with the integration test "anything" server. - backend "anything" { - path = "/unset/by/endpoint" - origin = env.COUPER_TEST_BACKEND_ADDR - } -} diff --git a/server/testdata/integration/endpoint_eval/03_couper.hcl b/server/testdata/integration/endpoint_eval/03_couper.hcl deleted file mode 100644 index eebf6c358..000000000 --- a/server/testdata/integration/endpoint_eval/03_couper.hcl +++ /dev/null @@ -1,46 +0,0 @@ -server "api" { - api { - - endpoint "/endpoint1" { - proxy { - backend "anything" { - path = "/anything" - } - } - } - - endpoint "/endpoint2" { - path = "/unset/by/local-backend" - proxy { - backend "anything" { - path = "/anything" - } - } - } - - # don't override path - endpoint "/endpoint3" { - proxy { - backend = "anything" - } - } - - endpoint "/endpoint4" { - path = "/unset/here" - proxy { - backend "anything" { - path = "/anything" - } - } - } - - } -} - -definitions { - # backend origin within a definition block gets replaced with the integration test "anything" server. - backend "anything" { - path = "/unset/by/endpoint" - origin = env.COUPER_TEST_BACKEND_ADDR - } -} diff --git a/server/testdata/integration/endpoint_eval/05_couper.hcl b/server/testdata/integration/endpoint_eval/05_couper.hcl index 9a9111669..c92aa59c4 100644 --- a/server/testdata/integration/endpoint_eval/05_couper.hcl +++ b/server/testdata/integration/endpoint_eval/05_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/" { - path = "/yyy" proxy { backend = "anything" } diff --git a/server/testdata/integration/endpoint_eval/06_couper.hcl b/server/testdata/integration/endpoint_eval/06_couper.hcl index 2bea1aa6d..7bbd0e120 100644 --- a/server/testdata/integration/endpoint_eval/06_couper.hcl +++ b/server/testdata/integration/endpoint_eval/06_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/" { - path = "/yyy" proxy { backend "anything" { path = "/zzz" diff --git a/server/testdata/integration/endpoint_eval/11_couper.hcl b/server/testdata/integration/endpoint_eval/11_couper.hcl index a5bffb09c..7ca92d23f 100644 --- a/server/testdata/integration/endpoint_eval/11_couper.hcl +++ b/server/testdata/integration/endpoint_eval/11_couper.hcl @@ -1,7 +1,6 @@ server "api" { api { endpoint "/**" { - path = "/**" proxy { backend { origin = env.COUPER_TEST_BACKEND_ADDR diff --git a/server/testdata/integration/files_spa_api/01_couper.hcl b/server/testdata/integration/files_spa_api/01_couper.hcl index f67780ea7..8cffaec7f 100644 --- a/server/testdata/integration/files_spa_api/01_couper.hcl +++ b/server/testdata/integration/files_spa_api/01_couper.hcl @@ -10,7 +10,6 @@ server "spa" { api { error_file = "./../api_error.json" endpoint "/api" { - path = "/" proxy { backend = "anything" } diff --git a/server/testdata/integration/vhosts/01_couper.hcl b/server/testdata/integration/vhosts/01_couper.hcl index 3f6784995..6fde75ac1 100644 --- a/server/testdata/integration/vhosts/01_couper.hcl +++ b/server/testdata/integration/vhosts/01_couper.hcl @@ -16,7 +16,6 @@ server "v-server1" { error_file = "./../api_error.json" endpoint "/" { - path = "/" proxy { backend = "anything" } @@ -43,7 +42,6 @@ server "v-server2" { error_file = "./../api_error.json" endpoint "/" { - path = "/" proxy { backend = "anything" } From d7858c70bac5fb82eb30caa7e353b7505311e701 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Wed, 1 Jun 2022 18:47:00 +0200 Subject: [PATCH 2/9] test case should probably test a request, not a proxy --- server/http_backend_test.go | 2 +- server/testdata/integration/backends/01_couper.hcl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/http_backend_test.go b/server/http_backend_test.go index 3ddeca5cc..b1ee46992 100644 --- a/server/http_backend_test.go +++ b/server/http_backend_test.go @@ -145,7 +145,7 @@ func TestBackend_WithoutOrigin(t *testing.T) { {"/proxy/backend-path", `configuration error: anonymous_6_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, {"/proxy/url", `configuration error: anonymous_15_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, {"/request/backend-path", `configuration error: anonymous_28_15: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, - {"/request/url", `configuration error: anonymous_37_13: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, + {"/request/url", `configuration error: anonymous_37_15: the origin attribute has to contain an absolute URL with a valid hostname: ""`}, } { t.Run(tc.path, func(st *testing.T) { hook.Reset() diff --git a/server/testdata/integration/backends/01_couper.hcl b/server/testdata/integration/backends/01_couper.hcl index 656f86f10..d26158ca0 100644 --- a/server/testdata/integration/backends/01_couper.hcl +++ b/server/testdata/integration/backends/01_couper.hcl @@ -34,7 +34,7 @@ server { } endpoint "/url" { - proxy { + request { url = "/my-path" backend { # no origin From 28cc04ac219fa881bbe460c0a61df9bc2a6790cd Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Thu, 2 Jun 2022 10:54:05 +0200 Subject: [PATCH 3/9] enhance error detail with hint; add test for path attribute in various valid and invalid places --- config/configload/schema.go | 15 ++++++ config/configload/validate_test.go | 86 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/config/configload/schema.go b/config/configload/schema.go index 3c274ed91..b502cf7a1 100644 --- a/config/configload/schema.go +++ b/config/configload/schema.go @@ -30,6 +30,7 @@ var ( func ValidateConfigSchema(body hcl.Body, obj interface{}) hcl.Diagnostics { attrs, blocks, diags := getSchemaComponents(body, obj) + diags = enhanceErrors(diags, obj) diags = filterValidErrors(attrs, blocks, diags) for _, block := range blocks { @@ -39,6 +40,20 @@ func ValidateConfigSchema(body hcl.Body, obj interface{}) hcl.Diagnostics { return uniqueErrors(diags) } +// enhanceErrors enhances diagnostics e.g. by providing a hint how to solve the issue +func enhanceErrors(diags hcl.Diagnostics, obj interface{}) hcl.Diagnostics { + _, isEndpoint := obj.(*config.Endpoint) + _, isProxy := obj.(*config.Proxy) + for _, err := range diags { + if err.Summary == summUnsupportedAttr && (isEndpoint || isProxy) { + if matches := reFetchUnexpectedArg.FindStringSubmatch(err.Detail); matches != nil && matches[1] == `"path"` { + err.Detail = err.Detail + ` Use the "path" attribute in a backend block instead.` + } + } + } + return diags +} + // filterValidErrors ignores certain schema related errors due to their specific non hcl conform implementation. // Related attributes and blocks will be logic checked with LoadConfig. // TODO: Improve checkObjectFields to remove this filter requirement. E.g. optional label struct tag diff --git a/config/configload/validate_test.go b/config/configload/validate_test.go index f5fda3cf5..c3f95ddf5 100644 --- a/config/configload/validate_test.go +++ b/config/configload/validate_test.go @@ -556,3 +556,89 @@ func TestPermissionMixed(t *testing.T) { }) } } + +func TestPathAttr(t *testing.T) { + tests := []struct { + name string + hcl string + error string + }{ + { + "path in endpoint: error", + `server { + endpoint "/**" { + path = "/a/**" + } +}`, + "couper.hcl:3,5-9: Unsupported argument; An argument named \"path\" is not expected here. Use the \"path\" attribute in a backend block instead.", + }, + { + "path in proxy: error", + `server { + endpoint "/**" { + proxy { + path = "/a/**" + } + } +}`, + "couper.hcl:4,7-11: Unsupported argument; An argument named \"path\" is not expected here. Use the \"path\" attribute in a backend block instead.", + }, + { + "path in referenced backend: ok", + `server { + endpoint "/**" { + proxy { + backend = "a" + } + } +} +definitions { + backend "a" { + path = "/a/**" + } +}`, + "", + }, + { + "path in refined backend: ok", + `server { + endpoint "/**" { + proxy { + backend "a" { + path = "/a/**" + } + } + } +} +definitions { + backend "a" { + } +}`, + "", + }, + } + + logger, _ := logrustest.NewNullLogger() + log := logger.WithContext(context.TODO()) + + for _, tt := range tests { + t.Run(tt.name, func(subT *testing.T) { + conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl") + if conf != nil { + tmpStoreCh := make(chan struct{}) + defer close(tmpStoreCh) + + _, err = runtime.NewServerConfiguration(conf, log, cache.New(log, tmpStoreCh)) + } + + var errMsg string + if err != nil { + errMsg = err.Error() + } + + if tt.error != errMsg { + subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot: %q", tt.name, tt.error, errMsg) + } + }) + } +} From 78e0d84ec3d557d107926a925425841fb2c12f15 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Thu, 2 Jun 2022 14:47:12 +0200 Subject: [PATCH 4/9] changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6746c95b4..8e633517d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Unreleased changes are available as `avenga/couper:edge` container. * **Removed** * support for `beta_oidc` block (use [`oidc` block](./docs/REFERENCE.md#oidc-block) instead) ([#475](https://github.com/avenga/couper/pull/475)) * support for `beta_oauth_authorization_url` and `beta_oauth_verifier` functions (use `oauth2_authorization_url` and `oauth2_verifier` [functions](./docs/REFERENCE.md#functions) instead) ([#475](https://github.com/avenga/couper/pull/475)) + * `path` attribute from `endpoint` block; use `path` attribute in `backend` block instead ([#516](https://github.com/avenga/couper/pull/516)) --- From eea3429f120ce0be6d339f5155da29380d0016c9 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Fri, 3 Jun 2022 09:20:50 +0200 Subject: [PATCH 5/9] modify examples: move path attribute to backend, use url with wildcard --- docs/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/README.md b/docs/README.md index 717fc315d..e5f10c7eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -222,11 +222,11 @@ Couper's main concept is exposing APIs via the configuration block `endpoint`, f server "example"{ endpoint "/public/**"{ - path = "/**" proxy { backend { origin = "https://httpbin.org" + path = "/**" } } } @@ -247,11 +247,11 @@ server "example" { endpoint "/private/**" { access_control = ["accessToken"] - path = "/**" proxy { backend { origin = "https://httpbin.org" + path = "/**" } } } @@ -283,9 +283,8 @@ api "my_api" { } endpoint "/cart/**" { - path = "/api/v1/**" proxy { - url = "http://cartservice:8080" + url = "http://cartservice:8080/**" } } From 356648566ddc654775bfa3081a668aca5580fdf8 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Fri, 3 Jun 2022 09:38:07 +0200 Subject: [PATCH 6/9] remove sentence containing path attribute from intro text --- docs/REFERENCE.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md index 00c197492..2f14c542a 100644 --- a/docs/REFERENCE.md +++ b/docs/REFERENCE.md @@ -127,9 +127,7 @@ as JSON error with an error body payload. This can be customized via `error_file ### Endpoint Block `endpoint` blocks define the entry points of Couper. The required _label_ -defines the path suffix for the incoming client request. The `path` attribute -changes the path for the outgoing request (compare -[path mapping example](README.md#routing-path-mapping)). Each `endpoint` block must +defines the path suffix for the incoming client request. Each `endpoint` block must produce an explicit or implicit client response. | Block name | Context | Label | Nested block(s) | From 73ce1db05f08df45e303d58d66718e064699762c Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Fri, 3 Jun 2022 10:01:40 +0200 Subject: [PATCH 7/9] remove unnecessary test code --- config/configload/validate_test.go | 33 +++--------------------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/config/configload/validate_test.go b/config/configload/validate_test.go index c3f95ddf5..b74301768 100644 --- a/config/configload/validate_test.go +++ b/config/configload/validate_test.go @@ -418,18 +418,9 @@ defaults { }, } - logger, _ := logrustest.NewNullLogger() - log := logger.WithContext(context.TODO()) - for _, tt := range tests { t.Run(tt.name, func(subT *testing.T) { - conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl") - if conf != nil { - tmpStoreCh := make(chan struct{}) - defer close(tmpStoreCh) - - _, err = runtime.NewServerConfiguration(conf, log, cache.New(log, tmpStoreCh)) - } + _, err := LoadBytes([]byte(tt.hcl), "couper.hcl") var errMsg string if err != nil { @@ -531,18 +522,9 @@ func TestPermissionMixed(t *testing.T) { }, } - logger, _ := logrustest.NewNullLogger() - log := logger.WithContext(context.TODO()) - for _, tt := range tests { t.Run(tt.name, func(subT *testing.T) { - conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl") - if conf != nil { - tmpStoreCh := make(chan struct{}) - defer close(tmpStoreCh) - - _, err = runtime.NewServerConfiguration(conf, log, cache.New(log, tmpStoreCh)) - } + _, err := LoadBytes([]byte(tt.hcl), "couper.hcl") var errMsg string if err != nil { @@ -618,18 +600,9 @@ definitions { }, } - logger, _ := logrustest.NewNullLogger() - log := logger.WithContext(context.TODO()) - for _, tt := range tests { t.Run(tt.name, func(subT *testing.T) { - conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl") - if conf != nil { - tmpStoreCh := make(chan struct{}) - defer close(tmpStoreCh) - - _, err = runtime.NewServerConfiguration(conf, log, cache.New(log, tmpStoreCh)) - } + _, err := LoadBytes([]byte(tt.hcl), "couper.hcl") var errMsg string if err != nil { From 1e8fd305f1b7118931e65b1c874af1441c679584 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Fri, 3 Jun 2022 10:42:17 +0200 Subject: [PATCH 8/9] path prefix --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index e5f10c7eb..9cec8e6b6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -284,7 +284,7 @@ api "my_api" { endpoint "/cart/**" { proxy { - url = "http://cartservice:8080/**" + url = "http://cartservice:8080/api/v1/**" } } From d103fd6d41909256397458ddef44d4e2efb459b5 Mon Sep 17 00:00:00 2001 From: Johannes Koch Date: Fri, 3 Jun 2022 11:09:46 +0200 Subject: [PATCH 9/9] added proxy to changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e633517d..212c27aef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ Unreleased changes are available as `avenga/couper:edge` container. * **Removed** * support for `beta_oidc` block (use [`oidc` block](./docs/REFERENCE.md#oidc-block) instead) ([#475](https://github.com/avenga/couper/pull/475)) * support for `beta_oauth_authorization_url` and `beta_oauth_verifier` functions (use `oauth2_authorization_url` and `oauth2_verifier` [functions](./docs/REFERENCE.md#functions) instead) ([#475](https://github.com/avenga/couper/pull/475)) - * `path` attribute from `endpoint` block; use `path` attribute in `backend` block instead ([#516](https://github.com/avenga/couper/pull/516)) + * `path` attribute from `endpoint` (and `proxy`) block; use `path` attribute in `backend` block instead ([#516](https://github.com/avenga/couper/pull/516)) ---