mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-10-05 15:38:15 +08:00

* add module (php_server directive) based workers * refactor moduleID to uintptr for faster comparisons * let workers inherit environment variables and root from php_server * caddy can shift FrankenPHPModules in memory for some godforsaken reason, can't rely on them staying the same * remove debugging statement * fix tests * refactor moduleID to uint64 for faster comparisons * actually allow multiple workers per script filename * remove logging * utility function * reuse existing worker with same filename and environment when calling newWorker with a filepath that already has a suitable worker, simply add number of threads * no cleanup happens between tests, so restore old global worker overwriting logic * add test, use getWorker(ForContext) function in frankenphp.go as well * bring error on second global worker with the same filename again * refactor to using name instead of moduleID * nicer name * nicer name * add more tests * remove test case already covered by previous test * revert back to single variable, moduleIDs no longer relevant * update comment * figure out the worker to use in FrankenPHPModule::ServeHTTP * add caddy/config_tests, add --retry 5 to download * add caddy/config_tests * sum up logic a bit, put worker thread addition into moduleWorkers parsing, before workers are actually created * implement suggestions as far as possible * fixup * remove tags * feat: download the mostly static binary when possible (#1467) * feat: download the mostly static binary when possible * cs * docs: remove wildcard matcher from root directive (#1513) * docs: update README with additional documentation links Add link to classic mode, efficiently serving large static files and monitoring FrankenPHP Signed-off-by: Romain Bastide <romain.bastide@orange.com> * ci: combine dependabot updates for one group to 1 pull-request * feat: compatibility with libphp.dylib on macOS * feat: upgrade to Caddy 2.10 * feat: upgrade to Caddy 2.10 * chore: run prettier * fix: build-static.sh consecutive builds (#1496) * fix consecutive builds * use minor version in PHP_VERSION * install jq in centos container * fix "arm64" download arch for spc binary * jq is not available as a rpm download * linter * specify php 8.4 default specify 8.4 so we manually switch to 8.5 when we make sure it works allows to run without jq installed * Apply suggestions from code review Co-authored-by: Kévin Dunglas <kevin@dunglas.fr> --------- Co-authored-by: Kévin Dunglas <kevin@dunglas.fr> * chore: update Go and toolchain version (#1526) * apply suggestions one be one - scriptpath only * generate unique worker names by filename and number * support worker config from embedded apps * rename back to make sure we don't accidentally add FrankenPHPApp workers to the slice * fix test after changing error message * use 🧩 for module workers * use 🌍 for global workers :) * revert1c414cebbc
* revert4cc8893ced
* apply suggestions * add dynamic config loading test of module worker * fix test * minor changes --------- Signed-off-by: Romain Bastide <romain.bastide@orange.com> Co-authored-by: Kévin Dunglas <kevin@dunglas.fr> Co-authored-by: Indra Gunawan <hello@indra.my.id> Co-authored-by: Romain Bastide <romain.bastide@orange.com>
135 lines
3.6 KiB
Go
135 lines
3.6 KiB
Go
package frankenphp
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"path/filepath"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/dunglas/frankenphp/internal/fastabs"
|
|
)
|
|
|
|
// RequestOption instances allow to configure a FrankenPHP Request.
|
|
type RequestOption func(h *frankenPHPContext) error
|
|
|
|
var (
|
|
documentRootCache sync.Map
|
|
documentRootCacheLen atomic.Uint32
|
|
)
|
|
|
|
// WithRequestDocumentRoot sets the root directory of the PHP application.
|
|
// if resolveSymlink is true, oath declared as root directory will be resolved
|
|
// to its absolute value after the evaluation of any symbolic links.
|
|
// Due to the nature of PHP opcache, root directory path is cached: when
|
|
// using a symlinked directory as root this could generate errors when
|
|
// symlink is changed without PHP being restarted; enabling this
|
|
// directive will set $_SERVER['DOCUMENT_ROOT'] to the real directory path.
|
|
func WithRequestDocumentRoot(documentRoot string, resolveSymlink bool) RequestOption {
|
|
return func(o *frankenPHPContext) (err error) {
|
|
v, ok := documentRootCache.Load(documentRoot)
|
|
if !ok {
|
|
// make sure file root is absolute
|
|
v, err = fastabs.FastAbs(documentRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// prevent the cache to grow forever, this is a totally arbitrary value
|
|
if documentRootCacheLen.Load() < 1024 {
|
|
documentRootCache.LoadOrStore(documentRoot, v)
|
|
documentRootCacheLen.Add(1)
|
|
}
|
|
}
|
|
|
|
if resolveSymlink {
|
|
if v, err = filepath.EvalSymlinks(v.(string)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
o.documentRoot = v.(string)
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithRequestResolvedDocumentRoot is similar to WithRequestDocumentRoot
|
|
// but doesn't do 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
|
|
}
|
|
}
|
|
|
|
// WithRequestSplitPath contains a list of split path strings.
|
|
//
|
|
// 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
|
|
// PATH_INFO for the CGI script to use.
|
|
//
|
|
// Future enhancements should be careful to avoid CVE-2019-11043,
|
|
// which can be mitigated with use of a try_files-like behavior
|
|
// that 404s if the FastCGI path info is not found.
|
|
func WithRequestSplitPath(splitPath []string) RequestOption {
|
|
return func(o *frankenPHPContext) error {
|
|
o.splitPath = splitPath
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type PreparedEnv = map[string]string
|
|
|
|
func PrepareEnv(env map[string]string) PreparedEnv {
|
|
preparedEnv := make(PreparedEnv, len(env))
|
|
for k, v := range env {
|
|
preparedEnv[k+"\x00"] = v
|
|
}
|
|
|
|
return preparedEnv
|
|
}
|
|
|
|
// WithRequestEnv set CGI-like environment variables that will be available in $_SERVER.
|
|
// Values set with WithEnv always have priority over automatically populated values.
|
|
func WithRequestEnv(env map[string]string) RequestOption {
|
|
return WithRequestPreparedEnv(PrepareEnv(env))
|
|
}
|
|
|
|
func WithRequestPreparedEnv(env PreparedEnv) RequestOption {
|
|
return func(o *frankenPHPContext) error {
|
|
o.env = env
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithOriginalRequest(r *http.Request) RequestOption {
|
|
return func(o *frankenPHPContext) error {
|
|
o.originalRequest = r
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithRequestLogger sets the logger associated with the current request
|
|
func WithRequestLogger(logger *slog.Logger) RequestOption {
|
|
return func(o *frankenPHPContext) error {
|
|
o.logger = logger
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// WithWorkerName sets the worker that should handle the request
|
|
func WithWorkerName(name string) RequestOption {
|
|
return func(o *frankenPHPContext) error {
|
|
o.workerName = name
|
|
|
|
return nil
|
|
}
|
|
}
|