Skip to content

Commit a165c4e

Browse files
committed
add module (php_server directive) based workers
1 parent 8092f4a commit a165c4e

6 files changed

Lines changed: 116 additions & 9 deletions

File tree

caddy/caddy.go

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const defaultDocumentRoot = "public"
3030

3131
var iniError = errors.New("'php_ini' must be in the format: php_ini \"<key>\" \"<value>\"")
3232

33+
// moduleWorkers is a package-level variable to store workers that can be accessed by both FrankenPHPModule and FrankenPHPApp
34+
var moduleWorkers []workerConfig
35+
3336
func init() {
3437
caddy.RegisterModule(FrankenPHPApp{})
3538
caddy.RegisterModule(FrankenPHPModule{})
@@ -55,6 +58,8 @@ type workerConfig struct {
5558
Env map[string]string `json:"env,omitempty"`
5659
// Directories to watch for file changes
5760
Watch []string `json:"watch,omitempty"`
61+
// ModuleID identifies which module created this worker
62+
ModuleID string `json:"module_id,omitempty"`
5863
}
5964

6065
type FrankenPHPApp struct {
@@ -84,6 +89,7 @@ func (f FrankenPHPApp) CaddyModule() caddy.ModuleInfo {
8489
// Provision sets up the module.
8590
func (f *FrankenPHPApp) Provision(ctx caddy.Context) error {
8691
f.logger = ctx.Logger()
92+
f.logger.Info("FrankenPHPApp provisioning 🐘")
8793

8894
if httpApp, err := ctx.AppIfConfigured("http"); err == nil {
8995
if httpApp.(*caddyhttp.App).Metrics != nil {
@@ -108,8 +114,14 @@ func (f *FrankenPHPApp) Start() error {
108114
frankenphp.WithPhpIni(f.PhpIni),
109115
frankenphp.WithMaxWaitTime(f.MaxWaitTime),
110116
}
117+
// Add workers from FrankenPHPApp configuration
111118
for _, w := range f.Workers {
112-
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch))
119+
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch, w.ModuleID))
120+
}
121+
122+
// Add workers from shared location (added by FrankenPHPModule)
123+
for _, w := range moduleWorkers {
124+
opts = append(opts, frankenphp.WithWorkers(w.Name, repl.ReplaceKnown(w.FileName, ""), w.Num, w.Env, w.Watch, w.ModuleID))
113125
}
114126

115127
frankenphp.Shutdown()
@@ -135,6 +147,9 @@ func (f *FrankenPHPApp) Stop() error {
135147
f.NumThreads = 0
136148
f.MaxWaitTime = 0
137149

150+
// reset shared workers
151+
moduleWorkers = nil
152+
138153
return nil
139154
}
140155

@@ -341,6 +356,8 @@ type FrankenPHPModule struct {
341356
ResolveRootSymlink *bool `json:"resolve_root_symlink,omitempty"`
342357
// Env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
343358
Env map[string]string `json:"env,omitempty"`
359+
// Workers configures the worker scripts to start.
360+
Workers []workerConfig `json:"workers,omitempty"`
344361

345362
resolvedDocumentRoot string
346363
preparedEnv frankenphp.PreparedEnv
@@ -359,6 +376,7 @@ func (FrankenPHPModule) CaddyModule() caddy.ModuleInfo {
359376
// Provision sets up the module.
360377
func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
361378
f.logger = ctx.Logger()
379+
f.logger.Info("FrankenPHPModule provisioning 🐘")
362380

363381
if f.Root == "" {
364382
if frankenphp.EmbeddedAppPath == "" {
@@ -412,6 +430,15 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
412430
}
413431
}
414432

433+
if len(f.Workers) > 0 {
434+
// Tag workers with a unique module ID based on the module's memory address
435+
moduleID := fmt.Sprintf("%p", f)
436+
for i := range f.Workers {
437+
f.Workers[i].ModuleID = moduleID
438+
}
439+
moduleWorkers = append(moduleWorkers, f.Workers...)
440+
}
441+
415442
return nil
416443
}
417444

@@ -422,7 +449,7 @@ func needReplacement(s string) bool {
422449

423450
// ServeHTTP implements caddyhttp.MiddlewareHandler.
424451
// 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
425-
func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
452+
func (f *FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error {
426453
origReq := r.Context().Value(caddyhttp.OriginalRequestCtxKey).(http.Request)
427454
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
428455

@@ -447,6 +474,7 @@ func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ ca
447474
frankenphp.WithRequestSplitPath(f.SplitPath),
448475
frankenphp.WithRequestPreparedEnv(env),
449476
frankenphp.WithOriginalRequest(&origReq),
477+
frankenphp.WithModuleID(fmt.Sprintf("%p", f)),
450478
)
451479

452480
if err != nil {
@@ -466,6 +494,66 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
466494
for d.Next() {
467495
for d.NextBlock(0) {
468496
switch d.Val() {
497+
case "worker":
498+
wc := workerConfig{}
499+
if d.NextArg() {
500+
wc.FileName = d.Val()
501+
}
502+
503+
if d.NextArg() {
504+
v, err := strconv.Atoi(d.Val())
505+
if err != nil {
506+
return err
507+
}
508+
wc.Num = v
509+
}
510+
511+
for d.NextBlock(1) {
512+
switch d.Val() {
513+
case "name":
514+
if !d.NextArg() {
515+
return d.ArgErr()
516+
}
517+
wc.Name = d.Val()
518+
case "file":
519+
if !d.NextArg() {
520+
return d.ArgErr()
521+
}
522+
wc.FileName = d.Val()
523+
case "num":
524+
if !d.NextArg() {
525+
return d.ArgErr()
526+
}
527+
v, err := strconv.Atoi(d.Val())
528+
if err != nil {
529+
return err
530+
}
531+
wc.Num = v
532+
case "env":
533+
args := d.RemainingArgs()
534+
if len(args) != 2 {
535+
return d.ArgErr()
536+
}
537+
if wc.Env == nil {
538+
wc.Env = make(map[string]string)
539+
}
540+
wc.Env[args[0]] = args[1]
541+
case "watch":
542+
if !d.NextArg() {
543+
wc.Watch = append(wc.Watch, "./**/*.{php,yaml,yml,twig,env}")
544+
} else {
545+
wc.Watch = append(wc.Watch, d.Val())
546+
}
547+
default:
548+
return fmt.Errorf("unknown worker subdirective: %s", d.Val())
549+
}
550+
}
551+
552+
if wc.FileName == "" {
553+
return errors.New(`the "file" argument must be specified`)
554+
}
555+
556+
f.Workers = append(f.Workers, wc)
469557
case "root":
470558
if !d.NextArg() {
471559
return d.ArgErr()
@@ -505,7 +593,7 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
505593

506594
f.ResolveRootSymlink = &v
507595
default:
508-
allowedDirectives := "root, split, env, resolve_root_symlink"
596+
allowedDirectives := "root, split, env, resolve_root_symlink, worker"
509597
return wrongSubDirectiveError("php or php_server", allowedDirectives, d.Val())
510598
}
511599
}
@@ -516,7 +604,7 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
516604

517605
// parseCaddyfile unmarshals tokens from h into a new Middleware.
518606
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
519-
m := FrankenPHPModule{}
607+
m := &FrankenPHPModule{}
520608
err := m.UnmarshalCaddyfile(h.Dispenser)
521609

522610
return m, err
@@ -753,6 +841,7 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
753841
// using the php directive syntax
754842
dispenser.Next() // consume the directive name
755843
err = phpsrv.UnmarshalCaddyfile(dispenser)
844+
756845
if err != nil {
757846
return nil, err
758847
}

context.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ type frankenPHPContext struct {
3131

3232
done chan interface{}
3333
startedAt time.Time
34+
35+
// The module ID that created this request
36+
moduleID string
3437
}
3538

3639
// fromContext extracts the frankenPHPContext from a context.

frankenphp.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,11 @@ func ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) error
406406

407407
// Detect if a worker is available to handle this request
408408
if worker, ok := workers[fc.scriptFilename]; ok {
409-
worker.handleRequest(fc)
410-
return nil
409+
// can handle with a global worker, or a module worker from the matching module
410+
if worker.moduleID == "" || worker.moduleID == fc.moduleID {
411+
worker.handleRequest(fc)
412+
return nil
413+
}
411414
}
412415

413416
// If no worker was availabe send the request to non-worker threads

options.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type workerOpt struct {
2828
num int
2929
env PreparedEnv
3030
watch []string
31+
moduleID string
3132
}
3233

3334
// WithNumThreads configures the number of PHP threads to start.
@@ -55,10 +56,10 @@ func WithMetrics(m Metrics) Option {
5556
}
5657
}
5758

58-
// WithWorkers configures the PHP workers to start.
59-
func WithWorkers(name string, fileName string, num int, env map[string]string, watch []string) Option {
59+
// WithWorkers configures the PHP workers to start, moduleID is used to identify the worker for a specific domain
60+
func WithWorkers(name string, fileName string, num int, env map[string]string, watch []string, moduleID string) Option {
6061
return func(o *opt) error {
61-
o.workers = append(o.workers, workerOpt{name, fileName, num, PrepareEnv(env), watch})
62+
o.workers = append(o.workers, workerOpt{name, fileName, num, PrepareEnv(env), watch, moduleID})
6263

6364
return nil
6465
}

request_options.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,12 @@ func WithRequestLogger(logger *zap.Logger) RequestOption {
123123
return nil
124124
}
125125
}
126+
127+
// WithModuleID sets the module ID associated with the current request
128+
func WithModuleID(moduleID string) RequestOption {
129+
return func(o *frankenPHPContext) error {
130+
o.moduleID = moduleID
131+
132+
return nil
133+
}
134+
}

worker.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type worker struct {
2020
requestChan chan *frankenPHPContext
2121
threads []*phpThread
2222
threadMutex sync.RWMutex
23+
moduleID string
2324
}
2425

2526
var (
@@ -81,6 +82,7 @@ func newWorker(o workerOpt) (*worker, error) {
8182
num: o.num,
8283
env: o.env,
8384
requestChan: make(chan *frankenPHPContext),
85+
moduleID: o.moduleID,
8486
}
8587
workers[absFileName] = w
8688

0 commit comments

Comments
 (0)