| 
									
										
										
										
											2019-09-09 12:23:27 -06:00
										 |  |  |  | // Copyright 2015 Matthew Holt and The Caddy Authors | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  | // limitations under the License. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | package fastcgi | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | import ( | 
					
						
							|  |  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 	"github.com/caddyserver/caddy/v2" | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig" | 
					
						
							|  |  |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" | 
					
						
							|  |  |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" | 
					
						
							|  |  |  |  | 	"github.com/caddyserver/caddy/v2/modules/caddyhttp" | 
					
						
							|  |  |  |  | 	"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver" | 
					
						
							|  |  |  |  | 	"github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy" | 
					
						
							|  |  |  |  | 	"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func init() { | 
					
						
							|  |  |  |  | 	httpcaddyfile.RegisterDirective("php_fastcgi", parsePHPFastCGI) | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-09 12:23:27 -06:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | // UnmarshalCaddyfile deserializes Caddyfile tokens into h. | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | //     transport fastcgi { | 
					
						
							|  |  |  |  | //         root <path> | 
					
						
							|  |  |  |  | //         split <at> | 
					
						
							|  |  |  |  | //         env <key> <value> | 
					
						
							|  |  |  |  | //     } | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-09-10 19:21:52 -06:00
										 |  |  |  | 	for d.Next() { | 
					
						
							|  |  |  |  | 		for d.NextBlock(0) { | 
					
						
							|  |  |  |  | 			switch d.Val() { | 
					
						
							|  |  |  |  | 			case "root": | 
					
						
							|  |  |  |  | 				if !d.NextArg() { | 
					
						
							|  |  |  |  | 					return d.ArgErr() | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				t.Root = d.Val() | 
					
						
							| 
									
										
										
										
											2019-09-09 12:23:27 -06:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 19:21:52 -06:00
										 |  |  |  | 			case "split": | 
					
						
							|  |  |  |  | 				if !d.NextArg() { | 
					
						
							|  |  |  |  | 					return d.ArgErr() | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				t.SplitPath = d.Val() | 
					
						
							| 
									
										
										
										
											2019-09-09 12:23:27 -06:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 19:21:52 -06:00
										 |  |  |  | 			case "env": | 
					
						
							|  |  |  |  | 				args := d.RemainingArgs() | 
					
						
							|  |  |  |  | 				if len(args) != 2 { | 
					
						
							|  |  |  |  | 					return d.ArgErr() | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				if t.EnvVars == nil { | 
					
						
							|  |  |  |  | 					t.EnvVars = make(map[string]string) | 
					
						
							|  |  |  |  | 				} | 
					
						
							|  |  |  |  | 				t.EnvVars[args[0]] = args[1] | 
					
						
							| 
									
										
										
										
											2019-09-09 12:23:27 -06:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 19:21:52 -06:00
										 |  |  |  | 			default: | 
					
						
							|  |  |  |  | 				return d.Errf("unrecognized subdirective %s", d.Val()) | 
					
						
							|  |  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-09-09 12:23:27 -06:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | // parsePHPFastCGI parses the php_fastcgi directive, which has the same syntax | 
					
						
							|  |  |  |  | // as the reverse_proxy directive (in fact, the reverse_proxy's directive | 
					
						
							|  |  |  |  | // Unmarshaler is invoked by this function) but the resulting proxy is specially | 
					
						
							|  |  |  |  | // configured for most™️ PHP apps over FastCGI. A line such as this: | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | //     php_fastcgi localhost:7777 | 
					
						
							|  |  |  |  | // | 
					
						
							| 
									
										
										
										
											2020-01-16 17:08:52 -07:00
										 |  |  |  | // is equivalent to a route consisting of: | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | // | 
					
						
							| 
									
										
										
										
											2020-01-09 14:38:59 -07:00
										 |  |  |  | //     @canonicalPath { | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | //         file { | 
					
						
							| 
									
										
										
										
											2019-10-28 15:08:45 -06:00
										 |  |  |  | //             try_files {path}/index.php | 
					
						
							|  |  |  |  | //         } | 
					
						
							|  |  |  |  | //         not { | 
					
						
							|  |  |  |  | //             path */ | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | //         } | 
					
						
							|  |  |  |  | //     } | 
					
						
							| 
									
										
										
										
											2020-01-09 14:38:59 -07:00
										 |  |  |  | //     redir @canonicalPath {path}/ 308 | 
					
						
							| 
									
										
										
										
											2019-10-28 15:08:45 -06:00
										 |  |  |  | // | 
					
						
							|  |  |  |  | //     try_files {path} {path}/index.php index.php | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | // | 
					
						
							| 
									
										
										
										
											2020-01-09 14:38:59 -07:00
										 |  |  |  | //     @phpFiles { | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | //         path *.php | 
					
						
							|  |  |  |  | //     } | 
					
						
							| 
									
										
										
										
											2020-01-09 14:38:59 -07:00
										 |  |  |  | //     reverse_proxy @phpFiles localhost:7777 { | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | //         transport fastcgi { | 
					
						
							|  |  |  |  | //             split .php | 
					
						
							|  |  |  |  | //         } | 
					
						
							|  |  |  |  | //     } | 
					
						
							|  |  |  |  | // | 
					
						
							| 
									
										
										
										
											2020-01-16 17:08:52 -07:00
										 |  |  |  | // Thus, this directive produces multiple handlers, each with a different | 
					
						
							|  |  |  |  | // matcher because multiple consecutive hgandlers are necessary to support | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | // the common PHP use case. If this "common" config is not compatible | 
					
						
							| 
									
										
										
										
											2019-10-28 15:08:45 -06:00
										 |  |  |  | // with a user's PHP requirements, they can use a manual approach based | 
					
						
							|  |  |  |  | // on the example above to configure it precisely as they need. | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | // | 
					
						
							|  |  |  |  | // If a matcher is specified by the user, for example: | 
					
						
							|  |  |  |  | // | 
					
						
							|  |  |  |  | //     php_fastcgi /subpath localhost:7777 | 
					
						
							|  |  |  |  | // | 
					
						
							| 
									
										
										
										
											2020-01-16 17:08:52 -07:00
										 |  |  |  | // then the resulting handlers are wrapped in a subroute that uses the | 
					
						
							| 
									
										
										
										
											2019-12-17 10:14:04 -07:00
										 |  |  |  | // user's matcher as a prerequisite to enter the subroute. In other | 
					
						
							|  |  |  |  | // words, the directive's matcher is necessary, but not sufficient. | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { | 
					
						
							|  |  |  |  | 	if !h.Next() { | 
					
						
							|  |  |  |  | 		return nil, h.ArgErr() | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 	// route to redirect to canonical path if index PHP file | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 	redirMatcherSet := caddy.ModuleMap{ | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 		"file": h.JSON(fileserver.MatchFile{ | 
					
						
							|  |  |  |  | 			TryFiles: []string{"{http.request.uri.path}/index.php"}, | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  |  | 		}), | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 		"not": h.JSON(caddyhttp.MatchNegate{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 			MatchersRaw: caddy.ModuleMap{ | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  |  | 				"path": h.JSON(caddyhttp.MatchPath{"*/"}), | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  |  | 		}), | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 	redirHandler := caddyhttp.StaticResponse{ | 
					
						
							|  |  |  |  | 		StatusCode: caddyhttp.WeakString("308"), | 
					
						
							|  |  |  |  | 		Headers:    http.Header{"Location": []string{"{http.request.uri.path}/"}}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	redirRoute := caddyhttp.Route{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 		MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet}, | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	// route to rewrite to PHP index file | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 	rewriteMatcherSet := caddy.ModuleMap{ | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 		"file": h.JSON(fileserver.MatchFile{ | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 			TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php", "index.php"}, | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  |  | 		}), | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 	rewriteHandler := rewrite.Rewrite{ | 
					
						
							| 
									
										
										
										
											2020-01-11 13:47:42 -07:00
										 |  |  |  | 		URI: "{http.matchers.file.relative}", | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 	rewriteRoute := caddyhttp.Route{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 		MatcherSetsRaw: []caddy.ModuleMap{rewriteMatcherSet}, | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// route to actually reverse proxy requests to PHP files; | 
					
						
							|  |  |  |  | 	// match only requests that are for PHP files | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 	rpMatcherSet := caddy.ModuleMap{ | 
					
						
							| 
									
										
										
										
											2019-12-12 15:27:09 -07:00
										 |  |  |  | 		"path": h.JSON([]string{"*.php"}), | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// if the user specified a matcher token, use that | 
					
						
							|  |  |  |  | 	// matcher in a route that wraps both of our routes; | 
					
						
							|  |  |  |  | 	// either way, strip the matcher token and pass | 
					
						
							|  |  |  |  | 	// the remaining tokens to the unmarshaler so that | 
					
						
							|  |  |  |  | 	// we can gain the rest of the reverse_proxy syntax | 
					
						
							|  |  |  |  | 	userMatcherSet, hasUserMatcher, err := h.MatcherToken() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if hasUserMatcher { | 
					
						
							|  |  |  |  | 		h.Dispenser.Delete() // strip matcher token | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	h.Dispenser.Reset() // pretend this lookahead never happened | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// set up the transport for FastCGI, and specifically PHP | 
					
						
							|  |  |  |  | 	fcgiTransport := Transport{SplitPath: ".php"} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// create the reverse proxy handler which uses our FastCGI transport | 
					
						
							|  |  |  |  | 	rpHandler := &reverseproxy.Handler{ | 
					
						
							|  |  |  |  | 		TransportRaw: caddyconfig.JSONModuleObject(fcgiTransport, "protocol", "fastcgi", nil), | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// the rest of the config is specified by the user | 
					
						
							|  |  |  |  | 	// using the reverse_proxy directive syntax | 
					
						
							|  |  |  |  | 	err = rpHandler.UnmarshalCaddyfile(h.Dispenser) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// create the final reverse proxy route which is | 
					
						
							|  |  |  |  | 	// conditional on matching PHP files | 
					
						
							|  |  |  |  | 	rpRoute := caddyhttp.Route{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 		MatcherSetsRaw: []caddy.ModuleMap{rpMatcherSet}, | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(rpHandler, "handler", "reverse_proxy", nil)}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 17:08:52 -07:00
										 |  |  |  | 	subroute := caddyhttp.Subroute{ | 
					
						
							|  |  |  |  | 		Routes: caddyhttp.RouteList{redirRoute, rewriteRoute, rpRoute}, | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	// the user's matcher is a prerequisite for ours, so | 
					
						
							|  |  |  |  | 	// wrap ours in a subroute and return that | 
					
						
							|  |  |  |  | 	if hasUserMatcher { | 
					
						
							|  |  |  |  | 		return []httpcaddyfile.ConfigValue{ | 
					
						
							|  |  |  |  | 			{ | 
					
						
							|  |  |  |  | 				Class: "route", | 
					
						
							|  |  |  |  | 				Value: caddyhttp.Route{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  |  | 					MatcherSetsRaw: []caddy.ModuleMap{userMatcherSet}, | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 					HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(subroute, "handler", "subroute", nil)}, | 
					
						
							|  |  |  |  | 				}, | 
					
						
							|  |  |  |  | 			}, | 
					
						
							|  |  |  |  | 		}, nil | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-16 17:08:52 -07:00
										 |  |  |  | 	// otherwise, return the literal subroute instead of | 
					
						
							|  |  |  |  | 	// individual routes, to ensure they stay together and | 
					
						
							|  |  |  |  | 	// are treated as a single unit, without necessarily | 
					
						
							|  |  |  |  | 	// creating an actual subroute in the output | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 	return []httpcaddyfile.ConfigValue{ | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  |  | 		{ | 
					
						
							|  |  |  |  | 			Class: "route", | 
					
						
							| 
									
										
										
										
											2020-01-16 17:08:52 -07:00
										 |  |  |  | 			Value: subroute, | 
					
						
							| 
									
										
										
										
											2019-09-10 14:16:41 -06:00
										 |  |  |  | 		}, | 
					
						
							|  |  |  |  | 	}, nil | 
					
						
							|  |  |  |  | } |