1
1
package config
2
2
3
3
import (
4
+ "encoding/json"
4
5
"fmt"
5
- "regexp "
6
+ "maps "
6
7
"strconv"
7
8
"strings"
8
9
gotemplate "text/template"
@@ -39,52 +40,57 @@ var baseHeaders = []http.Header{
39
40
},
40
41
}
41
42
42
- func executeServers (conf dataplane.Configuration ) ([]http. Server , map [ string ][]http. RouteMatch ) {
43
+ func executeServers (conf dataplane.Configuration ) [] executeResult {
43
44
servers , httpMatchPairs := createServers (conf .HTTPServers , conf .SSLServers )
44
45
45
- return servers , httpMatchPairs
46
+ serverResult := executeResult {
47
+ dest : httpConfigFile ,
48
+ data : execute (serversTemplate , servers ),
49
+ }
50
+
51
+ // create httpMatchPair conf
52
+ httpMatchConf , err := json .Marshal (httpMatchPairs )
53
+ if err != nil {
54
+ // panic is safe here because we should never fail to marshal the match unless we constructed it incorrectly.
55
+ panic (fmt .Errorf ("could not marshal http match pairs: %w" , err ))
56
+ }
57
+
58
+ httpMatchResult := executeResult {
59
+ dest : httpMatchVarsFile ,
60
+ data : httpMatchConf ,
61
+ }
62
+
63
+ return []executeResult {serverResult , httpMatchResult }
46
64
}
47
65
48
- func createServers (httpServers , sslServers []dataplane.VirtualServer ) (
49
- []http.Server ,
50
- map [string ][]http.RouteMatch ,
51
- ) {
66
+ func createServers (httpServers , sslServers []dataplane.VirtualServer ) ([]http.Server , httpMatchPairs ) {
52
67
servers := make ([]http.Server , 0 , len (httpServers )+ len (sslServers ))
53
- finalMatchPairs := make (map [ string ][]http. RouteMatch )
68
+ finalMatchPairs := make (httpMatchPairs )
54
69
55
- for _ , s := range httpServers {
56
- httpServer , matchPair := createServer (s )
70
+ for serverID , s := range httpServers {
71
+ httpServer , matchPair := createServer (s , serverID )
57
72
servers = append (servers , httpServer )
58
-
59
- for key , val := range matchPair {
60
- finalMatchPairs [key ] = val
61
- }
73
+ maps .Copy (finalMatchPairs , matchPair )
62
74
}
63
75
64
- for _ , s := range sslServers {
65
- sslServer , matchPair := createSSLServer (s )
76
+ for serverID , s := range sslServers {
77
+ sslServer , matchPair := createSSLServer (s , serverID )
66
78
servers = append (servers , sslServer )
67
-
68
- for key , val := range matchPair {
69
- finalMatchPairs [key ] = val
70
- }
79
+ maps .Copy (finalMatchPairs , matchPair )
71
80
}
72
81
73
82
return servers , finalMatchPairs
74
83
}
75
84
76
- func createSSLServer (virtualServer dataplane.VirtualServer ) (
77
- http.Server ,
78
- map [string ][]http.RouteMatch ,
79
- ) {
85
+ func createSSLServer (virtualServer dataplane.VirtualServer , serverID int ) (http.Server , httpMatchPairs ) {
80
86
if virtualServer .IsDefault {
81
87
return http.Server {
82
88
IsDefaultSSL : true ,
83
89
Port : virtualServer .Port ,
84
90
}, nil
85
91
}
86
92
87
- locs , matchPairs := createLocations (virtualServer )
93
+ locs , matchPairs := createLocations (& virtualServer , serverID )
88
94
89
95
return http.Server {
90
96
ServerName : virtualServer .Hostname ,
@@ -97,18 +103,15 @@ func createSSLServer(virtualServer dataplane.VirtualServer) (
97
103
}, matchPairs
98
104
}
99
105
100
- func createServer (virtualServer dataplane.VirtualServer ) (
101
- http.Server ,
102
- map [string ][]http.RouteMatch ,
103
- ) {
106
+ func createServer (virtualServer dataplane.VirtualServer , serverID int ) (http.Server , httpMatchPairs ) {
104
107
if virtualServer .IsDefault {
105
108
return http.Server {
106
109
IsDefaultHTTP : true ,
107
110
Port : virtualServer .Port ,
108
111
}, nil
109
112
}
110
113
111
- locs , matchPairs := createLocations (virtualServer )
114
+ locs , matchPairs := createLocations (& virtualServer , serverID )
112
115
113
116
return http.Server {
114
117
ServerName : virtualServer .Hostname ,
@@ -124,19 +127,16 @@ type rewriteConfig struct {
124
127
Rewrite string
125
128
}
126
129
127
- type httpMatchPairs map [string ][]http. RouteMatch
130
+ type httpMatchPairs map [string ][]RouteMatch
128
131
129
- func createLocations (server dataplane.VirtualServer ) (
130
- []http.Location ,
131
- map [string ][]http.RouteMatch ,
132
- ) {
132
+ func createLocations (server * dataplane.VirtualServer , serverID int ) ([]http.Location , httpMatchPairs ) {
133
133
maxLocs , pathsAndTypes := getMaxLocationCountAndPathMap (server .PathRules )
134
134
locs := make ([]http.Location , 0 , maxLocs )
135
135
matchPairs := make (httpMatchPairs )
136
136
var rootPathExists bool
137
137
138
138
for pathRuleIdx , rule := range server .PathRules {
139
- matches := make ([]http. RouteMatch , 0 , len (rule .MatchRules ))
139
+ matches := make ([]RouteMatch , 0 , len (rule .MatchRules ))
140
140
141
141
if rule .Path == rootPath {
142
142
rootPathExists = true
@@ -157,9 +157,19 @@ func createLocations(server dataplane.VirtualServer) (
157
157
}
158
158
159
159
if len (matches ) > 0 {
160
- for i := range extLocations {
161
- key := server .Hostname + extLocations [i ].Path + strconv .Itoa (int (server .Port ))
162
- extLocations [i ].HTTPMatchKey = sanitizeKey (key )
160
+ for i , loc := range extLocations {
161
+ // FIXME(sberman): De-dupe matches and associated locations
162
+ // so we don't need nginx/njs to perform unnecessary matching.
163
+ // https://github.com/nginxinc/nginx-gateway-fabric/issues/662
164
+ var key string
165
+ if server .SSL != nil {
166
+ key += "SSL"
167
+ }
168
+ key += strconv .Itoa (serverID ) + "_" + strconv .Itoa (pathRuleIdx )
169
+ if strings .Contains (loc .Path , "= /" ) {
170
+ key += "EXACT"
171
+ }
172
+ extLocations [i ].HTTPMatchKey = key
163
173
matchPairs [extLocations [i ].HTTPMatchKey ] = matches
164
174
}
165
175
locs = append (locs , extLocations ... )
@@ -173,13 +183,6 @@ func createLocations(server dataplane.VirtualServer) (
173
183
return locs , matchPairs
174
184
}
175
185
176
- // removeSpecialCharacters removes '/', '.' from key and replaces '= ' with 'EXACT',
177
- // to avoid compilation issues with NJS and NGINX Conf.
178
- func sanitizeKey (input string ) string {
179
- s := regexp .MustCompile ("[./]" ).ReplaceAllString (input , "" )
180
- return regexp .MustCompile ("= " ).ReplaceAllString (s , "EXACT" )
181
- }
182
-
183
186
// pathAndTypeMap contains a map of paths and any path types defined for that path
184
187
// for example, {/foo: {exact: {}, prefix: {}}}
185
188
type pathAndTypeMap map [string ]map [dataplane.PathType ]struct {}
@@ -256,7 +259,7 @@ func initializeInternalLocation(
256
259
pathruleIdx ,
257
260
matchRuleIdx int ,
258
261
match dataplane.Match ,
259
- ) (http.Location , http. RouteMatch ) {
262
+ ) (http.Location , RouteMatch ) {
260
263
path := fmt .Sprintf ("@rule%d-route%d" , pathruleIdx , matchRuleIdx )
261
264
return createMatchLocation (path ), createRouteMatch (match , path )
262
265
}
@@ -431,8 +434,26 @@ func createRewritesValForRewriteFilter(filter *dataplane.HTTPURLRewriteFilter, p
431
434
return rewrites
432
435
}
433
436
434
- func createRouteMatch (match dataplane.Match , redirectPath string ) http.RouteMatch {
435
- hm := http.RouteMatch {
437
+ // httpMatch is an internal representation of an HTTPRouteMatch.
438
+ // This struct is marshaled into a string and stored as a variable in the nginx location block for the route's path.
439
+ // The NJS httpmatches module will look up this variable on the request object and compare the request against the
440
+ // Method, Headers, and QueryParams contained in httpMatch.
441
+ // If the request satisfies the httpMatch, NGINX will redirect the request to the location RedirectPath.
442
+ type RouteMatch struct {
443
+ // Method is the HTTPMethod of the HTTPRouteMatch.
444
+ Method string `json:"method,omitempty"`
445
+ // RedirectPath is the path to redirect the request to if the request satisfies the match conditions.
446
+ RedirectPath string `json:"redirectPath,omitempty"`
447
+ // Headers is a list of HTTPHeaders name value pairs with the format "{name}:{value}".
448
+ Headers []string `json:"headers,omitempty"`
449
+ // QueryParams is a list of HTTPQueryParams name value pairs with the format "{name}={value}".
450
+ QueryParams []string `json:"params,omitempty"`
451
+ // Any represents a match with no match conditions.
452
+ Any bool `json:"any,omitempty"`
453
+ }
454
+
455
+ func createRouteMatch (match dataplane.Match , redirectPath string ) RouteMatch {
456
+ hm := RouteMatch {
436
457
RedirectPath : redirectPath ,
437
458
}
438
459
0 commit comments