mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 01:19:20 +08:00 
			
		
		
		
	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
This commit is contained in:
		@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					:8884
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					php_fastcgi localhost:9000 {
 | 
				
			||||||
 | 
					    # some php_fastcgi-specific subdirectives
 | 
				
			||||||
 | 
					    split .php .php5
 | 
				
			||||||
 | 
					    env VAR1 value1
 | 
				
			||||||
 | 
					    env VAR2 value2
 | 
				
			||||||
 | 
					    root /var/www
 | 
				
			||||||
 | 
					    index off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # passed through to reverse_proxy (directive order doesn't matter!)
 | 
				
			||||||
 | 
					    lb_policy random
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					----------
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"apps": {
 | 
				
			||||||
 | 
							"http": {
 | 
				
			||||||
 | 
								"servers": {
 | 
				
			||||||
 | 
									"srv0": {
 | 
				
			||||||
 | 
										"listen": [
 | 
				
			||||||
 | 
											":8884"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"routes": [
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												"match": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"path": [
 | 
				
			||||||
 | 
															"*.php",
 | 
				
			||||||
 | 
															"*.php5"
 | 
				
			||||||
 | 
														]
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												],
 | 
				
			||||||
 | 
												"handle": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"handler": "reverse_proxy",
 | 
				
			||||||
 | 
														"load_balancing": {
 | 
				
			||||||
 | 
															"selection_policy": {
 | 
				
			||||||
 | 
																"policy": "random"
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
														"transport": {
 | 
				
			||||||
 | 
															"env": {
 | 
				
			||||||
 | 
																"VAR1": "value1",
 | 
				
			||||||
 | 
																"VAR2": "value2"
 | 
				
			||||||
 | 
															},
 | 
				
			||||||
 | 
															"protocol": "fastcgi",
 | 
				
			||||||
 | 
															"root": "/var/www",
 | 
				
			||||||
 | 
															"split_path": [
 | 
				
			||||||
 | 
																".php",
 | 
				
			||||||
 | 
																".php5"
 | 
				
			||||||
 | 
															]
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
														"upstreams": [
 | 
				
			||||||
 | 
															{
 | 
				
			||||||
 | 
																"dial": "localhost:9000"
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
														]
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												]
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					:8884
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					php_fastcgi localhost:9000 {
 | 
				
			||||||
 | 
					    # some php_fastcgi-specific subdirectives
 | 
				
			||||||
 | 
					    split .php .php5
 | 
				
			||||||
 | 
					    env VAR1 value1
 | 
				
			||||||
 | 
					    env VAR2 value2
 | 
				
			||||||
 | 
					    root /var/www
 | 
				
			||||||
 | 
					    index index.php5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # passed through to reverse_proxy (directive order doesn't matter!)
 | 
				
			||||||
 | 
					    lb_policy random
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					----------
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"apps": {
 | 
				
			||||||
 | 
							"http": {
 | 
				
			||||||
 | 
								"servers": {
 | 
				
			||||||
 | 
									"srv0": {
 | 
				
			||||||
 | 
										"listen": [
 | 
				
			||||||
 | 
											":8884"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"routes": [
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												"match": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"file": {
 | 
				
			||||||
 | 
															"try_files": [
 | 
				
			||||||
 | 
																"{http.request.uri.path}/index.php5"
 | 
				
			||||||
 | 
															]
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
														"not": [
 | 
				
			||||||
 | 
															{
 | 
				
			||||||
 | 
																"path": [
 | 
				
			||||||
 | 
																	"*/"
 | 
				
			||||||
 | 
																]
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
														]
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												],
 | 
				
			||||||
 | 
												"handle": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"handler": "static_response",
 | 
				
			||||||
 | 
														"headers": {
 | 
				
			||||||
 | 
															"Location": [
 | 
				
			||||||
 | 
																"{http.request.uri.path}/"
 | 
				
			||||||
 | 
															]
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
														"status_code": 308
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												]
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												"match": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"file": {
 | 
				
			||||||
 | 
															"try_files": [
 | 
				
			||||||
 | 
																"{http.request.uri.path}",
 | 
				
			||||||
 | 
																"{http.request.uri.path}/index.php5",
 | 
				
			||||||
 | 
																"index.php5"
 | 
				
			||||||
 | 
															],
 | 
				
			||||||
 | 
															"split_path": [
 | 
				
			||||||
 | 
																".php",
 | 
				
			||||||
 | 
																".php5"
 | 
				
			||||||
 | 
															]
 | 
				
			||||||
 | 
														}
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												],
 | 
				
			||||||
 | 
												"handle": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"handler": "rewrite",
 | 
				
			||||||
 | 
														"uri": "{http.matchers.file.relative}"
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												]
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												"match": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"path": [
 | 
				
			||||||
 | 
															"*.php",
 | 
				
			||||||
 | 
															"*.php5"
 | 
				
			||||||
 | 
														]
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												],
 | 
				
			||||||
 | 
												"handle": [
 | 
				
			||||||
 | 
													{
 | 
				
			||||||
 | 
														"handler": "reverse_proxy",
 | 
				
			||||||
 | 
														"load_balancing": {
 | 
				
			||||||
 | 
															"selection_policy": {
 | 
				
			||||||
 | 
																"policy": "random"
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
														"transport": {
 | 
				
			||||||
 | 
															"env": {
 | 
				
			||||||
 | 
																"VAR1": "value1",
 | 
				
			||||||
 | 
																"VAR2": "value2"
 | 
				
			||||||
 | 
															},
 | 
				
			||||||
 | 
															"protocol": "fastcgi",
 | 
				
			||||||
 | 
															"root": "/var/www",
 | 
				
			||||||
 | 
															"split_path": [
 | 
				
			||||||
 | 
																".php",
 | 
				
			||||||
 | 
																".php5"
 | 
				
			||||||
 | 
															]
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
														"upstreams": [
 | 
				
			||||||
 | 
															{
 | 
				
			||||||
 | 
																"dial": "localhost:9000"
 | 
				
			||||||
 | 
															}
 | 
				
			||||||
 | 
														]
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
 | 
												]
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -123,47 +123,133 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
 | 
				
			|||||||
		return nil, h.ArgErr()
 | 
							return nil, h.ArgErr()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// route to redirect to canonical path if index PHP file
 | 
						// set up the transport for FastCGI, and specifically PHP
 | 
				
			||||||
	redirMatcherSet := caddy.ModuleMap{
 | 
						fcgiTransport := Transport{}
 | 
				
			||||||
		"file": h.JSON(fileserver.MatchFile{
 | 
					
 | 
				
			||||||
			TryFiles: []string{"{http.request.uri.path}/index.php"},
 | 
						// set up the set of file extensions allowed to execute PHP code
 | 
				
			||||||
		}),
 | 
						extensions := []string{".php"}
 | 
				
			||||||
		"not": h.JSON(caddyhttp.MatchNot{
 | 
					
 | 
				
			||||||
			MatcherSetsRaw: []caddy.ModuleMap{
 | 
						// set the default index file for the try_files rewrites
 | 
				
			||||||
				{
 | 
						indexFile := "index.php"
 | 
				
			||||||
					"path": h.JSON(caddyhttp.MatchPath{"*/"}),
 | 
					
 | 
				
			||||||
				},
 | 
						// make a new dispenser from the remaining tokens so that we
 | 
				
			||||||
			},
 | 
						// can reset the dispenser back to this point for the
 | 
				
			||||||
		}),
 | 
						// reverse_proxy unmarshaler to read from it as well
 | 
				
			||||||
	}
 | 
						dispenser := h.NewFromNextSegment()
 | 
				
			||||||
	redirHandler := caddyhttp.StaticResponse{
 | 
					
 | 
				
			||||||
		StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
 | 
						// read the subdirectives that we allow as overrides to
 | 
				
			||||||
		Headers:    http.Header{"Location": []string{"{http.request.uri.path}/"}},
 | 
						// the php_fastcgi shortcut
 | 
				
			||||||
	}
 | 
						// NOTE: we delete the tokens as we go so that the reverse_proxy
 | 
				
			||||||
	redirRoute := caddyhttp.Route{
 | 
						// unmarshal doesn't see these subdirectives which it cannot handle
 | 
				
			||||||
		MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},
 | 
						for dispenser.Next() {
 | 
				
			||||||
		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},
 | 
							for dispenser.NextBlock(0) {
 | 
				
			||||||
 | 
								switch dispenser.Val() {
 | 
				
			||||||
 | 
								case "root":
 | 
				
			||||||
 | 
									if !dispenser.NextArg() {
 | 
				
			||||||
 | 
										return nil, dispenser.ArgErr()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fcgiTransport.Root = dispenser.Val()
 | 
				
			||||||
 | 
									dispenser.Delete()
 | 
				
			||||||
 | 
									dispenser.Delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case "split":
 | 
				
			||||||
 | 
									extensions = dispenser.RemainingArgs()
 | 
				
			||||||
 | 
									dispenser.Delete()
 | 
				
			||||||
 | 
									for range extensions {
 | 
				
			||||||
 | 
										dispenser.Delete()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if len(extensions) == 0 {
 | 
				
			||||||
 | 
										return nil, dispenser.ArgErr()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case "env":
 | 
				
			||||||
 | 
									args := dispenser.RemainingArgs()
 | 
				
			||||||
 | 
									dispenser.Delete()
 | 
				
			||||||
 | 
									for range args {
 | 
				
			||||||
 | 
										dispenser.Delete()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if len(args) != 2 {
 | 
				
			||||||
 | 
										return nil, dispenser.ArgErr()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if fcgiTransport.EnvVars == nil {
 | 
				
			||||||
 | 
										fcgiTransport.EnvVars = make(map[string]string)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									fcgiTransport.EnvVars[args[0]] = args[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case "index":
 | 
				
			||||||
 | 
									args := dispenser.RemainingArgs()
 | 
				
			||||||
 | 
									dispenser.Delete()
 | 
				
			||||||
 | 
									for range args {
 | 
				
			||||||
 | 
										dispenser.Delete()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if len(args) != 1 {
 | 
				
			||||||
 | 
										return nil, dispenser.ArgErr()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									indexFile = args[0]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// route to rewrite to PHP index file
 | 
						// reset the dispenser after we're done so that the reverse_proxy
 | 
				
			||||||
	rewriteMatcherSet := caddy.ModuleMap{
 | 
						// unmarshaler can read it from the start
 | 
				
			||||||
		"file": h.JSON(fileserver.MatchFile{
 | 
						dispenser.Reset()
 | 
				
			||||||
			TryFiles: []string{"{http.request.uri.path}", "{http.request.uri.path}/index.php", "index.php"},
 | 
					
 | 
				
			||||||
			SplitPath: []string{".php"},
 | 
						// set up a route list that we'll append to
 | 
				
			||||||
		}),
 | 
						routes := caddyhttp.RouteList{}
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	rewriteHandler := rewrite.Rewrite{
 | 
						// set the list of allowed path segments on which to split
 | 
				
			||||||
		URI: "{http.matchers.file.relative}",
 | 
						fcgiTransport.SplitPath = extensions
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	rewriteRoute := caddyhttp.Route{
 | 
						// if the index is turned off, we skip the redirect and try_files
 | 
				
			||||||
		MatcherSetsRaw: []caddy.ModuleMap{rewriteMatcherSet},
 | 
						if indexFile != "off" {
 | 
				
			||||||
		HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},
 | 
							// route to redirect to canonical path if index PHP file
 | 
				
			||||||
 | 
							redirMatcherSet := caddy.ModuleMap{
 | 
				
			||||||
 | 
								"file": h.JSON(fileserver.MatchFile{
 | 
				
			||||||
 | 
									TryFiles: []string{"{http.request.uri.path}/" + indexFile},
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
								"not": h.JSON(caddyhttp.MatchNot{
 | 
				
			||||||
 | 
									MatcherSetsRaw: []caddy.ModuleMap{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											"path": h.JSON(caddyhttp.MatchPath{"*/"}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							redirHandler := caddyhttp.StaticResponse{
 | 
				
			||||||
 | 
								StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
 | 
				
			||||||
 | 
								Headers:    http.Header{"Location": []string{"{http.request.uri.path}/"}},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							redirRoute := caddyhttp.Route{
 | 
				
			||||||
 | 
								MatcherSetsRaw: []caddy.ModuleMap{redirMatcherSet},
 | 
				
			||||||
 | 
								HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(redirHandler, "handler", "static_response", nil)},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// route to rewrite to PHP index file
 | 
				
			||||||
 | 
							rewriteMatcherSet := caddy.ModuleMap{
 | 
				
			||||||
 | 
								"file": h.JSON(fileserver.MatchFile{
 | 
				
			||||||
 | 
									TryFiles:  []string{"{http.request.uri.path}", "{http.request.uri.path}/" + indexFile, indexFile},
 | 
				
			||||||
 | 
									SplitPath: extensions,
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rewriteHandler := rewrite.Rewrite{
 | 
				
			||||||
 | 
								URI: "{http.matchers.file.relative}",
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							rewriteRoute := caddyhttp.Route{
 | 
				
			||||||
 | 
								MatcherSetsRaw: []caddy.ModuleMap{rewriteMatcherSet},
 | 
				
			||||||
 | 
								HandlersRaw:    []json.RawMessage{caddyconfig.JSONModuleObject(rewriteHandler, "handler", "rewrite", nil)},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							routes = append(routes, redirRoute, rewriteRoute)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// route to actually reverse proxy requests to PHP files;
 | 
						// route to actually reverse proxy requests to PHP files;
 | 
				
			||||||
	// match only requests that are for PHP files
 | 
						// match only requests that are for PHP files
 | 
				
			||||||
 | 
						pathList := []string{}
 | 
				
			||||||
 | 
						for _, ext := range extensions {
 | 
				
			||||||
 | 
							pathList = append(pathList, "*"+ext)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	rpMatcherSet := caddy.ModuleMap{
 | 
						rpMatcherSet := caddy.ModuleMap{
 | 
				
			||||||
		"path": h.JSON([]string{"*.php"}),
 | 
							"path": h.JSON(pathList),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// if the user specified a matcher token, use that
 | 
						// if the user specified a matcher token, use that
 | 
				
			||||||
@ -176,9 +262,6 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// set up the transport for FastCGI, and specifically PHP
 | 
					 | 
				
			||||||
	fcgiTransport := Transport{SplitPath: []string{".php"}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create the reverse proxy handler which uses our FastCGI transport
 | 
						// create the reverse proxy handler which uses our FastCGI transport
 | 
				
			||||||
	rpHandler := &reverseproxy.Handler{
 | 
						rpHandler := &reverseproxy.Handler{
 | 
				
			||||||
		TransportRaw: caddyconfig.JSONModuleObject(fcgiTransport, "protocol", "fastcgi", nil),
 | 
							TransportRaw: caddyconfig.JSONModuleObject(fcgiTransport, "protocol", "fastcgi", nil),
 | 
				
			||||||
@ -188,7 +271,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
 | 
				
			|||||||
	// using the reverse_proxy directive syntax
 | 
						// using the reverse_proxy directive syntax
 | 
				
			||||||
	// TODO: this can overwrite our fcgiTransport that we encoded and
 | 
						// TODO: this can overwrite our fcgiTransport that we encoded and
 | 
				
			||||||
	// set on the rpHandler... even with a non-fastcgi transport!
 | 
						// set on the rpHandler... even with a non-fastcgi transport!
 | 
				
			||||||
	err = rpHandler.UnmarshalCaddyfile(h.Dispenser)
 | 
						err = rpHandler.UnmarshalCaddyfile(dispenser)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -201,7 +284,7 @@ func parsePHPFastCGI(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	subroute := caddyhttp.Subroute{
 | 
						subroute := caddyhttp.Subroute{
 | 
				
			||||||
		Routes: caddyhttp.RouteList{redirRoute, rewriteRoute, rpRoute},
 | 
							Routes: append(routes, rpRoute),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// the user's matcher is a prerequisite for ours, so
 | 
						// the user's matcher is a prerequisite for ours, so
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user