mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-12-24 13:38:11 +08:00
perf: cache document root resolution when possible
This commit is contained in:
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user