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
New 'php_fastcgi' directive for convenient PHP+FastCGI reverse proxy
Matthew Holt committed
Sep 10, 2019 at 14:16 UTC
0cf592fa2e0d2fff8e9379095bbe17f7c8cbd4f2
2 files changed
+146
-1
caddyconfig/httpcaddyfile/directives.go
+1
@@ -34,6 +34,7 @@ var defaultDirectiveOrder = []string{
34
"templates",
35
"redir",
36
"static_response", // TODO: "reply" or "respond"?
37
+ "php_fastcgi",
38
"reverse_proxy",
39
"file_server",
40
}
modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go
+145
-1
@@ -14,7 +14,21 @@
14
15
package fastcgi
16
17
-import "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
17
+import (
18
+ "encoding/json"
19
+
20
+ "github.com/caddyserver/caddy/v2/caddyconfig"
21
+ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
22
+ "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
23
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp"
24
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
25
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
26
+ "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
27
+)
28
+
29
+func init() {
30
+ httpcaddyfile.RegisterDirective("php_fastcgi", parsePHPFastCGI)
31
+}
32
33
// UnmarshalCaddyfile deserializes Caddyfile tokens into h.
34
//
@@ -55,3 +69,133 @@ func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
69
}
70
return nil
71
}
72
+
73
+// parsePHPFastCGI parses the php_fastcgi directive, which has the same syntax
74
+// as the reverse_proxy directive (in fact, the reverse_proxy's directive
75
+// Unmarshaler is invoked by this function) but the resulting proxy is specially
76
+// configured for most™️ PHP apps over FastCGI. A line such as this:
77
+//
78
+// php_fastcgi localhost:7777
79
+//
80
+// is equivalent to:
81
+//
82
+// matcher indexFiles {
83
+// file {
84
+// try_files {path} index.php
85
+// }
86
+// }
87
+// rewrite match:indexFiles {http.matchers.file.relative}
88
+//
89
+// matcher phpFiles {
90
+// path *.php
91
+// }
92
+// reverse_proxy match:phpFiles localhost:7777 {
93
+// transport fastcgi {
94
+// split .php
95
+// }
96
+// }
97
+//
98
+// Thus, this directive produces multiple routes, each with a different
99
+// matcher because multiple consecutive routes are necessary to support
100
+// the common PHP use case. If this "common" config is not compatible
101
+// with a user's PHP requirements, they can use the manual approach as
102
+// above to configure it precisely as they need.
103
+//
104
+// If a matcher is specified by the user, for example:
105
+//
106
+// php_fastcgi /subpath localhost:7777
107
+//
108
+// then the resulting routes are wrapped in a subroute that uses the
109
+// user's matcher as a prerequisite to enter the subroute.
110
+func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
111
+ if !h.Next() {
112
+ return nil, h.ArgErr()
113
+ }
114
+
115
+ // route to rewrite to PHP index file
116
+ rewriteMatcherSet := map[string]json.RawMessage{
117
+ "file": h.JSON(fileserver.MatchFile{
118
+ TryFiles: []string{"{http.request.uri.path}", "index.php"},
119
+ }, nil),
120
+ }
121
+ rewriteHandler := rewrite.Rewrite{
122
+ URI: "{http.matchers.file.relative}{http.request.uri.query_string}",
123
+ Rehandle: true,
124
+ }
125
+ rewriteRoute := caddyhttp.Route{
126
+ MatcherSetsRaw: []map[string]json.RawMessage{rewriteMatcherSet},
127
+ HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},
128
+ }
129
+
130
+ // route to actually reverse proxy requests to PHP files;
131
+ // match only requests that are for PHP files
132
+ rpMatcherSet := map[string]json.RawMessage{
133
+ "path": h.JSON([]string{"*.php"}, nil),
134
+ }
135
+
136
+ // if the user specified a matcher token, use that
137
+ // matcher in a route that wraps both of our routes;
138
+ // either way, strip the matcher token and pass
139
+ // the remaining tokens to the unmarshaler so that
140
+ // we can gain the rest of the reverse_proxy syntax
141
+ userMatcherSet, hasUserMatcher, err := h.MatcherToken()
142
+ if err != nil {
143
+ return nil, err
144
+ }
145
+ if hasUserMatcher {
146
+ h.Dispenser.Delete() // strip matcher token
147
+ }
148
+ h.Dispenser.Reset() // pretend this lookahead never happened
149
+
150
+ // set up the transport for FastCGI, and specifically PHP
151
+ fcgiTransport := Transport{SplitPath: ".php"}
152
+
153
+ // create the reverse proxy handler which uses our FastCGI transport
154
+ rpHandler := &reverseproxy.Handler{
155
+ TransportRaw: caddyconfig.JSONModuleObject(fcgiTransport, "protocol", "fastcgi", nil),
156
+ }
157
+
158
+ // the rest of the config is specified by the user
159
+ // using the reverse_proxy directive syntax
160
+ err = rpHandler.UnmarshalCaddyfile(h.Dispenser)
161
+ if err != nil {
162
+ return nil, err
163
+ }
164
+
165
+ // create the final reverse proxy route which is
166
+ // conditional on matching PHP files
167
+ rpRoute := caddyhttp.Route{
168
+ MatcherSetsRaw: []map[string]json.RawMessage{rpMatcherSet},
169
+ HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(rpHandler, "handler", "reverse_proxy", nil)},
170
+ }
171
+
172
+ // the user's matcher is a prerequisite for ours, so
173
+ // wrap ours in a subroute and return that
174
+ if hasUserMatcher {
175
+ subroute := caddyhttp.Subroute{
176
+ Routes: caddyhttp.RouteList{rewriteRoute, rpRoute},
177
+ }
178
+ return []httpcaddyfile.ConfigValue{
179
+ {
180
+ Class: "route",
181
+ Value: caddyhttp.Route{
182
+ MatcherSetsRaw: []map[string]json.RawMessage{userMatcherSet},
183
+ HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(subroute, "handler", "subroute", nil)},
184
+ },
185
+ },
186
+ }, nil
187
+ }
188
+
189
+ // if the user did not specify a matcher, then
190
+ // we can just use our own matchers
191
+ return []httpcaddyfile.ConfigValue{
192
+ {
193
+ Class: "route",
194
+ Value: rewriteRoute,
195
+ },
196
+ {
197
+ Class: "route",
198
+ Value: rpRoute,
199
+ },
200
+ }, nil
201
+}