Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions gateway/build-manifest.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
version: v1
policies:
- name: token-based-ratelimit
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/token-based-ratelimit@v1
- name: advanced-ratelimit
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/advanced-ratelimit@v1
Expand Down Expand Up @@ -33,12 +30,24 @@ policies:
- name: dynamic-endpoint
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/dynamic-endpoint@v1
- name: host-rewrite
version: v1.0.0
gomodule: github.com/wso2/gateway-controllers/policies/host-rewrite@v1
- name: json-schema-guardrail
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/json-schema-guardrail@v1
- name: json-xml-mediator
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/json-xml-mediator@v1
- name: jwt-auth
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/jwt-auth@v1
- name: llm-cost
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost@v1
- name: llm-cost-based-ratelimit
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost-based-ratelimit@v1
- name: log-message
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/log-message@v1
Expand Down Expand Up @@ -87,30 +96,24 @@ policies:
- name: semantic-prompt-guard
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/semantic-prompt-guard@v1
- name: semantic-tool-filtering
version: v1.0.2
gomodule: github.com/wso2/gateway-controllers/policies/semantic-tool-filtering@v1
- name: sentence-count-guardrail
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/sentence-count-guardrail@v1
- name: set-headers
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/set-headers@v1
- name: subscription-validation
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/subscription-validation@v1
- name: token-based-ratelimit
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/token-based-ratelimit@v1
- name: url-guardrail
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/url-guardrail@v1
- name: word-count-guardrail
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/word-count-guardrail@v1
- name: json-xml-mediator
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/json-xml-mediator@v1
- name: subscription-validation
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/subscription-validation@v1
- name: llm-cost
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost@v1
- name: llm-cost-based-ratelimit
version: v1.0.1
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost-based-ratelimit@v1
- name: semantic-tool-filtering
version: v1.0.2
gomodule: github.com/wso2/gateway-controllers/policies/semantic-tool-filtering@v1
26 changes: 14 additions & 12 deletions gateway/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ version: v1
gateway:
version: 1.0.0-rc.2-SNAPSHOT
policies:
- name: token-based-ratelimit
gomodule: github.com/wso2/gateway-controllers/policies/token-based-ratelimit@v1
- name: advanced-ratelimit
gomodule: github.com/wso2/gateway-controllers/policies/advanced-ratelimit@v1
- name: analytics-header-filter
Expand All @@ -24,10 +22,18 @@ policies:
gomodule: github.com/wso2/gateway-controllers/policies/cors@v1
- name: dynamic-endpoint
gomodule: github.com/wso2/gateway-controllers/policies/dynamic-endpoint@v1
- name: host-rewrite
gomodule: github.com/wso2/gateway-controllers/policies/host-rewrite@v1
- name: json-schema-guardrail
gomodule: github.com/wso2/gateway-controllers/policies/json-schema-guardrail@v1
- name: json-xml-mediator
gomodule: github.com/wso2/gateway-controllers/policies/json-xml-mediator@v1
- name: jwt-auth
gomodule: github.com/wso2/gateway-controllers/policies/jwt-auth@v1
- name: llm-cost
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost@v1
- name: llm-cost-based-ratelimit
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost-based-ratelimit@v1
- name: log-message
gomodule: github.com/wso2/gateway-controllers/policies/log-message@v1
- name: mcp-acl-list
Expand Down Expand Up @@ -60,21 +66,17 @@ policies:
gomodule: github.com/wso2/gateway-controllers/policies/semantic-cache@v1
- name: semantic-prompt-guard
gomodule: github.com/wso2/gateway-controllers/policies/semantic-prompt-guard@v1
- name: semantic-tool-filtering
gomodule: github.com/wso2/gateway-controllers/policies/semantic-tool-filtering@v1
- name: sentence-count-guardrail
gomodule: github.com/wso2/gateway-controllers/policies/sentence-count-guardrail@v1
- name: set-headers
gomodule: github.com/wso2/gateway-controllers/policies/set-headers@v1
- name: subscription-validation
gomodule: github.com/wso2/gateway-controllers/policies/subscription-validation@v1
- name: token-based-ratelimit
gomodule: github.com/wso2/gateway-controllers/policies/token-based-ratelimit@v1
- name: url-guardrail
gomodule: github.com/wso2/gateway-controllers/policies/url-guardrail@v1
- name: word-count-guardrail
gomodule: github.com/wso2/gateway-controllers/policies/word-count-guardrail@v1
- name: json-xml-mediator
gomodule: github.com/wso2/gateway-controllers/policies/json-xml-mediator@v1
- name: subscription-validation
gomodule: github.com/wso2/gateway-controllers/policies/subscription-validation@v1
- name: llm-cost
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost@v1
- name: llm-cost-based-ratelimit
gomodule: github.com/wso2/gateway-controllers/policies/llm-cost-based-ratelimit@v1
- name: semantic-tool-filtering
gomodule: github.com/wso2/gateway-controllers/policies/semantic-tool-filtering@v1
2 changes: 1 addition & 1 deletion gateway/gateway-builder/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.26.1

require (
github.com/stretchr/testify v1.11.1
github.com/wso2/api-platform/sdk/core v0.2.7
github.com/wso2/api-platform/sdk/core v0.2.9
golang.org/x/mod v0.32.0
gopkg.in/yaml.v3 v3.0.1
)
Expand Down
4 changes: 2 additions & 2 deletions gateway/gateway-builder/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/wso2/api-platform/sdk/core v0.2.7 h1:Y4L3ZS3aemgjRkNVFr+WqumjLOnc8ot+2L3SAelnLk8=
github.com/wso2/api-platform/sdk/core v0.2.7/go.mod h1:vgNVzR16g9k5cun3VXZ7wDg8UGbPxsVU2TW8EbRCv0o=
github.com/wso2/api-platform/sdk/core v0.2.9 h1:3lvAsMlLhy8nNgPL24/UFS/f4sq5e+XpryA4L1PO7dU=
github.com/wso2/api-platform/sdk/core v0.2.9/go.mod h1:vgNVzR16g9k5cun3VXZ7wDg8UGbPxsVU2TW8EbRCv0o=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
21 changes: 21 additions & 0 deletions gateway/gateway-controller/default-policies/host-rewrite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: host-rewrite
version: v1.0.0
description: |
Rewrite the Host header of incoming requests before forwarding to the upstream.

parameters:
type: object
properties:
host:
type: string
x-wso2-policy-advanced-param: false
description: The new Host header value to set for the request.
minLength: 1
maxLength: 255
pattern: "^[a-zA-Z0-9.-]+$"
required:
- host
systemParameters:
type: object
additionalProperties: false
properties: {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: model-round-robin
version: v1.0.1
version: v1.0.2
description: |
Distributes requests across configured AI models in round-robin order to
balance traffic and reduce overloading on any single model.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: model-weighted-round-robin
version: v1.0.1
version: v1.0.2
description: |
Distribute requests across models using weighted round-robin so higher-weight models receive proportionally more traffic.

Expand Down
2 changes: 1 addition & 1 deletion gateway/gateway-controller/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/prometheus/client_model v0.6.2
github.com/stretchr/testify v1.11.1
github.com/wso2/api-platform/common v0.0.0
github.com/wso2/api-platform/sdk/core v0.2.7
github.com/wso2/api-platform/sdk/core v0.2.9
github.com/xeipuuv/gojsonschema v1.2.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
Expand Down
4 changes: 2 additions & 2 deletions gateway/gateway-controller/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
github.com/wso2/api-platform/sdk/core v0.2.7 h1:Y4L3ZS3aemgjRkNVFr+WqumjLOnc8ot+2L3SAelnLk8=
github.com/wso2/api-platform/sdk/core v0.2.7/go.mod h1:vgNVzR16g9k5cun3VXZ7wDg8UGbPxsVU2TW8EbRCv0o=
github.com/wso2/api-platform/sdk/core v0.2.9 h1:3lvAsMlLhy8nNgPL24/UFS/f4sq5e+XpryA4L1PO7dU=
github.com/wso2/api-platform/sdk/core v0.2.9/go.mod h1:vgNVzR16g9k5cun3VXZ7wDg8UGbPxsVU2TW8EbRCv0o=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
Expand Down
63 changes: 47 additions & 16 deletions gateway/gateway-controller/lua/request_transformation.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Request transformation Lua filter for HTTP method and path rewriting.
-- Request transformation Lua filter for HTTP method, path, and host rewriting.
-- Reads target values from dynamic metadata and replaces the corresponding headers.
-- This filter runs AFTER routing, so path rewrites here do NOT trigger route re-evaluation.
-- For path rewrites, Lua strips the API context and prepends the upstream base path since
Expand Down Expand Up @@ -50,6 +50,30 @@ local function resolve_target_method(metadata)
return nil
end

local function resolve_target_host(metadata)
if metadata == nil then
return nil
end

-- New SDK format: mods.Host sets this
local host_key = metadata["host"]
if host_key ~= nil then
return host_key
end

local direct = metadata["request_transformation.target_host"]
if direct ~= nil then
return direct
end

local nested = metadata["request_transformation"]
if type(nested) == "table" then
return nested["target_host"]
end

return nil
end

local function resolve_target_path(metadata)
if metadata == nil then
return nil
Expand Down Expand Up @@ -167,8 +191,8 @@ function envoy_on_request(handle)
end
end

handle:logInfo("path_rewrite: target_path=" .. tostring(target_path) ..
" api_context=" .. tostring(api_context) ..
handle:logInfo("path_rewrite: target_path=" .. tostring(target_path) ..
" api_context=" .. tostring(api_context) ..
" upstream_base_path=" .. tostring(upstream_base_path))

-- Compute the final upstream path
Expand All @@ -179,22 +203,29 @@ function envoy_on_request(handle)
end
end

-- Handle host/authority rewriting
local target_host = resolve_target_host(extproc_metadata)
if target_host ~= nil and type(target_host) == "string" and target_host ~= "" then
handle:logInfo("host_rewrite: target_host=" .. tostring(target_host))
-- Replace both :authority (HTTP/2) and Host (HTTP/1) for compatibility
handle:headers():replace(":authority", target_host)
handle:headers():replace("host", target_host)
end

-- Handle HTTP method rewriting
local target_method = resolve_target_method(extproc_metadata)
if target_method == nil then
return
end
if target_method ~= nil then
local normalized = normalize_method(target_method)
if normalized == nil then
handle:logWarn("request transformation: target_method is not a string")
return
end

local normalized = normalize_method(target_method)
if normalized == nil then
handle:logWarn("request transformation: target_method is not a string")
return
end
if not allowed_methods[normalized] then
handle:logWarn("request transformation: invalid target_method: " .. tostring(target_method))
return
end

if not allowed_methods[normalized] then
handle:logWarn("request transformation: invalid target_method: " .. tostring(target_method))
return
handle:headers():replace(":method", normalized)
end

handle:headers():replace(":method", normalized)
end
7 changes: 2 additions & 5 deletions gateway/gateway-controller/pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,8 @@ const (
" headers:\n" +
" - name: '%s'\n" +
" value: '%s'\n"
PROXY_HOST__HEADER_POLICY_NAME = "set-headers"
PROXY_HOST__HEADER_POLICY_PARAMS = "request:\n" +
" headers:\n" +
" - name: Host\n" +
" value: '%s'\n"
PROXY_HOST__HEADER_POLICY_NAME = "host-rewrite"
PROXY_HOST__HEADER_POLICY_PARAMS = "host: '%s'\n"

ACCESS_CONTROL_DENY_POLICY_NAME = "respond"
// YAML for default 404 respond policy params
Expand Down
3 changes: 2 additions & 1 deletion gateway/gateway-controller/pkg/utils/llm_transformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,10 +679,11 @@ func GetUpstreamAuthApikeyPolicyParams(header, value string) (map[string]interfa
return m, nil
}

// GetHostAdditionPolicyParams renders the policy params with given header and value
// GetHostAdditionPolicyParams renders the policy params with given host value (host-rewrite)
func GetHostAdditionPolicyParams(value string) (map[string]interface{}, error) {
rendered := fmt.Sprintf(constants.PROXY_HOST__HEADER_POLICY_PARAMS, value)
var m map[string]interface{}
// For host-rewrite, params are simple mapping, so unmarshal into map
if err := yaml.Unmarshal([]byte(rendered), &m); err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ func TestGetHostAdditionPolicyParams(t *testing.T) {
params, err := GetHostAdditionPolicyParams("api.example.com")
assert.NoError(t, err)
assert.NotNil(t, params)
assert.Contains(t, params, "request")
assert.Contains(t, params, "host")
assert.Equal(t, "api.example.com", params["host"])
})

t.Run("Empty host value", func(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion gateway/gateway-controller/pkg/xds/translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2983,7 +2983,8 @@ func (t *Translator) createExtProcFilter() (*hcm.HttpFilter, error) {
},
MessageTimeout: durationpb.New(time.Duration(policyEngine.MessageTimeoutMs) * time.Millisecond),
MutationRules: &mutationrules.HeaderMutationRules{
AllowAllRouting: wrapperspb.Bool(true),
DisallowSystem: wrapperspb.Bool(true),
DisallowIsError: wrapperspb.Bool(true),
},
MetadataOptions: &extproc.MetadataOptions{
ReceivingNamespaces: &extproc.MetadataOptions_MetadataNamespaces{
Expand Down
2 changes: 1 addition & 1 deletion gateway/gateway-runtime/policy-engine/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/prometheus/client_golang v1.23.2
github.com/stretchr/testify v1.11.1
github.com/wso2/api-platform/common v0.0.0-20260326194347-3d85c50eae71
github.com/wso2/api-platform/sdk/core v0.2.7
github.com/wso2/api-platform/sdk/core v0.2.9
go.opentelemetry.io/otel v1.40.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0
go.opentelemetry.io/otel/sdk v1.40.0
Expand Down
4 changes: 2 additions & 2 deletions gateway/gateway-runtime/policy-engine/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/wso2/api-platform/sdk/core v0.2.7 h1:Y4L3ZS3aemgjRkNVFr+WqumjLOnc8ot+2L3SAelnLk8=
github.com/wso2/api-platform/sdk/core v0.2.7/go.mod h1:vgNVzR16g9k5cun3VXZ7wDg8UGbPxsVU2TW8EbRCv0o=
github.com/wso2/api-platform/sdk/core v0.2.9 h1:3lvAsMlLhy8nNgPL24/UFS/f4sq5e+XpryA4L1PO7dU=
github.com/wso2/api-platform/sdk/core v0.2.9/go.mod h1:vgNVzR16g9k5cun3VXZ7wDg8UGbPxsVU2TW8EbRCv0o=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ func (s *ExternalProcessorServer) skipAllProcessing(routeMetadata RouteMetadata)
}

// Build dynamic metadata structure
dynamicMetadata := buildDynamicMetadata(analyticsStruct, nil, nil, nil)
dynamicMetadata := buildDynamicMetadata(analyticsStruct, nil, nil)

return &extprocv3.ProcessingResponse{
Response: &extprocv3.ProcessingResponse_RequestHeaders{
Expand Down
Loading
Loading