diff --git a/adapter/internal/oasparser/envoyconf/routes_configs.go b/adapter/internal/oasparser/envoyconf/routes_configs.go index dbafbec47..585ab47ba 100644 --- a/adapter/internal/oasparser/envoyconf/routes_configs.go +++ b/adapter/internal/oasparser/envoyconf/routes_configs.go @@ -128,7 +128,7 @@ func generateRouteAction(apiType string, routeConfig *model.EndpointConfig, rate return action } -func generateRequestRedirectRoute(route string, policyParams interface{}) (action *routev3.Route_Redirect) { +func generateRequestRedirectRoute(route string, policyParams interface{}) (*routev3.Route_Redirect, error) { policyParameters, _ := policyParams.(map[string]interface{}) scheme, _ := policyParameters[constants.RedirectScheme].(string) hostname, _ := policyParameters[constants.RedirectHostname].(string) @@ -137,10 +137,10 @@ func generateRequestRedirectRoute(route string, policyParams interface{}) (actio replaceFullPath, _ := policyParameters[constants.RedirectPath].(string) redirectActionStatusCode := mapStatusCodeToEnum(statusCode) if redirectActionStatusCode == -1 { - _ = fmt.Errorf("Invalid status code provided") + return nil, fmt.Errorf("Invalid status code provided") } - action = &routev3.Route_Redirect{ + action := &routev3.Route_Redirect{ Redirect: &routev3.RedirectAction{ SchemeRewriteSpecifier: &routev3.RedirectAction_HttpsRedirect{ HttpsRedirect: scheme == "https", @@ -153,7 +153,7 @@ func generateRequestRedirectRoute(route string, policyParams interface{}) (actio ResponseCode: routev3.RedirectAction_RedirectResponseCode(redirectActionStatusCode), }, } - return action + return action, nil } func mapStatusCodeToEnum(statusCode int) int { @@ -162,12 +162,6 @@ func mapStatusCodeToEnum(statusCode int) int { return 0 case 302: return 1 - case 303: - return 2 - case 307: - return 3 - case 308: - return 4 default: return -1 } diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index 869246b19..17ab9f110 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -998,7 +998,10 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error case constants.ActionRedirectRequest: logger.LoggerOasparser.Debugf("Adding %s policy to request flow for %s %s", constants.ActionRedirectRequest, resourcePath, operation.GetMethod()) - requestRedirectAction = generateRequestRedirectRoute(resourcePath, requestPolicy.Parameters) + requestRedirectAction, err = generateRequestRedirectRoute(resourcePath, requestPolicy.Parameters) + if err != nil { + return nil, err + } } } diff --git a/runtime/config-deployer-service/ballerina/APIClient.bal b/runtime/config-deployer-service/ballerina/APIClient.bal index 7c919c728..237a433b3 100644 --- a/runtime/config-deployer-service/ballerina/APIClient.bal +++ b/runtime/config-deployer-service/ballerina/APIClient.bal @@ -860,10 +860,7 @@ public class APIClient { model:HTTPHeader[] setHeaders = []; string[] removeHeaders = []; boolean hasRedirectPolicy = false; - model:HTTPRouteFilter headerModifierFilter = {'type: "RequestHeaderModifier"}; - if !isRequest { - headerModifierFilter.'type = "ResponseHeaderModifier"; - } + foreach APKOperationPolicy policy in operationPolicies { if policy is HeaderModifierPolicy { HeaderModifierPolicyParameters policyParameters = policy.parameters; @@ -871,19 +868,19 @@ public class APIClient { AddHeaders => { ModifierHeader[] headers = policyParameters.headers; foreach ModifierHeader header in headers { - headers.push(header); + addHeaders.push(header); } } SetHeaders => { ModifierHeader[] headers = policyParameters.headers; foreach ModifierHeader header in headers { - headers.push(header); + setHeaders.push(header); } } RemoveHeaders => { string[] headers = policyParameters.headers; foreach string header in headers { - headers.push(header); + removeHeaders.push(header); } } } @@ -939,35 +936,64 @@ public class APIClient { } RequestRedirectPolicyParameters policyParameters = policy.parameters; string url = policyParameters.url; - int statusCode = policyParameters.statusCode; model:HTTPRouteFilter redirectFilter = {'type: "RequestRedirect"}; int|error port = self.getPort(url); + if port is int { redirectFilter.requestRedirect = { hostname: self.getHost(url), scheme: self.getProtocol(url), - statusCode: statusCode, path: { 'type: "ReplaceFullPath", replaceFullPath: self.getPath(url) } }; + if policyParameters.statusCode is int { + int statusCode = policyParameters.statusCode; + redirectFilter.requestRedirect.statusCode = statusCode; + } } + httpRouteFilters.push(redirectFilter); } } + if isRequest { + model:HTTPHeaderFilter requestHeaderModifier = {}; + if addHeaders != [] { + requestHeaderModifier.add = addHeaders; + } + if setHeaders != [] { + requestHeaderModifier.set = setHeaders; + } + if removeHeaders != [] { + requestHeaderModifier.remove = removeHeaders; + } - if addHeaders != [] { - headerModifierFilter.requestHeaderModifier.add = addHeaders; - } - if setHeaders != [] { - headerModifierFilter.requestHeaderModifier.set = setHeaders; - } - if removeHeaders != [] { - headerModifierFilter.requestHeaderModifier.remove = removeHeaders; - } - if addHeaders.length() > 0 || setHeaders.length() > 0 || removeHeaders.length() > 0 { - httpRouteFilters.push(headerModifierFilter); + if addHeaders.length() > 0 || setHeaders.length() > 0 || removeHeaders.length() > 0 { + model:HTTPRouteFilter headerModifierFilter = { + 'type: "RequestHeaderModifier", + requestHeaderModifier: requestHeaderModifier + }; + httpRouteFilters.push(headerModifierFilter); + } + } else { + model:HTTPHeaderFilter responseHeaderModifier = {}; + if addHeaders != [] { + responseHeaderModifier.add = addHeaders; + } + if setHeaders != [] { + responseHeaderModifier.set = setHeaders; + } + if removeHeaders != [] { + responseHeaderModifier.remove = removeHeaders; + } + if addHeaders.length() > 0 || setHeaders.length() > 0 || removeHeaders.length() > 0 { + model:HTTPRouteFilter headerModifierFilter = { + 'type: "ResponseHeaderModifier", + responseHeaderModifier: responseHeaderModifier + }; + httpRouteFilters.push(headerModifierFilter); + } } return [httpRouteFilters, hasRedirectPolicy]; diff --git a/runtime/config-deployer-service/ballerina/resources/apk-conf-schema.yaml b/runtime/config-deployer-service/ballerina/resources/apk-conf-schema.yaml index 2adc6185e..6285131a6 100644 --- a/runtime/config-deployer-service/ballerina/resources/apk-conf-schema.yaml +++ b/runtime/config-deployer-service/ballerina/resources/apk-conf-schema.yaml @@ -268,7 +268,7 @@ components: SetHeaders: "#/components/schemas/HeaderModifierPolicy" RemoveHeaders: "#/components/schemas/HeaderModifierPolicy" RequestMirror: "#/components/schemas/RequestMirrorPolicy" - RequstRedirect: "#/components/schemas/RequestRedirectPolicy" + RequestRedirect: "#/components/schemas/RequestRedirectPolicy" BaseOperationPolicy: title: API Operation Policy required: @@ -544,13 +544,10 @@ components: statusCode: type: integer description: The status code to show upon redirecting the request. - default: 301 + default: 302 enum: - 301 - 302 - - 303 - - 307 - - 308 additionalProperties: false CustomClaims: type: object diff --git a/runtime/config-deployer-service/ballerina/types.bal b/runtime/config-deployer-service/ballerina/types.bal index eecc9c4e9..edd5e4362 100644 --- a/runtime/config-deployer-service/ballerina/types.bal +++ b/runtime/config-deployer-service/ballerina/types.bal @@ -182,7 +182,7 @@ public type RequestRedirectPolicy record { # + statusCode - The status code to be sent as response to the client. public type RequestRedirectPolicyParameters record {| string url; - int statusCode; + int statusCode?; |}; # Configuration for API deployment using the apk-conf file. diff --git a/runtime/config-deployer-service/docker/config-deployer/conf/apk-schema.json b/runtime/config-deployer-service/docker/config-deployer/conf/apk-schema.json index 9f202e8d1..0ad4df6c2 100644 --- a/runtime/config-deployer-service/docker/config-deployer/conf/apk-schema.json +++ b/runtime/config-deployer-service/docker/config-deployer/conf/apk-schema.json @@ -768,13 +768,10 @@ "statusCode": { "type": "integer", "description": "The status code to show upon redirecting the request.", - "default": 301, + "default": 302, "enum": [ 301, - 302, - 303, - 307, - 308 + 302 ] } }, diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleHTTPClient.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleHTTPClient.java index d06cd0705..eb591864b 100644 --- a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleHTTPClient.java +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleHTTPClient.java @@ -26,6 +26,7 @@ import javax.net.ssl.TrustManager; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; @@ -87,7 +88,11 @@ public SimpleHTTPClient() throws NoSuchAlgorithmException, KeyStoreException, Ke .build(); final SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslcontext); + RequestConfig requestConfig = RequestConfig.custom() + .setRedirectsEnabled(false) // Disable redirects + .build(); this.client = HttpClients.custom() + .setDefaultRequestConfig(requestConfig) .setSSLSocketFactory(csf) .evictExpiredConnections() .setMaxConnPerRoute(100) diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request_and_response_filters.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/header-modifier-filter.apk-conf similarity index 94% rename from test/cucumber-tests/src/test/resources/artifacts/apk-confs/request_and_response_filters.apk-conf rename to test/cucumber-tests/src/test/resources/artifacts/apk-confs/header-modifier-filter.apk-conf index a41f67efc..a9d08d90f 100644 --- a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request_and_response_filters.apk-conf +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/header-modifier-filter.apk-conf @@ -1,7 +1,7 @@ --- -id: "api-with-request-and-response-filters" +id: "api-with-header-modifier-filters" name: "EmployeeServiceAPI" -basePath: "/request-and-response-filters" +basePath: "/header-modifier-filters" version: "3.14" type: "REST" defaultVersion: false diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request-mirror-filter.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request-mirror-filter.apk-conf new file mode 100644 index 000000000..46523900a --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request-mirror-filter.apk-conf @@ -0,0 +1,35 @@ +--- +id: "api-with-request-mirror-filter" +name: "EmployeeServiceAPI" +basePath: "/request-mirror-filter" +version: "3.14" +type: "REST" +defaultVersion: false +endpointConfigurations: + production: + endpoint: "http://backend:80/anything" +operations: + - target: "/employee" + verb: "GET" + secured: false + scopes: [] + operationPolicies: + request: + - policyName: RequestMirror + policyVersion: v1 + parameters: + urls: + - "http://backend:80/anything" + - "http://backend:80/anything" + - target: "/employee" + verb: "POST" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "PUT" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "DELETE" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request-redirect-filter.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request-redirect-filter.apk-conf new file mode 100644 index 000000000..c972aeea2 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/request-redirect-filter.apk-conf @@ -0,0 +1,34 @@ +--- +id: "api-with-request-redirect-filter" +name: "EmployeeServiceAPI" +basePath: "/request-redirect-filter" +version: "3.14" +type: "REST" +defaultVersion: false +endpointConfigurations: + production: + endpoint: "http://backend:80/anything" +operations: + - target: "/employee" + verb: "GET" + secured: false + scopes: [] + operationPolicies: + request: + - policyName: RequestRedirect + policyVersion: v1 + parameters: + url: "http://backend:80/anything" + statusCode: 301 + - target: "/employee" + verb: "POST" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "PUT" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "DELETE" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/tests/api/HTTPRouteFilters.feature b/test/cucumber-tests/src/test/resources/tests/api/HeaderModifier.feature similarity index 84% rename from test/cucumber-tests/src/test/resources/tests/api/HTTPRouteFilters.feature rename to test/cucumber-tests/src/test/resources/tests/api/HeaderModifier.feature index e8138c5b5..f6482ceca 100644 --- a/test/cucumber-tests/src/test/resources/tests/api/HTTPRouteFilters.feature +++ b/test/cucumber-tests/src/test/resources/tests/api/HeaderModifier.feature @@ -2,13 +2,13 @@ Feature: Test HTTPRoute Filter Header Modifier functionality Scenario: Test request and response header modification functionality Given The system is ready And I have a valid subscription - When I use the APK Conf file "artifacts/apk-confs/request_and_response_filters.apk-conf" + When I use the APK Conf file "artifacts/apk-confs/header-modifier-filter.apk-conf" And the definition file "artifacts/definitions/employees_api.json" And make the API deployment request Then the response status code should be 200 Then I set headers | Authorization | bearer ${accessToken} | - And I send "GET" request to "https://default.gw.wso2.com:9095/request-and-response-filters/3.14/employee/" with body "" + And I send "GET" request to "https://default.gw.wso2.com:9095/header-modifier-filters/3.14/employee/" with body "" And I eventually receive 200 response code, not accepting | 401 | And the response body should contain "\"Test-Request-Header\": \"Test-Value\"" @@ -22,7 +22,7 @@ Feature: Test HTTPRoute Filter Header Modifier functionality Scenario: Undeploy the API Given The system is ready And I have a valid subscription - When I undeploy the API whose ID is "api-with-request-and-response-filters" + When I undeploy the API whose ID is "api-with-header-modifier-filters" Then the response status code should be 202 \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/tests/api/RequestMirror.feature b/test/cucumber-tests/src/test/resources/tests/api/RequestMirror.feature new file mode 100644 index 000000000..0c4ed706f --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/api/RequestMirror.feature @@ -0,0 +1,21 @@ +Feature: Test HTTPRoute Filter Request Mirror functionality + Scenario: Test request mirror functionality + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/request-mirror-filter.apk-conf" + And the definition file "artifacts/definitions/employees_api.json" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I send "GET" request to "https://default.gw.wso2.com:9095/request-mirror-filter/3.14/employee/" with body "" + And I eventually receive 200 response code, not accepting + | 401 | + + Scenario: Undeploy the API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "api-with-request-mirror-filter" + Then the response status code should be 202 + + \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/tests/api/RequestRedirect.feature b/test/cucumber-tests/src/test/resources/tests/api/RequestRedirect.feature new file mode 100644 index 000000000..85b393f90 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/api/RequestRedirect.feature @@ -0,0 +1,21 @@ +Feature: Test HTTPRoute Filter Request Redirect functionality + Scenario: Test request redirect functionality + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/request-redirect-filter.apk-conf" + And the definition file "artifacts/definitions/employees_api.json" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I send "GET" request to "https://default.gw.wso2.com:9095/request-redirect-filter/3.14/employee/" with body "" + And I eventually receive 301 response code, not accepting + | 401 | + + Scenario: Undeploy the API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "api-with-request-redirect-filter" + Then the response status code should be 202 + + \ No newline at end of file