Created
July 20, 2018 16:32
-
-
Save ijt/dfb2e87087089380f4164ab27991af7c to your computer and use it in GitHub Desktop.
Repro case for gofast issue 28
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This program spins up php-fpm in the background and a Go web server sending | |
// all requests to a PHP router script. | |
package main | |
import ( | |
"context" | |
"flag" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"os/exec" | |
"path/filepath" | |
"syscall" | |
"text/template" | |
"github.com/gorilla/handlers" | |
"github.com/yookoala/gofast" | |
"golang.org/x/sync/errgroup" | |
) | |
func main() { | |
phpFpm := flag.String("php-fpm", "php-fpm", "name or path of PHP fastCGI process manager") | |
port := flag.Int("port", 8080, "port which to listen") | |
logAccess := flag.Bool("emit-access-logs", false, "whether to turn on access logs") | |
router := "index.php" | |
numWorkers := 1 | |
ctx, cancel := context.WithCancel(context.Background()) | |
status := 0 | |
log.Printf("Listening on port %d", *port) | |
if err := serve(ctx, numWorkers, router, *port, *phpFpm, *logAccess); err != nil { | |
fmt.Fprintf(os.Stderr, "serve failed: %v\n", err) | |
status = 1 | |
} | |
cancel() | |
os.Exit(status) | |
} | |
// serve configures php-fpm to have the given number of workers and a web | |
// server to talk to php-fpm over a socket, and spawns php-fpm along with the | |
// web server. | |
func serve(ctx context.Context, numWorkers int, router string, port int, phpFpm string, logAccess bool) error { | |
g, ctx := errgroup.WithContext(ctx) | |
if _, err := os.Stat(router); err != nil { | |
return fmt.Errorf("router %s not found", router) | |
} | |
// Make a temp directory to put config files in. | |
confDir, err := ioutil.TempDir("/tmp", "serve-") | |
if err != nil { | |
return fmt.Errorf("creating temp dir: %v", err) | |
} | |
// Start php-fpm. | |
fifoPath := filepath.Join(confDir, "php-fpm.sock") | |
cmd, err := startPhpFpm(ctx, phpFpm, numWorkers, confDir, fifoPath) | |
if err != nil { | |
return fmt.Errorf("starting php-fpm: %v", err) | |
} | |
g.Go(func() error { return cmd.Wait() }) | |
// Start the web server, forwarding requests to php-fpm via fastCGI. | |
cf := gofast.SimpleConnFactory("unix", fifoPath) | |
var h http.Handler = gofast.NewHandler( | |
gofast.NewFileEndpoint(router)(gofast.BasicSession), | |
gofast.SimpleClientFactory(cf, 0), | |
) | |
if logAccess { | |
h = handlers.LoggingHandler(os.Stdout, h) | |
} | |
s := &http.Server{ | |
Addr: fmt.Sprintf(":%d", port), | |
Handler: handlers.ProxyHeaders(h), | |
} | |
g.Go(func() error { return s.ListenAndServe() }) | |
defer s.Shutdown(ctx) | |
return g.Wait() | |
} | |
// startPhpFpm starts php-fpm listening to the unix socket at fifoPath. | |
func startPhpFpm(ctx context.Context, phpFpm string, numWorkers int, confDir string, fifoPath string) (*exec.Cmd, error) { | |
pidPath := filepath.Join(confDir, "php-fpm.pid") | |
phpFpmConfPath := filepath.Join(confDir, "php-fpm.conf") | |
phpFpmConf, err := os.Create(phpFpmConfPath) | |
if err != nil { | |
return nil, fmt.Errorf("creating php-fpm config file: %v", err) | |
} | |
args := map[string]string{ | |
"fifo": fifoPath, | |
"workers": fmt.Sprintf("%d", numWorkers), | |
"pidpath": pidPath, | |
} | |
if err := phpFpmTemplate.Execute(phpFpmConf, args); err != nil { | |
return nil, fmt.Errorf("writing php-fpm config file: %v", err) | |
} | |
if err := phpFpmConf.Close(); err != nil { | |
return nil, fmt.Errorf("closing php-fpm config file: %v", err) | |
} | |
cmd := exec.CommandContext(ctx, phpFpm, "-R", "--nodaemonize", "--fpm-config", phpFpmConfPath) | |
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} | |
// Start php-fpm in the background. | |
cmd.Stdout = os.Stdout | |
cmd.Stderr = os.Stderr | |
if err := cmd.Start(); err != nil { | |
return nil, err | |
} | |
return cmd, nil | |
} | |
var phpFpmTemplate = template.Must(template.New("phpfpm").Parse(` | |
; Send errors to stderr. | |
error_log = /proc/self/fd/2 | |
log_level = warning | |
pid = {{.pidpath}} | |
; Pool configuration | |
[app] | |
; The address on which to accept FastCGI requests | |
listen = {{.fifo}} | |
; Create child processes with a static policy. | |
pm = static | |
; The number of child processes to be created | |
pm.max_children = {{.workers}} | |
; Keep the environment variables of the parent. | |
clear_env = no | |
catch_workers_output = yes | |
`)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment