feat: add an annotation for ignoring URI case in HTTP match (#174)

Signed-off-by: charlie <qianglin98@qq.com>
This commit is contained in:
charlie
2023-02-14 19:52:17 +08:00
committed by GitHub
parent 6efa393e7d
commit e0807dce0b
6 changed files with 306 additions and 0 deletions

View File

@@ -63,6 +63,8 @@ type Ingress struct {
Auth *AuthConfig
Destination *DestinationConfig
IgnoreCase *IgnoreCaseConfig
}
func (i *Ingress) NeedRegexMatch() bool {
@@ -132,6 +134,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
fallback{},
auth{},
destination{},
ignoreCaseMatching{},
},
gatewayHandlers: []GatewayHandler{
downstreamTLS{},
@@ -146,6 +149,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
ipAccessControl{},
retry{},
fallback{},
ignoreCaseMatching{},
},
trafficPolicyHandlers: []TrafficPolicyHandler{
upstreamTLS{},

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package annotations
import (
networking "istio.io/api/networking/v1alpha3"
)
const (
enableIgnoreCase = "ignore-path-case"
)
type IgnoreCaseConfig struct {
IgnoreUriCase bool
}
type ignoreCaseMatching struct{}
func (m ignoreCaseMatching) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
if config == nil || config.IgnoreCase == nil || !config.IgnoreCase.IgnoreUriCase {
return
}
for _, v := range route.Match {
v.IgnoreUriCase = true
}
}
func (m ignoreCaseMatching) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error {
if !needIgnoreCaseMatch(annotations) {
return nil
}
config.IgnoreCase = &IgnoreCaseConfig{}
config.IgnoreCase.IgnoreUriCase, _ = annotations.ParseBoolASAP(enableIgnoreCase)
return nil
}
func needIgnoreCaseMatch(annotation Annotations) bool {
return annotation.HasASAP(enableIgnoreCase)
}

View File

@@ -0,0 +1,135 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package annotations
import (
"testing"
"github.com/google/go-cmp/cmp"
networking "istio.io/api/networking/v1alpha3"
)
func TestIgnoreCaseMatching_ApplyRoute(t *testing.T) {
handler := ignoreCaseMatching{}
testCases := []struct {
input *networking.HTTPRoute
config *Ingress
expect *networking.HTTPRoute
}{
{
input: &networking.HTTPRoute{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/abc"},
},
IgnoreUriCase: false,
},
},
},
config: &Ingress{
IgnoreCase: &IgnoreCaseConfig{IgnoreUriCase: true},
},
expect: &networking.HTTPRoute{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/abc"},
},
IgnoreUriCase: true,
},
},
},
},
{
input: &networking.HTTPRoute{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/abc"},
},
IgnoreUriCase: false,
},
},
},
config: &Ingress{
IgnoreCase: &IgnoreCaseConfig{IgnoreUriCase: false},
},
expect: &networking.HTTPRoute{
Match: []*networking.HTTPMatchRequest{
{
Uri: &networking.StringMatch{
MatchType: &networking.StringMatch_Exact{Exact: "/abc"},
},
IgnoreUriCase: false,
},
},
},
},
}
t.Run("", func(t *testing.T) {
for _, tt := range testCases {
handler.ApplyRoute(tt.input, tt.config)
if diff := cmp.Diff(tt.expect, tt.input); diff != "" {
t.Fatalf("TestIgnoreCaseMatching_ApplyRoute() mismatch(-want +got): \n%s", diff)
}
}
})
}
func TestIgnoreCaseMatching_Parse(t *testing.T) {
parser := ignoreCaseMatching{}
testCases := []struct {
input Annotations
expect *IgnoreCaseConfig
}{
{
input: Annotations{},
expect: nil,
},
{
input: Annotations{
buildHigressAnnotationKey(enableIgnoreCase): "true",
},
expect: &IgnoreCaseConfig{
IgnoreUriCase: true,
},
},
{
input: Annotations{
buildHigressAnnotationKey(enableIgnoreCase): "false",
},
expect: &IgnoreCaseConfig{
IgnoreUriCase: false,
},
},
}
t.Run("", func(t *testing.T) {
for _, tt := range testCases {
config := &Ingress{}
_ = parser.Parse(tt.input, config, nil)
if diff := cmp.Diff(tt.expect, config.IgnoreCase); diff != "" {
t.Fatalf("TestIgnoreCaseMatching_Parse() mismatch(-want +got): \n%s", diff)
}
}
})
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tests
import (
"testing"
"github.com/alibaba/higress/test/ingress/conformance/utils/http"
"github.com/alibaba/higress/test/ingress/conformance/utils/suite"
)
func init() {
HigressConformanceTests = append(HigressConformanceTests, HTTPRouteIgnoreCaseMatch)
}
var HTTPRouteIgnoreCaseMatch = suite.ConformanceTest{
ShortName: "HTTPRouteIgnoreCaseMatch",
Description: "A Ingress in the higress-conformance-infra namespace that ignores URI case in HTTP match",
Manifests: []string{"tests/httproute-ignore-case-match.yaml"},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
testcases := []http.Assertion{
{
Meta: http.AssertionMeta{
TestCaseName: "case1: normal request",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/foo",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
}, {
Meta: http.AssertionMeta{
TestCaseName: "case2: enable ignoreCase",
TargetBackend: "infra-backend-v1",
TargetNamespace: "higress-conformance-infra",
},
Request: http.AssertionRequest{
ActualRequest: http.Request{
Path: "/fOO",
Host: "foo.com",
},
},
Response: http.AssertionResponse{
ExpectedResponse: http.Response{
StatusCode: 200,
},
},
},
}
t.Run("Enable IgnoreCase Cases Split", func(t *testing.T) {
for _, testcase := range testcases {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, suite.GatewayAddress, testcase)
}
})
},
}

View File

@@ -0,0 +1,34 @@
# Copyright (c) 2022 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
higress.io/ignore-path-case: "true"
name: httproute-ignore-case-match
namespace: higress-conformance-infra
spec:
ingressClassName: higress
rules:
- host: "foo.com"
http:
paths:
- pathType: Exact
path: "/foo"
backend:
service:
name: infra-backend-v1
port:
number: 8080

View File

@@ -56,6 +56,7 @@ func TestHigressConformanceTests(t *testing.T) {
tests.HTTPRouteRewriteHost,
tests.HTTPRouteCanaryHeader,
tests.HTTPRouteEnableCors,
tests.HTTPRouteIgnoreCaseMatch,
}
cSuite.Run(t, higressTests)