@@ -75,24 +75,9 @@ func createServer(virtualServer dataplane.VirtualServer) http.Server {
75
75
}
76
76
77
77
func createLocations (pathRules []dataplane.PathRule , listenerPort int32 ) []http.Location {
78
- lenPathRules := len (pathRules )
79
-
80
- if lenPathRules == 0 {
81
- return []http.Location {createDefaultRootLocation ()}
82
- }
83
-
84
- // To calculate the maximum number of locations, we need to take into account the following:
85
- // 1. Each match rule for a path rule will have one location.
86
- // 2. Each path rule may have an additional location if it contains non-path-only matches.
87
- // 3. There may be an additional location for the default root path.
88
- maxLocs := 1
89
- for _ , rules := range pathRules {
90
- maxLocs += len (rules .MatchRules ) + 1
91
- }
92
-
78
+ maxLocs , pathsAndTypes := getMaxLocationCountAndPathMap (pathRules )
93
79
locs := make ([]http.Location , 0 , maxLocs )
94
-
95
- rootPathExists := false
80
+ var rootPathExists bool
96
81
97
82
for _ , rule := range pathRules {
98
83
matches := make ([]httpMatch , 0 , len (rule .MatchRules ))
@@ -101,27 +86,23 @@ func createLocations(pathRules []dataplane.PathRule, listenerPort int32) []http.
101
86
rootPathExists = true
102
87
}
103
88
89
+ extLocations := initializeExternalLocations (rule , pathsAndTypes )
90
+
104
91
for matchRuleIdx , r := range rule .MatchRules {
105
92
m := r .GetMatch ()
106
93
107
- var loc http.Location
108
-
109
- // handle case where the only route is a path-only match
110
- // generate a standard location block without http_matches.
111
- if len (rule .MatchRules ) == 1 && isPathOnlyMatch (m ) {
112
- loc = http.Location {
113
- Path : rule .Path ,
114
- Exact : rule .PathType == dataplane .PathTypeExact ,
115
- }
116
- } else {
117
- path := createPathForMatch (rule .Path , rule .PathType , matchRuleIdx )
118
- loc = createMatchLocation (path )
119
- matches = append (matches , createHTTPMatch (m , path ))
94
+ buildLocations := extLocations
95
+ intLocation , match , intLocationExists := initializeInternalLocation (rule , matchRuleIdx , m )
96
+ if intLocationExists {
97
+ buildLocations = []http.Location {intLocation }
98
+ matches = append (matches , match )
120
99
}
121
100
122
101
if r .Filters .InvalidFilter != nil {
123
- loc .Return = & http.Return {Code : http .StatusInternalServerError }
124
- locs = append (locs , loc )
102
+ for i := range buildLocations {
103
+ buildLocations [i ].Return = & http.Return {Code : http .StatusInternalServerError }
104
+ }
105
+ locs = append (locs , buildLocations ... )
125
106
continue
126
107
}
127
108
@@ -136,21 +117,24 @@ func createLocations(pathRules []dataplane.PathRule, listenerPort int32) []http.
136
117
137
118
// RequestRedirect and proxying are mutually exclusive.
138
119
if r .Filters .RequestRedirect != nil {
139
- loc .Return = createReturnValForRedirectFilter (r .Filters .RequestRedirect , listenerPort )
140
- locs = append (locs , loc )
120
+ ret := createReturnValForRedirectFilter (r .Filters .RequestRedirect , listenerPort )
121
+ for i := range buildLocations {
122
+ buildLocations [i ].Return = ret
123
+ }
124
+ locs = append (locs , buildLocations ... )
141
125
continue
142
126
}
143
127
144
- backendName := backendGroupName (r .BackendGroup )
145
- loc .ProxySetHeaders = generateProxySetHeaders (r .Filters .RequestHeaderModifiers )
146
-
147
- if backendGroupNeedsSplit (r .BackendGroup ) {
148
- loc .ProxyPass = createProxyPassForVar (backendName )
149
- } else {
150
- loc .ProxyPass = createProxyPass (backendName )
128
+ proxySetHeaders := generateProxySetHeaders (r .Filters .RequestHeaderModifiers )
129
+ for i := range buildLocations {
130
+ buildLocations [i ].ProxySetHeaders = proxySetHeaders
151
131
}
152
132
153
- locs = append (locs , loc )
133
+ proxyPass := createProxyPass (r .BackendGroup )
134
+ for i := range buildLocations {
135
+ buildLocations [i ].ProxyPass = proxyPass
136
+ }
137
+ locs = append (locs , buildLocations ... )
154
138
}
155
139
156
140
if len (matches ) > 0 {
@@ -163,13 +147,10 @@ func createLocations(pathRules []dataplane.PathRule, listenerPort int32) []http.
163
147
panic (fmt .Errorf ("could not marshal http match: %w" , err ))
164
148
}
165
149
166
- pathLoc := http.Location {
167
- Path : rule .Path ,
168
- Exact : rule .PathType == dataplane .PathTypeExact ,
169
- HTTPMatchVar : string (b ),
150
+ for i := range extLocations {
151
+ extLocations [i ].HTTPMatchVar = string (b )
170
152
}
171
-
172
- locs = append (locs , pathLoc )
153
+ locs = append (locs , extLocations ... )
173
154
}
174
155
}
175
156
@@ -180,6 +161,93 @@ func createLocations(pathRules []dataplane.PathRule, listenerPort int32) []http.
180
161
return locs
181
162
}
182
163
164
+ // pathAndTypeMap contains a map of paths and any path types defined for that path
165
+ // for example, {/foo: {exact: {}, prefix: {}}}
166
+ type pathAndTypeMap map [string ]map [dataplane.PathType ]struct {}
167
+
168
+ // To calculate the maximum number of locations, we need to take into account the following:
169
+ // 1. Each match rule for a path rule will have one location.
170
+ // 2. Each path rule may have an additional location if it contains non-path-only matches.
171
+ // 3. Each prefix path rule may have an additional location if it doesn't contain trailing slash.
172
+ // 4. There may be an additional location for the default root path.
173
+ // We also return a map of all paths and their types.
174
+ func getMaxLocationCountAndPathMap (pathRules []dataplane.PathRule ) (int , pathAndTypeMap ) {
175
+ maxLocs := 1
176
+ pathsAndTypes := make (pathAndTypeMap )
177
+ for _ , rule := range pathRules {
178
+ maxLocs += len (rule .MatchRules ) + 2
179
+ if pathsAndTypes [rule .Path ] == nil {
180
+ pathsAndTypes [rule .Path ] = map [dataplane.PathType ]struct {}{
181
+ rule .PathType : {},
182
+ }
183
+ } else {
184
+ pathsAndTypes [rule.Path ][rule.PathType ] = struct {}{}
185
+ }
186
+ }
187
+
188
+ return maxLocs , pathsAndTypes
189
+ }
190
+
191
+ func initializeExternalLocations (
192
+ rule dataplane.PathRule ,
193
+ pathsAndTypes pathAndTypeMap ,
194
+ ) []http.Location {
195
+ extLocations := make ([]http.Location , 0 , 2 )
196
+ externalLocPath := createPath (rule )
197
+ // If the path type is Prefix and doesn't contain a trailing slash, then we need a second location
198
+ // that handles the Exact prefix case (if it doesn't already exist), and the first location is updated
199
+ // to handle the trailing slash prefix case (if it doesn't already exist)
200
+ if isNonSlashedPrefixPath (rule .PathType , externalLocPath ) {
201
+ // if Exact path and trailing slash Prefix path already exist, then we don't need to build anything
202
+ _ , exactPathExists := pathsAndTypes [rule.Path ][dataplane.PathTypeExact ]
203
+ var trailingSlashPrefixPathExists bool
204
+ if pathTypes , exists := pathsAndTypes [rule .Path + "/" ]; exists {
205
+ _ , trailingSlashPrefixPathExists = pathTypes [dataplane .PathTypePrefix ]
206
+ }
207
+
208
+ if exactPathExists && trailingSlashPrefixPathExists {
209
+ return []http.Location {}
210
+ }
211
+
212
+ if ! trailingSlashPrefixPathExists {
213
+ externalLocTrailing := http.Location {
214
+ Path : externalLocPath + "/" ,
215
+ }
216
+ extLocations = append (extLocations , externalLocTrailing )
217
+ }
218
+ if ! exactPathExists {
219
+ externalLocExact := http.Location {
220
+ Path : exactPath (externalLocPath ),
221
+ }
222
+ extLocations = append (extLocations , externalLocExact )
223
+ }
224
+ } else {
225
+ externalLoc := http.Location {
226
+ Path : externalLocPath ,
227
+ }
228
+ extLocations = []http.Location {externalLoc }
229
+ }
230
+
231
+ return extLocations
232
+ }
233
+
234
+ func initializeInternalLocation (
235
+ rule dataplane.PathRule ,
236
+ matchRuleIdx int ,
237
+ match v1beta1.HTTPRouteMatch ,
238
+ ) (http.Location , httpMatch , bool ) {
239
+ var intLocation http.Location
240
+ var hm httpMatch
241
+ intLocationNeeded := len (rule .MatchRules ) != 1 || ! isPathOnlyMatch (match )
242
+ if intLocationNeeded {
243
+ path := createPathForMatch (rule .Path , rule .PathType , matchRuleIdx )
244
+ hm = createHTTPMatch (match , path )
245
+ intLocation = createMatchLocation (path )
246
+ }
247
+
248
+ return intLocation , hm , intLocationNeeded
249
+ }
250
+
183
251
func createReturnValForRedirectFilter (filter * v1beta1.HTTPRequestRedirectFilter , listenerPort int32 ) * http.Return {
184
252
if filter == nil {
185
253
return nil
@@ -308,12 +376,13 @@ func isPathOnlyMatch(match v1beta1.HTTPRouteMatch) bool {
308
376
return match .Method == nil && match .Headers == nil && match .QueryParams == nil
309
377
}
310
378
311
- func createProxyPass (address string ) string {
312
- return "http://" + address
313
- }
379
+ func createProxyPass (backendGroup dataplane.BackendGroup ) string {
380
+ backendName := backendGroupName (backendGroup )
381
+ if backendGroupNeedsSplit (backendGroup ) {
382
+ return "http://$" + convertStringToSafeVariableName (backendName )
383
+ }
314
384
315
- func createProxyPassForVar (variable string ) string {
316
- return "http://$" + convertStringToSafeVariableName (variable )
385
+ return "http://" + backendName
317
386
}
318
387
319
388
func createMatchLocation (path string ) http.Location {
@@ -369,6 +438,20 @@ func convertSetHeaders(headers []dataplane.HTTPHeader) []http.Header {
369
438
return locHeaders
370
439
}
371
440
441
+ func exactPath (path string ) string {
442
+ return fmt .Sprintf ("= %s" , path )
443
+ }
444
+
445
+ // createPath builds the location path depending on the path type.
446
+ func createPath (rule dataplane.PathRule ) string {
447
+ switch rule .PathType {
448
+ case dataplane .PathTypeExact :
449
+ return exactPath (rule .Path )
450
+ default :
451
+ return rule .Path
452
+ }
453
+ }
454
+
372
455
func createPathForMatch (path string , pathType dataplane.PathType , routeIdx int ) string {
373
456
return fmt .Sprintf ("%s_%s_route%d" , path , pathType , routeIdx )
374
457
}
@@ -379,3 +462,8 @@ func createDefaultRootLocation() http.Location {
379
462
Return : & http.Return {Code : http .StatusNotFound },
380
463
}
381
464
}
465
+
466
+ // returns whether or not a path is of type Prefix and does not contain a trailing slash
467
+ func isNonSlashedPrefixPath (pathType dataplane.PathType , path string ) bool {
468
+ return pathType == dataplane .PathTypePrefix && ! strings .HasSuffix (path , "/" )
469
+ }
0 commit comments