Skip to content

Commit a2f80bc

Browse files
committed
add GEP-2891: HTTP Cookie Match
1 parent 18ad4ba commit a2f80bc

File tree

2 files changed

+307
-0
lines changed

2 files changed

+307
-0
lines changed

geps/gep-2891/index.md

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# GEP 2891: HTTP Cookie Match
2+
3+
* Issue: [#2891](https://github.com/kubernetes-sigs/gateway-api/issues/2891)
4+
* Status: Experimental
5+
6+
## TLDR
7+
8+
Just like HTTP route based on header and query parameter is common, it’d be helpful to have a `HTTPCookieMatch` field in `HTTPRouteMatch` which would let gateway forwards request based on cookie to the certain backends.
9+
10+
## Goals
11+
12+
* Support cookie matching in a `HTTPRoute`
13+
* Add a new match type `list`
14+
15+
## Introduction
16+
17+
Cookie is an essential part of the HTTP request. The Cookie header has multiple cookie-pair which contains the cookie-name and cookie-value.
18+
```
19+
cookie-header = "Cookie:" OWS cookie-string OWS
20+
cookie-string = cookie-pair *( ";" SP cookie-pair )
21+
cookie-pair = cookie-name "=" cookie-value
22+
cookie-name = token
23+
cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
24+
cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
25+
; US-ASCII characters excluding CTLs,
26+
; whitespace DQUOTE, comma, semicolon,
27+
; and backslash
28+
token = <token, defined in [[RFC2616], Section 2.2](https://www.rfc-editor.org/rfc/rfc2616#section-2.2)>
29+
```
30+
31+
They are used to maintain state and identify specific users. Moreover, cookies, headers and query parameters are common techniques used in a canary release.
32+
Currently `HTTPRouteMatch` API supports the following condition: path, method, header and query parameter. This GEP proposes adding support for cookie matching in a `HTTPRoute`.
33+
34+
## API
35+
36+
This GEP proposes to add a new field `HTTPCookieMatch` to `HTTPRouteMatch`.
37+
Moreover, a new match type `List` is added. Matches if the value of the cookie with name field is present in a list of strings. This match type `List` can be applied to serving `HTTPHeaderMatch` and `HTTPQueryParamMatch` as well.
38+
39+
The `HTTPCookieMatch` and `List` are considered an extended feature.
40+
41+
```golang
42+
// HTTPCookieMatch describes how to select a HTTP route by matching HTTP request
43+
// CookieMatchType specifies the semantics of how HTTP cookie values should be
44+
// compared. Valid CookieMatchType values, along with their conformance levels, are:
45+
//
46+
// * "Exact" - Core
47+
// * "List" - Extended
48+
// * "RegularExpression" - Implementation Specific
49+
//
50+
// * "Exact" matching exact string
51+
// * "List" matching string in a list of strings
52+
//
53+
// Note that values may be added to this enum, implementations
54+
// must ensure that unknown values will not cause a crash.
55+
//
56+
// Unknown values here must result in the implementation setting the
57+
// Accepted Condition for the Route to `status: False`, with a
58+
// Reason of `UnsupportedValue`.
59+
//
60+
// +kubebuilder:validation:Enum=Exact;RegularExpression
61+
type CookieMatchType string
62+
63+
// CookieMatchType constants.
64+
const (
65+
CookieMatchExact CookieMatchType = "Exact"
66+
CookieMatchList CookieMatchType = "List"
67+
CookieMatchRegularExpression CookieMatchType = "RegularExpression"
68+
)
69+
70+
// HTTPCookieMatch describes how to select a HTTP route by matching HTTP request
71+
// cookies.
72+
type HTTPCookieMatch struct {
73+
// Type specifies how to match against the value of the cookie.
74+
//
75+
// Support: Core (Exact)
76+
//
77+
// Support: Extended (List)
78+
//
79+
// Support: Implementation-specific (RegularExpression)
80+
//
81+
// Since RegularExpression CookieMatchType has implementation-specific
82+
// conformance, implementations can support POSIX, PCRE or any other dialects
83+
// of regular expressions. Please read the implementation's documentation to
84+
// determine the supported dialect.
85+
//
86+
// +optional
87+
// +kubebuilder:default=Exact
88+
Type *CookieMatchType `json:"type,omitempty"`
89+
90+
// Name is the cookie-name of the cookie-pair in the HTTP Cookie header to be matched.
91+
// (See https://www.rfc-editor.org/rfc/rfc6265#section-4.2.1)
92+
// cookie-header = "Cookie:" OWS cookie-string OWS
93+
// cookie-string = cookie-pair *( ";" SP cookie-pair )
94+
// (See https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1)
95+
// cookie-pair = cookie-name "=" cookie-value
96+
// cookie-name = token
97+
// token = <token, defined in [RFC2616], Section 2.2>
98+
//
99+
// If multiple entries specify equivalent cookie names, only the first
100+
// entry with an equivalent name MUST be considered for a match. Subsequent
101+
// entries with an equivalent cookie name MUST be ignored. Due to the
102+
// case-insensitivity of cookie names, "foo" and "Foo" are considered
103+
// equivalent.
104+
//
105+
// When a Cookie header is repeated in an HTTP request, it is
106+
// implementation-specific behavior as to how this is represented.
107+
// Generally, proxies should follow the guidance from the RFC:
108+
// https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding
109+
// processing a repeated header.
110+
Name HTTPHeaderName `json:"name"`
111+
112+
// Values is the cookie-value of the cookie-pair in the HTTP Cookie header to be matched.
113+
// Matches if the value of the cookie with name field is present in the HTTP Cookie header.
114+
//
115+
// +kubebuilder:validation:MinLength=1
116+
// +kubebuilder:validation:MaxLength=1024
117+
Value string `json:"value"`
118+
119+
// Values are the cookie-value list of the cookie-pair in the HTTP Cookie header to be matched.
120+
// Matches if the value of the cookie with name field is present in the list.
121+
//
122+
// +kubebuilder:validation:MinLength=1
123+
// +kubebuilder:validation:MaxLength=4096
124+
Values []string `json:"values"`
125+
}
126+
```
127+
128+
## Examples
129+
130+
The following example shows how the HTTPRoute matches request on cookie and forward it to different backend service.
131+
132+
* Backend service wants to select specific users for measuring the effectiveness of advertising campaigns. With the following HTTPRoute, the http requests with cookie name "unb" and cookie value in a list of strings (i.e., 2426168118, 2208203664638, 2797880990, 70772956, 2215140160618) will be routed to the service "http-route-canary-campaign:7001".
133+
134+
```yaml
135+
apiVersion: gateway.networking.k8s.io/v1
136+
kind: HTTPRoute
137+
metadata:
138+
name: http-route-cookie
139+
spec:
140+
hostnames:
141+
- http.route.cookies.com
142+
parentRefs:
143+
- group: gateway.networking.k8s.io
144+
kind: Gateway
145+
name: http-gateway
146+
rules:
147+
- backendRefs:
148+
- kind: Service
149+
name: http-route-production
150+
port: 7001
151+
matches:
152+
- path:
153+
type: PathPrefix
154+
value: /
155+
- backendRefs:
156+
- kind: Service
157+
name: http-route-canary-campaign
158+
port: 7001
159+
matches:
160+
- cookies:
161+
- name: unb
162+
type: List
163+
values:
164+
- 2426168118
165+
- 2208203664638
166+
- 2797880990
167+
- 70772956
168+
- 2215140160618
169+
```
170+
171+
* This HTTPRoute directs incoming HTTP requests with cookie "gray=true" to the canary service "http-site-canary:80".
172+
173+
```yaml
174+
apiVersion: gateway.networking.k8s.io/v1
175+
kind: HTTPRoute
176+
metadata:
177+
name: http-route-cookie
178+
spec:
179+
hostnames:
180+
- http.site.cookie.com
181+
- http.site.cookies.com
182+
parentRefs:
183+
- group: gateway.networking.k8s.io
184+
kind: Gateway
185+
name: http-gateway
186+
rules:
187+
- backendRefs:
188+
- kind: Service
189+
name: http-site-production
190+
port: 80
191+
matches:
192+
- path:
193+
type: PathPrefix
194+
value: /
195+
- backendRefs:
196+
- kind: Service
197+
name: http-site-canary
198+
port: 80
199+
matches:
200+
- cookies:
201+
- name: gray
202+
type: Exact
203+
value: true
204+
```
205+
206+
## Prior Art
207+
Some implementations already support HTTP cookie match.
208+
209+
### Ingress-nginx supports this with a annotation nginx.ingress.kubernetes.io/canary-by-cookie
210+
211+
* The cookie to use for notifying the Ingress to route the request to the service specified in the Canary Ingress. When the cookie value of this annotation is set to always, it will be routed to the canary. When the cookie value of the annotation is set to never, it will never be routed to the canary.
212+
213+
If the Cookie header has a cookie-pair "cookie-test=always", the request will be forwarded to the backend service "cookie-test-service:80".
214+
215+
```yaml
216+
apiVersion: networking.k8s.io/v1
217+
kind: Ingress
218+
metadata:
219+
annotations:
220+
nginx.ingress.kubernetes.io/canary: "true"
221+
nginx.ingress.kubernetes.io/canary-by-cookie: "cookie-test"
222+
name: ingress-canary-cookie-test
223+
spec:
224+
ingressClassName: nginx-default
225+
rules:
226+
- host: canary.cookie.com
227+
http:
228+
paths:
229+
- path: /
230+
pathType: Exact
231+
backend:
232+
service:
233+
name: cookie-test-service
234+
port:
235+
number: 80
236+
```
237+
238+
### Nginx-ingress-controller supports this with CRDs
239+
240+
* The Match section of VirtualServer or VirtualServerRoute defines a match between conditions and an action or splits. The Condition defines a condition in a match. The Condition includes header, cookie, argument or variable.
241+
242+
In the example below, NGINX routes requests with the path "/coffee" to different upstreams based on the value of the cookie "user":
243+
244+
user=john -> coffee-future
245+
user=bob -> coffee-deprecated
246+
247+
If the cookie is not set or not equal to either "john" or "bob", NGINX routes to "coffee-stable".
248+
249+
```yaml
250+
path: /coffee
251+
matches:
252+
- conditions:
253+
- cookie: user
254+
value: john
255+
action:
256+
pass: coffee-future
257+
- conditions:
258+
- cookie: user
259+
value: bob
260+
action:
261+
pass: coffee-deprecated
262+
action:
263+
pass: coffee-stable
264+
```
265+
266+
### Tengine-ingress supports this with annotations nginx.ingress.kubernetes.io/canary-by-cookie and nginx.ingress.kubernetes.io/canary-by-cookie-value
267+
268+
* The annotation nginx.ingress.kubernetes.io/canary-by-cookie sets the cookie name.
269+
* The annotation nginx.ingress.kubernetes.io/canary-by-cookie-value sets the multiple cookie values.
270+
271+
In the following example, if the value of the cookie user is "mike" or "bob", the requests with the path "/gray" will be forwarded to the service "cookie-net-service:80".
272+
273+
```yaml
274+
apiVersion: networking.k8s.io/v1
275+
kind: Ingress
276+
metadata:
277+
annotations:
278+
nginx.ingress.kubernetes.io/canary: "true"
279+
nginx.ingress.kubernetes.io/canary-by-cookie: user
280+
nginx.ingress.kubernetes.io/canary-by-cookie-value: mike||bob
281+
name: tengine-ingress-cookie-value
282+
spec:
283+
rules:
284+
- host: tengine.cookie.net
285+
http:
286+
paths:
287+
- backend:
288+
service:
289+
name: cookie-net-service
290+
port:
291+
number: 80
292+
path: /gray
293+
pathType: ImplementationSpecific
294+
```
295+
296+
## References
297+
* [Ingress-nginx `canary`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#canary)
298+
* [Kubernetes-ingress `Condition`](https://docs.nginx.com/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/#condition)
299+
* [Tengine-ingress / `ingress_routes`](https://tengine.taobao.org/document/ingress_routes.html)
300+
* [RFC6265](https://www.rfc-editor.org/rfc/rfc6265)

geps/gep-2891/metadata.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: internal.gateway.networking.k8s.io/v1alpha1
2+
kind: GEPDetails
3+
number: 2891
4+
name: HTTP Cookie Match
5+
status: Experimental
6+
authors:
7+
- lianglli

0 commit comments

Comments
 (0)