forked from zalando/skipper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc.go
265 lines (166 loc) · 7.8 KB
/
doc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
Package eskip implements an in-memory representation of Skipper routes
and a DSL for describing Skipper route expressions, route definitions
and complete routing tables.
Grammar Summary
A routing table is built up from 0 or more route definitions. The
definitions are separated by ';'. A route definition contains one route
expression prefixed by its id and a ':'.
A routing table example:
catalog: Path("/*category") -> "https://catalog.example.org";
productPage: Path("/products/:id") -> "https://products.example.org";
userAccount: Path("/user/:id/*userpage") -> "https://users.example.org";
// 404
notfound: * ->
modPath(/.+/, "/notfound.html") -> static("/", "/var/www") ->
<shunt>
A route expression always contains a match expression and a backend
expression, and it can contain optional filter expressions. The match
expression, each filter and the backend are separated by '->'. The
filters take place between the matcher and the backend.
A route expression example:
Path("/api/*resource") && Header("Accept", "application/json") ->
modPath("^/api", "") -> requestHeader("X-Type", "external") ->
"https://api.example.org"
Match Expressions - Predicates
A match expression contains one or more predicates. An incoming
request must fulfill each of them to match the route. The predicates are
separated by '&&'.
A match expression example:
Path("/api/*resource") && Header("Accept", "application/json")
The following predicate expressions are recognized:
Path("/some/path")
The path predicate accepts a single argument, that can be a fixed path
like "/some/path", or it can contain wildcards in place of one or more
names in the path, e.g. "/some/:dir/:name", or it can end with a free
wildcard like "/some/path/*param", where the free wildcard can contain a
sub-path with multiple names. Note, that this solution implicitly
supports the glob standard, e.g. "/some/path/**" will work as expected.
The arguments are available to the filters while processing the matched
requests.
PathSubtree("/some/path")
The path subtree predicate behaves similar to the path predicate, but
it matches the exact path in the definition and any sub path below it.
The subpath is automatically provided in the path parameter with the
name "*". If a free wildcard is appended to the definition, e.g.
PathSubtree("/some/path/*rest"), the free wildcard name is used instead
of "*". The simple wildcards behave similar to the Path predicate.
PathRegexp(/regular-expression/)
The regexp path predicate accepts a regular expression as a single
argument that needs to be matched by the request path. The regular
expression can be surrounded by '/' or '"'.
Host(/host-regular-expression/)
The host predicate accepts a regular expression as a single argument
that needs to be matched by the host header in the request.
Method("HEAD")
The method predicate is used to match the http request method.
Header("Accept", "application/json")
The header predicate is used to match the http headers in the request.
It accepts two arguments, the name of the header field and the exact
header value to match.
HeaderRegexp("Accept", /\Wapplication\/json\W/)
The header regexp predicate works similar to the header expression, but
the value to be matched is a regular expression.
*
Catch all predicate.
Any()
Former, deprecated form of the catch all predicate.
Custom Predicates
Eskip supports custom route matching predicates, that can be implemented
in extensions. The notation of custom predicates is the same as of the
built-in route matching expressions:
Foo(3.14, "bar")
During parsing, custom predicates may define any arbitrary list of
arguments of types number, string or regular expression, and it is the
responsibility of the implementation to validate them.
(See the documentation of the routing package.)
Filters
Filters are used to augment the incoming requests and the outgoing
responses, or do other useful or fun stuff. Filters can have different
numbers of arguments depending on the implementation of the particular
filter. The arguments can be of type string ("a string"), number
(3.1415) or regular expression (/[.]html$/ or "[.]html$").
A filter example:
setResponseHeader("max-age", "86400") -> static("/", "/var/www/public")
The default Skipper implementation provides the following built-in
filters:
setRequestHeader("header-name", "header-value")
setResponseHeader("header-name", "header-value")
appendRequestHeader("header-name", "header-value")
appendResponseHeader("header-name", "header-value")
dropRequestHeader("header-name")
dropResponseHeader("header-name")
modPath(/regular-expression/, "replacement")
setPath("/replacement")
redirectTo(302, "https://ui.example.org")
flowId("reuse", 64)
healthcheck()
static("/images", "/var/www/images")
stripQuery("true")
preserveHost()
status(418)
tee("https://audit-logging.example.org")
consecutiveBreaker()
rateBreaker()
disableBreaker()
For details about the built-in filters, please, refer to the
documentation of the skipper/filters package. Skipper is designed to be
extendable primarily by implementing custom filters, for details about
how to create custom filters, please, refer to the documentation of the
root skipper package.
Naming conventions
Note, that the naming of predicates and filters follows the following
convention: both predicates and filters are written in camel case, and
predicates start with upper case, while filters start with lower case.
Backend
There are three types of backends: a network endpoint address, a shunt or
a loopback.
A network endpoint address example:
"http://internal.example.org:9090"
An endpoint address backend is surrounded by '"'. It contains the scheme
and the hostname of the endpoint, and optionally the port number that is
inferred from the scheme if not specified.
A shunt backend:
<shunt>
The shunt backend means that the route will not forward the request to a
network endpoint, but the router will handle the request itself. By
default, the response is in this case 404 Not found, unless a filter in
the route does not change it.
A loop backend:
<loopback>
The loopback means that the state of the request at the end of the request
filter chain will be matched against the lookup table instead of being sent
to a network backend. When the new route returns, the filter chain is on the
response is executed on the original loop route, and the response is
returned. The path parameters of the outer, looping, route are preserved for
the inner route, but the path parameters of the inner route are discarded
once it returns.
Comments
An eskip document can contain comments. The rule for comments is simple:
everything is a comment that starts with '//' and ends with a new-line
character.
Example with comments:
// forwards to the API endpoint
route1: Path("/api") -> "https://api.example.org";
route2: * -> <shunt> // everything else 404
Regular expressions
The matching predicates and the built-in filters that use regular
expressions, use the go stdlib regexp, which uses re2:
https://github.com/google/re2/wiki/Syntax
Parsing Filters
The eskip.ParseFilters method can be used to parse a chain of filters,
without the matcher and backend part of a full route expression.
Parsing
Parsing a routing table or a route expression happens with the
eskip.Parse function. In case of grammar error, it returns an error with
the approximate position of the invalid syntax element, otherwise it
returns a list of structured, in-memory route definitions.
The eskip parser does not validate the routes against all semantic rules,
e.g., whether a filter or a custom predicate implementation is available.
This validation happens during processing the parsed definitions.
Serializing
Serializing a single route happens by calling its String method.
Serializing a complete routing table happens by calling the
eskip.String method.
*/
package eskip