Commit 76f12f47 authored by ericdreeves's avatar ericdreeves Committed by GitHub

Merge pull request #1274 from mholt/fastcgi-timeout-defaults

Fix read timeout and add default timeout values.
parents 80eb45fc 32fa0ce6
...@@ -332,9 +332,6 @@ func TestReadTimeout(t *testing.T) { ...@@ -332,9 +332,6 @@ func TestReadTimeout(t *testing.T) {
t.Fatalf("Unable to create listener for test: %v", err) t.Fatalf("Unable to create listener for test: %v", err)
} }
defer listener.Close() defer listener.Close()
go fcgi.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Second * 1)
}))
network, address := parseAddress(listener.Addr().String()) network, address := parseAddress(listener.Addr().String())
handler := Handler{ handler := Handler{
...@@ -354,6 +351,10 @@ func TestReadTimeout(t *testing.T) { ...@@ -354,6 +351,10 @@ func TestReadTimeout(t *testing.T) {
} }
w := httptest.NewRecorder() w := httptest.NewRecorder()
go fcgi.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Millisecond * 130)
}))
_, err = handler.ServeHTTP(w, r) _, err = handler.ServeHTTP(w, r)
if err == nil { if err == nil {
t.Error("Expected i/o timeout error but had none") t.Error("Expected i/o timeout error but had none")
......
...@@ -212,6 +212,21 @@ func (c *FCGIClient) Close() error { ...@@ -212,6 +212,21 @@ func (c *FCGIClient) Close() error {
return c.rwc.Close() return c.rwc.Close()
} }
// setReadDeadline sets a read deadline on FCGIClient based on the configured
// readTimeout. A zero value for readTimeout means no deadline will be set.
func (c *FCGIClient) setReadDeadline() error {
if c.readTimeout > 0 {
conn, ok := c.rwc.(net.Conn)
if ok {
conn.SetReadDeadline(time.Now().Add(c.readTimeout))
} else {
return fmt.Errorf("Could not set Client ReadTimeout")
}
}
return nil
}
func (c *FCGIClient) writeRecord(recType uint8, content []byte) error { func (c *FCGIClient) writeRecord(recType uint8, content []byte) error {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
...@@ -350,20 +365,10 @@ func (w *streamReader) Read(p []byte) (n int, err error) { ...@@ -350,20 +365,10 @@ func (w *streamReader) Read(p []byte) (n int, err error) {
if len(p) > 0 { if len(p) > 0 {
if len(w.buf) == 0 { if len(w.buf) == 0 {
// filter outputs for error log // filter outputs for error log
for { for {
rec := &record{} rec := &record{}
var buf []byte var buf []byte
if readTimeout := w.c.ReadTimeout(); readTimeout > 0 {
conn, ok := w.c.rwc.(net.Conn)
if ok {
conn.SetReadDeadline(time.Now().Add(readTimeout))
} else {
err = fmt.Errorf("Could not set Client ReadTimeout")
return
}
}
buf, err = rec.read(w.c.rwc) buf, err = rec.read(w.c.rwc)
if err == errInvalidHeaderVersion { if err == errInvalidHeaderVersion {
continue continue
...@@ -441,6 +446,10 @@ func (c *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Res ...@@ -441,6 +446,10 @@ func (c *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Res
tp := textproto.NewReader(rb) tp := textproto.NewReader(rb)
resp = new(http.Response) resp = new(http.Response)
if err = c.setReadDeadline(); err != nil {
return
}
// Parse the response headers. // Parse the response headers.
mimeHeader, err := tp.ReadMIMEHeader() mimeHeader, err := tp.ReadMIMEHeader()
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
......
...@@ -53,15 +53,13 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) { ...@@ -53,15 +53,13 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
var rules []Rule var rules []Rule
for c.Next() { for c.Next() {
var rule Rule
args := c.RemainingArgs() args := c.RemainingArgs()
if len(args) < 2 || len(args) > 3 { if len(args) < 2 || len(args) > 3 {
return rules, c.ArgErr() return rules, c.ArgErr()
} }
rule.Path = args[0] rule := Rule{Path: args[0], ReadTimeout: 60 * time.Second}
upstreams := []string{args[1]} upstreams := []string{args[1]}
if len(args) == 3 { if len(args) == 3 {
...@@ -72,7 +70,7 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) { ...@@ -72,7 +70,7 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
var err error var err error
var pool int var pool int
var timeout time.Duration var connectTimeout = 60 * time.Second
var dialers []dialer var dialers []dialer
var poolSize = -1 var poolSize = -1
...@@ -133,7 +131,7 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) { ...@@ -133,7 +131,7 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
if !c.NextArg() { if !c.NextArg() {
return rules, c.ArgErr() return rules, c.ArgErr()
} }
timeout, err = time.ParseDuration(c.Val()) connectTimeout, err = time.ParseDuration(c.Val())
if err != nil { if err != nil {
return rules, err return rules, err
} }
...@@ -152,9 +150,18 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) { ...@@ -152,9 +150,18 @@ func fastcgiParse(c *caddy.Controller) ([]Rule, error) {
for _, rawAddress := range upstreams { for _, rawAddress := range upstreams {
network, address := parseAddress(rawAddress) network, address := parseAddress(rawAddress)
if poolSize >= 0 { if poolSize >= 0 {
dialers = append(dialers, &persistentDialer{size: poolSize, network: network, address: address, timeout: timeout}) dialers = append(dialers, &persistentDialer{
size: poolSize,
network: network,
address: address,
timeout: connectTimeout,
})
} else { } else {
dialers = append(dialers, basicDialer{network: network, address: address, timeout: timeout}) dialers = append(dialers, basicDialer{
network: network,
address: address,
timeout: connectTimeout,
})
} }
} }
......
...@@ -77,8 +77,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -77,8 +77,9 @@ func TestFastcgiParse(t *testing.T) {
Address: "127.0.0.1:9000", Address: "127.0.0.1:9000",
Ext: ".php", Ext: ".php",
SplitPath: ".php", SplitPath: ".php",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000"}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000", timeout: 60 * time.Second}}},
IndexFiles: []string{"index.php"}, IndexFiles: []string{"index.php"},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi /blog 127.0.0.1:9000 php { {`fastcgi /blog 127.0.0.1:9000 php {
upstream 127.0.0.1:9001 upstream 127.0.0.1:9001
...@@ -88,8 +89,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -88,8 +89,9 @@ func TestFastcgiParse(t *testing.T) {
Address: "127.0.0.1:9000,127.0.0.1:9001", Address: "127.0.0.1:9000,127.0.0.1:9001",
Ext: ".php", Ext: ".php",
SplitPath: ".php", SplitPath: ".php",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000"}, basicDialer{network: "tcp", address: "127.0.0.1:9001"}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000", timeout: 60 * time.Second}, basicDialer{network: "tcp", address: "127.0.0.1:9001", timeout: 60 * time.Second}}},
IndexFiles: []string{"index.php"}, IndexFiles: []string{"index.php"},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi /blog 127.0.0.1:9000 { {`fastcgi /blog 127.0.0.1:9000 {
upstream 127.0.0.1:9001 upstream 127.0.0.1:9001
...@@ -99,8 +101,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -99,8 +101,9 @@ func TestFastcgiParse(t *testing.T) {
Address: "127.0.0.1:9000,127.0.0.1:9001", Address: "127.0.0.1:9000,127.0.0.1:9001",
Ext: "", Ext: "",
SplitPath: "", SplitPath: "",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000"}, basicDialer{network: "tcp", address: "127.0.0.1:9001"}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: "tcp", address: "127.0.0.1:9000", timeout: 60 * time.Second}, basicDialer{network: "tcp", address: "127.0.0.1:9001", timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / ` + defaultAddress + ` { {`fastcgi / ` + defaultAddress + ` {
split .html split .html
...@@ -110,8 +113,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -110,8 +113,9 @@ func TestFastcgiParse(t *testing.T) {
Address: defaultAddress, Address: defaultAddress,
Ext: "", Ext: "",
SplitPath: ".html", SplitPath: ".html",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address, timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / ` + defaultAddress + ` { {`fastcgi / ` + defaultAddress + ` {
split .html split .html
...@@ -122,9 +126,10 @@ func TestFastcgiParse(t *testing.T) { ...@@ -122,9 +126,10 @@ func TestFastcgiParse(t *testing.T) {
Address: "127.0.0.1:9001", Address: "127.0.0.1:9001",
Ext: "", Ext: "",
SplitPath: ".html", SplitPath: ".html",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address, timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
IgnoredSubPaths: []string{"/admin", "/user"}, IgnoredSubPaths: []string{"/admin", "/user"},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / ` + defaultAddress + ` { {`fastcgi / ` + defaultAddress + ` {
pool 0 pool 0
...@@ -134,8 +139,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -134,8 +139,9 @@ func TestFastcgiParse(t *testing.T) {
Address: defaultAddress, Address: defaultAddress,
Ext: "", Ext: "",
SplitPath: "", SplitPath: "",
dialer: &loadBalancingDialer{dialers: []dialer{&persistentDialer{size: 0, network: network, address: address}}}, dialer: &loadBalancingDialer{dialers: []dialer{&persistentDialer{size: 0, network: network, address: address, timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / 127.0.0.1:8080 { {`fastcgi / 127.0.0.1:8080 {
upstream 127.0.0.1:9000 upstream 127.0.0.1:9000
...@@ -146,8 +152,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -146,8 +152,9 @@ func TestFastcgiParse(t *testing.T) {
Address: "127.0.0.1:8080,127.0.0.1:9000", Address: "127.0.0.1:8080,127.0.0.1:9000",
Ext: "", Ext: "",
SplitPath: "", SplitPath: "",
dialer: &loadBalancingDialer{dialers: []dialer{&persistentDialer{size: 5, network: "tcp", address: "127.0.0.1:8080"}, &persistentDialer{size: 5, network: "tcp", address: "127.0.0.1:9000"}}}, dialer: &loadBalancingDialer{dialers: []dialer{&persistentDialer{size: 5, network: "tcp", address: "127.0.0.1:8080", timeout: 60 * time.Second}, &persistentDialer{size: 5, network: "tcp", address: "127.0.0.1:9000", timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / ` + defaultAddress + ` { {`fastcgi / ` + defaultAddress + ` {
split .php split .php
...@@ -157,8 +164,9 @@ func TestFastcgiParse(t *testing.T) { ...@@ -157,8 +164,9 @@ func TestFastcgiParse(t *testing.T) {
Address: defaultAddress, Address: defaultAddress,
Ext: "", Ext: "",
SplitPath: ".php", SplitPath: ".php",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address, timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / ` + defaultAddress + ` { {`fastcgi / ` + defaultAddress + ` {
connect_timeout 5s connect_timeout 5s
...@@ -170,6 +178,7 @@ func TestFastcgiParse(t *testing.T) { ...@@ -170,6 +178,7 @@ func TestFastcgiParse(t *testing.T) {
SplitPath: "", SplitPath: "",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address, timeout: 5 * time.Second}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address, timeout: 5 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 60 * time.Second,
}}}, }}},
{`fastcgi / ` + defaultAddress + ` { {`fastcgi / ` + defaultAddress + ` {
read_timeout 5s read_timeout 5s
...@@ -179,7 +188,7 @@ func TestFastcgiParse(t *testing.T) { ...@@ -179,7 +188,7 @@ func TestFastcgiParse(t *testing.T) {
Address: defaultAddress, Address: defaultAddress,
Ext: "", Ext: "",
SplitPath: "", SplitPath: "",
dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address}}}, dialer: &loadBalancingDialer{dialers: []dialer{basicDialer{network: network, address: address, timeout: 60 * time.Second}}},
IndexFiles: []string{}, IndexFiles: []string{},
ReadTimeout: 5 * time.Second, ReadTimeout: 5 * time.Second,
}}}, }}},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment