mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-08 11:58:49 +03:00
b8722d9af3
By setting the read deadline in streamReader.Read(), the deadline was extended by the read timeout on each subsequent call. To avoid this, the deadline is set in FCGIClient.Request(), before the first read occurs. See #1094.
188 lines
3.9 KiB
Go
188 lines
3.9 KiB
Go
package fastcgi
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mholt/caddy"
|
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
|
)
|
|
|
|
func init() {
|
|
caddy.RegisterPlugin("fastcgi", caddy.Plugin{
|
|
ServerType: "http",
|
|
Action: setup,
|
|
})
|
|
}
|
|
|
|
// setup configures a new FastCGI middleware instance.
|
|
func setup(c *caddy.Controller) error {
|
|
cfg := httpserver.GetConfig(c)
|
|
absRoot, err := filepath.Abs(cfg.Root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rules, err := fastcgiParse(c)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
|
return Handler{
|
|
Next: next,
|
|
Rules: rules,
|
|
Root: cfg.Root,
|
|
AbsRoot: absRoot,
|
|
FileSys: http.Dir(cfg.Root),
|
|
SoftwareName: caddy.AppName,
|
|
SoftwareVersion: caddy.AppVersion,
|
|
ServerName: cfg.Addr.Host,
|
|
ServerPort: cfg.Addr.Port,
|
|
}
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
|
|
var rules []Rule
|
|
|
|
for c.Next() {
|
|
args := c.RemainingArgs()
|
|
|
|
if len(args) < 2 || len(args) > 3 {
|
|
return rules, c.ArgErr()
|
|
}
|
|
|
|
rule := Rule{Path: args[0], ReadTimeout: 60 * time.Second}
|
|
upstreams := []string{args[1]}
|
|
|
|
if len(args) == 3 {
|
|
if err := fastcgiPreset(args[2], &rule); err != nil {
|
|
return rules, err
|
|
}
|
|
}
|
|
|
|
var err error
|
|
var pool int
|
|
var connectTimeout = 60 * time.Second
|
|
var dialers []dialer
|
|
var poolSize = -1
|
|
|
|
for c.NextBlock() {
|
|
switch c.Val() {
|
|
case "ext":
|
|
if !c.NextArg() {
|
|
return rules, c.ArgErr()
|
|
}
|
|
rule.Ext = c.Val()
|
|
case "split":
|
|
if !c.NextArg() {
|
|
return rules, c.ArgErr()
|
|
}
|
|
rule.SplitPath = c.Val()
|
|
case "index":
|
|
args := c.RemainingArgs()
|
|
if len(args) == 0 {
|
|
return rules, c.ArgErr()
|
|
}
|
|
rule.IndexFiles = args
|
|
|
|
case "upstream":
|
|
args := c.RemainingArgs()
|
|
|
|
if len(args) != 1 {
|
|
return rules, c.ArgErr()
|
|
}
|
|
|
|
upstreams = append(upstreams, args[0])
|
|
case "env":
|
|
envArgs := c.RemainingArgs()
|
|
if len(envArgs) < 2 {
|
|
return rules, c.ArgErr()
|
|
}
|
|
rule.EnvVars = append(rule.EnvVars, [2]string{envArgs[0], envArgs[1]})
|
|
case "except":
|
|
ignoredPaths := c.RemainingArgs()
|
|
if len(ignoredPaths) == 0 {
|
|
return rules, c.ArgErr()
|
|
}
|
|
rule.IgnoredSubPaths = ignoredPaths
|
|
|
|
case "pool":
|
|
if !c.NextArg() {
|
|
return rules, c.ArgErr()
|
|
}
|
|
pool, err = strconv.Atoi(c.Val())
|
|
if err != nil {
|
|
return rules, err
|
|
}
|
|
if pool >= 0 {
|
|
poolSize = pool
|
|
} else {
|
|
return rules, c.Errf("positive integer expected, found %d", pool)
|
|
}
|
|
case "connect_timeout":
|
|
if !c.NextArg() {
|
|
return rules, c.ArgErr()
|
|
}
|
|
connectTimeout, err = time.ParseDuration(c.Val())
|
|
if err != nil {
|
|
return rules, err
|
|
}
|
|
case "read_timeout":
|
|
if !c.NextArg() {
|
|
return rules, c.ArgErr()
|
|
}
|
|
readTimeout, err := time.ParseDuration(c.Val())
|
|
if err != nil {
|
|
return rules, err
|
|
}
|
|
rule.ReadTimeout = readTimeout
|
|
}
|
|
}
|
|
|
|
for _, rawAddress := range upstreams {
|
|
network, address := parseAddress(rawAddress)
|
|
if poolSize >= 0 {
|
|
dialers = append(dialers, &persistentDialer{
|
|
size: poolSize,
|
|
network: network,
|
|
address: address,
|
|
timeout: connectTimeout,
|
|
})
|
|
} else {
|
|
dialers = append(dialers, basicDialer{
|
|
network: network,
|
|
address: address,
|
|
timeout: connectTimeout,
|
|
})
|
|
}
|
|
}
|
|
|
|
rule.dialer = &loadBalancingDialer{dialers: dialers}
|
|
rule.Address = strings.Join(upstreams, ",")
|
|
rules = append(rules, rule)
|
|
}
|
|
|
|
return rules, nil
|
|
}
|
|
|
|
// fastcgiPreset configures rule according to name. It returns an error if
|
|
// name is not a recognized preset name.
|
|
func fastcgiPreset(name string, rule *Rule) error {
|
|
switch name {
|
|
case "php":
|
|
rule.Ext = ".php"
|
|
rule.SplitPath = ".php"
|
|
rule.IndexFiles = []string{"index.php"}
|
|
default:
|
|
return errors.New(name + " is not a valid preset name")
|
|
}
|
|
return nil
|
|
}
|