diff --git a/caddy/caddy.go b/caddy/caddy.go index 493ab585..958fac38 100644 --- a/caddy/caddy.go +++ b/caddy/caddy.go @@ -6,9 +6,11 @@ package caddy import ( "encoding/json" "errors" + "fmt" "net/http" "path/filepath" "strconv" + "strings" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig" @@ -212,8 +214,10 @@ type FrankenPHPModule struct { // Env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables. Env map[string]string `json:"env,omitempty"` - preparedEnv frankenphp.PreparedEnv - logger *zap.Logger + resolvedDocumentRoot string + preparedEnv frankenphp.PreparedEnv + preparedEnvNeedsReplacement bool + logger *zap.Logger } // CaddyModule returns the Caddy module information. @@ -251,30 +255,69 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error { f.ResolveRootSymlink = &rrs } + if !needReplacement(f.Root) { + root, err := filepath.Abs(f.Root) + if err != nil { + return fmt.Errorf("unable to make the root path absolute: %w", err) + } + f.resolvedDocumentRoot = root + + if *f.ResolveRootSymlink { + root, err := filepath.EvalSymlinks(root) + if err != nil { + return fmt.Errorf("unable to resolve root symlink: %w", err) + } + + f.resolvedDocumentRoot = root + } + } + if f.preparedEnv == nil { f.preparedEnv = frankenphp.PrepareEnv(f.Env) + + for e := range f.preparedEnv { + if needReplacement(e) { + f.preparedEnvNeedsReplacement = true + + break + } + } } return nil } +// needReplacement checks if a string contains placeholdes. +func needReplacement(s string) bool { + return strings.Contains(s, "{") || strings.Contains(s, "}") +} + // ServeHTTP implements caddyhttp.MiddlewareHandler. // TODO: Expose TLS versions as env vars, as Apache's mod_ssl: https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go#L298 func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error { origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request) repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) - documentRoot := repl.ReplaceKnown(f.Root, "") + var documentRootOption frankenphp.RequestOption + if f.resolvedDocumentRoot == "" { + documentRootOption = frankenphp.WithRequestDocumentRoot(repl.ReplaceKnown(f.Root, ""), *f.ResolveRootSymlink) + } else { + documentRootOption = frankenphp.WithRequestResolvedDocumentRoot(f.resolvedDocumentRoot) + } env := make(map[string]string, len(f.preparedEnv)+1) env["REQUEST_URI\x00"] = origReq.URL.RequestURI() for k, v := range f.preparedEnv { - env[k] = repl.ReplaceKnown(v, "") + if f.preparedEnvNeedsReplacement { + env[k] = repl.ReplaceKnown(v, "") + } else { + env[k] = v + } } fr, err := frankenphp.NewRequestWithContext( r, - frankenphp.WithRequestDocumentRoot(documentRoot, *f.ResolveRootSymlink), + documentRootOption, frankenphp.WithRequestSplitPath(f.SplitPath), frankenphp.WithRequestPreparedEnv(env), ) diff --git a/request_options.go b/request_options.go index 6467c15c..a369fd9c 100644 --- a/request_options.go +++ b/request_options.go @@ -36,6 +36,16 @@ func WithRequestDocumentRoot(documentRoot string, resolveSymlink bool) RequestOp } } +// WithRequestResolvedDocumentRoot is similar to WithRequestDocumentRoot +// but doesn't does any checks or resolving on the path to improve performance. +func WithRequestResolvedDocumentRoot(documentRoot string) RequestOption { + return func(o *FrankenPHPContext) error { + o.documentRoot = documentRoot + + return nil + } +} + // The path in the URL will be split into two, with the first piece ending // with the value of SplitPath. The first piece will be assumed as the // actual resource (CGI script) name, and the second piece will be set to