Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
fastcgi: `php_fastcgi` subdirectives to override shortcut behaviour (#3255)
* fastcgi: Add new php_fastcgi subdirectives to override the shortcut * fastcgi: Support "index off" to disable redir and try_files * fastcgi: Remove whitespace to satisfy linter * fastcgi: Run gofmt * fastcgi: Make a new dispenser instead of using rewind * fastcgi: Some fmt * fastcgi: Add a couple adapt tests * fastcgi: Clean up for loops * fastcgi: Move adapt tests to separate files
Francis Lavoie committed
May 18, 2020 at 14:15 UTC
7243454a96d6dcbe504af7287de1e68ab4b80c83
3 files changed
+306
-39
caddytest/integration/caddyfile_adapt/php_fastcgi_index_off.txt
new
+66
@@ -0,0 +1,66 @@
1
+:8884
2
+
3
+php_fastcgi localhost:9000 {
4
+ # some php_fastcgi-specific subdirectives
5
+ split .php .php5
6
+ env VAR1 value1
7
+ env VAR2 value2
8
+ root /var/www
9
+ index off
10
+
11
+ # passed through to reverse_proxy (directive order doesn't matter!)
12
+ lb_policy random
13
+}
14
+----------
15
+{
16
+ "apps": {
17
+ "http": {
18
+ "servers": {
19
+ "srv0": {
20
+ "listen": [
21
+ ":8884"
22
+ ],
23
+ "routes": [
24
+ {
25
+ "match": [
26
+ {
27
+ "path": [
28
+ "*.php",
29
+ "*.php5"
30
+ ]
31
+ }
32
+ ],
33
+ "handle": [
34
+ {
35
+ "handler": "reverse_proxy",
36
+ "load_balancing": {
37
+ "selection_policy": {
38
+ "policy": "random"
39
+ }
40
+ },
41
+ "transport": {
42
+ "env": {
43
+ "VAR1": "value1",
44
+ "VAR2": "value2"
45
+ },
46
+ "protocol": "fastcgi",
47
+ "root": "/var/www",
48
+ "split_path": [
49
+ ".php",
50
+ ".php5"
51
+ ]
52
+ },
53
+ "upstreams": [
54
+ {
55
+ "dial": "localhost:9000"
56
+ }
57
+ ]
58
+ }
59
+ ]
60
+ }
61
+ ]
62
+ }
63
+ }
64
+ }
65
+ }
66
+}
\ No newline at end of file
caddytest/integration/caddyfile_adapt/php_fastcgi_subdirectives.txt
new
+118
@@ -0,0 +1,118 @@
1
+:8884
2
+
3
+php_fastcgi localhost:9000 {
4
+ # some php_fastcgi-specific subdirectives
5
+ split .php .php5
6
+ env VAR1 value1
7
+ env VAR2 value2
8
+ root /var/www
9
+ index index.php5
10
+
11
+ # passed through to reverse_proxy (directive order doesn't matter!)
12
+ lb_policy random
13
+}
14
+----------
15
+{
16
+ "apps": {
17
+ "http": {
18
+ "servers": {
19
+ "srv0": {
20
+ "listen": [
21
+ ":8884"
22
+ ],
23
+ "routes": [
24
+ {
25
+ "match": [
26
+ {
27
+ "file": {
28
+ "try_files": [
29
+ "{http.request.uri.path}/index.php5"
30
+ ]
31
+ },
32
+ "not": [
33
+ {
34
+ "path": [
35
+ "*/"
36
+ ]
37
+ }
38
+ ]
39
+ }
40
+ ],
41
+ "handle": [
42
+ {
43
+ "handler": "static_response",
44
+ "headers": {
45
+ "Location": [
46
+ "{http.request.uri.path}/"
47
+ ]
48
+ },
49
+ "status_code": 308
50
+ }
51
+ ]
52
+ },
53
+ {
54
+ "match": [
55
+ {
56
+ "file": {
57
+ "try_files": [
58
+ "{http.request.uri.path}",
59
+ "{http.request.uri.path}/index.php5",
60
+ "index.php5"
61
+ ],
62
+ "split_path": [
63
+ ".php",
64
+ ".php5"
65
+ ]
66
+ }
67
+ }
68
+ ],
69
+ "handle": [
70
+ {
71
+ "handler": "rewrite",
72
+ "uri": "{http.matchers.file.relative}"
73
+ }
74
+ ]
75
+ },
76
+ {
77
+ "match": [
78
+ {
79
+ "path": [
80
+ "*.php",
81
+ "*.php5"
82
+ ]
83
+ }
84
+ ],
85
+ "handle": [
86
+ {
87
+ "handler": "reverse_proxy",
88
+ "load_balancing": {
89
+ "selection_policy": {
90
+ "policy": "random"
91
+ }
92
+ },
93
+ "transport": {
94
+ "env": {
95
+ "VAR1": "value1",
96
+ "VAR2": "value2"
97
+ },
98
+ "protocol": "fastcgi",
99
+ "root": "/var/www",
100
+ "split_path": [
101
+ ".php",
102
+ ".php5"
103
+ ]
104
+ },
105
+ "upstreams": [
106
+ {
107
+ "dial": "localhost:9000"
108
+ }
109
+ ]
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ }
115
+ }
116
+ }
117
+ }
118
+}
\ No newline at end of file
modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go
+122
-39
@@ -123,47 +123,133 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
123
return nil, h.ArgErr()
124
}
125
126
- // route to redirect to canonical path if index PHP file
127
- redirMatcherSet := caddy.ModuleMap{
128
- "file": h.JSON(fileserver.MatchFile{
129
- TryFiles: []string{"{http.request.uri.path}/index.php"},
130
- }),
131
- "not": h.JSON(caddyhttp.MatchNot{
132
- MatcherSetsRaw: []caddy.ModuleMap{
133
- {
134
- "path": h.JSON(caddyhttp.MatchPath{"*/"}),
135
- },
136
- },
137
- }),
138
- }
139
- redirHandler := caddyhttp.StaticResponse{
140
- StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
141
- Headers: http.Header{"Location": []string{"{http.request.uri.path}/"}},
142
- }
143
- redirRoute := caddyhttp.Route{
144
- MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},
145
- HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},
146
- }
126
+ // set up the transport for FastCGI, and specifically PHP
127
+ fcgiTransport := Transport{}
128
148
- // route to rewrite to PHP index file
149
- rewriteMatcherSet := caddy.ModuleMap{
150
- "file": h.JSON(fileserver.MatchFile{
151
- TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php", "index.php"},
152
- SplitPath: []string{".php"},
153
- }),
154
- }
155
- rewriteHandler := rewrite.Rewrite{
156
- URI: "{http.matchers.file.relative}",
129
+ // set up the set of file extensions allowed to execute PHP code
130
+ extensions := []string{".php"}
131
+
132
+ // set the default index file for the try_files rewrites
133
+ indexFile := "index.php"
134
+
135
+ // make a new dispenser from the remaining tokens so that we
136
+ // can reset the dispenser back to this point for the
137
+ // reverse_proxy unmarshaler to read from it as well
138
+ dispenser := h.NewFromNextSegment()
139
+
140
+ // read the subdirectives that we allow as overrides to
141
+ // the php_fastcgi shortcut
142
+ // NOTE: we delete the tokens as we go so that the reverse_proxy
143
+ // unmarshal doesn't see these subdirectives which it cannot handle
144
+ for dispenser.Next() {
145
+ for dispenser.NextBlock(0) {
146
+ switch dispenser.Val() {
147
+ case "root":
148
+ if !dispenser.NextArg() {
149
+ return nil, dispenser.ArgErr()
150
+ }
151
+ fcgiTransport.Root = dispenser.Val()
152
+ dispenser.Delete()
153
+ dispenser.Delete()
154
+
155
+ case "split":
156
+ extensions = dispenser.RemainingArgs()
157
+ dispenser.Delete()
158
+ for range extensions {
159
+ dispenser.Delete()
160
+ }
161
+ if len(extensions) == 0 {
162
+ return nil, dispenser.ArgErr()
163
+ }
164
+
165
+ case "env":
166
+ args := dispenser.RemainingArgs()
167
+ dispenser.Delete()
168
+ for range args {
169
+ dispenser.Delete()
170
+ }
171
+ if len(args) != 2 {
172
+ return nil, dispenser.ArgErr()
173
+ }
174
+ if fcgiTransport.EnvVars == nil {
175
+ fcgiTransport.EnvVars = make(map[string]string)
176
+ }
177
+ fcgiTransport.EnvVars[args[0]] = args[1]
178
+
179
+ case "index":
180
+ args := dispenser.RemainingArgs()
181
+ dispenser.Delete()
182
+ for range args {
183
+ dispenser.Delete()
184
+ }
185
+ if len(args) != 1 {
186
+ return nil, dispenser.ArgErr()
187
+ }
188
+ indexFile = args[0]
189
+ }
190
+ }
191
}
158
- rewriteRoute := caddyhttp.Route{
159
- MatcherSetsRaw: []caddy.ModuleMap{rewriteMatcherSet},
160
- HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},
192
+
193
+ // reset the dispenser after we're done so that the reverse_proxy
194
+ // unmarshaler can read it from the start
195
+ dispenser.Reset()
196
+
197
+ // set up a route list that we'll append to
198
+ routes := caddyhttp.RouteList{}
199
+
200
+ // set the list of allowed path segments on which to split
201
+ fcgiTransport.SplitPath = extensions
202
+
203
+ // if the index is turned off, we skip the redirect and try_files
204
+ if indexFile != "off" {
205
+ // route to redirect to canonical path if index PHP file
206
+ redirMatcherSet := caddy.ModuleMap{
207
+ "file": h.JSON(fileserver.MatchFile{
208
+ TryFiles: []string{"{http.request.uri.path}/" + indexFile},
209
+ }),
210
+ "not": h.JSON(caddyhttp.MatchNot{
211
+ MatcherSetsRaw: []caddy.ModuleMap{
212
+ {
213
+ "path": h.JSON(caddyhttp.MatchPath{"*/"}),
214
+ },
215
+ },
216
+ }),
217
+ }
218
+ redirHandler := caddyhttp.StaticResponse{
219
+ StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
220
+ Headers: http.Header{"Location": []string{"{http.request.uri.path}/"}},
221
+ }
222
+ redirRoute := caddyhttp.Route{
223
+ MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},
224
+ HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},
225
+ }
226
+
227
+ // route to rewrite to PHP index file
228
+ rewriteMatcherSet := caddy.ModuleMap{
229
+ "file": h.JSON(fileserver.MatchFile{
230
+ TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/" + indexFile, indexFile},
231
+ SplitPath: extensions,
232
+ }),
233
+ }
234
+ rewriteHandler := rewrite.Rewrite{
235
+ URI: "{http.matchers.file.relative}",
236
+ }
237
+ rewriteRoute := caddyhttp.Route{
238
+ MatcherSetsRaw: []caddy.ModuleMap{rewriteMatcherSet},
239
+ HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},
240
+ }
241
+
242
+ routes = append(routes, redirRoute, rewriteRoute)
243
}
244
245
// route to actually reverse proxy requests to PHP files;
246
// match only requests that are for PHP files
247
+ pathList := []string{}
248
+ for _, ext := range extensions {
249
+ pathList = append(pathList, "*"+ext)
250
+ }
251
rpMatcherSet := caddy.ModuleMap{
166
- "path": h.JSON([]string{"*.php"}),
252
+ "path": h.JSON(pathList),
253
}
254
255
// if the user specified a matcher token, use that
@@ -176,9 +262,6 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
262
return nil, err
263
}
264
179
- // set up the transport for FastCGI, and specifically PHP
180
- fcgiTransport := Transport{SplitPath: []string{".php"}}
181
-
265
// create the reverse proxy handler which uses our FastCGI transport
266
rpHandler := &reverseproxy.Handler{
267
TransportRaw: caddyconfig.JSONModuleObject(fcgiTransport, "protocol", "fastcgi", nil),
@@ -188,7 +271,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
271
// using the reverse_proxy directive syntax
272
// TODO: this can overwrite our fcgiTransport that we encoded and
273
// set on the rpHandler... even with a non-fastcgi transport!
191
- err = rpHandler.UnmarshalCaddyfile(h.Dispenser)
274
+ err = rpHandler.UnmarshalCaddyfile(dispenser)
275
if err != nil {
276
return nil, err
277
}
@@ -201,7 +284,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
284
}
285
286
subroute := caddyhttp.Subroute{
204
- Routes: caddyhttp.RouteList{redirRoute, rewriteRoute, rpRoute},
287
+ Routes: append(routes, rpRoute),
288
}
289
290
// the user's matcher is a prerequisite for ours, so