Skip to content

Commit cf40cfa

Browse files
authored
feature: read timeout filter and write timeout filter (zalando#2271)
* feature: readTimeout filter and writeTimeout filters docs: add readTimeout and writeTimeout docs: move backendTimeout to Timeouts section Signed-off-by: Sandor Szücs <[email protected]> * drop implicit integer to ms value Signed-off-by: Sandor Szücs <[email protected]> --------- Signed-off-by: Sandor Szücs <[email protected]>
1 parent 1982930 commit cf40cfa

File tree

9 files changed

+502
-26
lines changed

9 files changed

+502
-26
lines changed

docs/reference/filters.md

+58-17
Original file line numberDiff line numberDiff line change
@@ -536,23 +536,6 @@ Examples:
536536

537537
You may use https://github.com/AlexanderYastrebov/unrepeat to decompose binary file into prefix, repeating content and suffix.
538538

539-
### backendTimeout
540-
541-
Configure backend timeout. Skipper responds with `504 Gateway Timeout` status if obtaining a connection,
542-
sending the request, and reading the backend response headers and body takes longer than the configured timeout.
543-
However, if response streaming has already started it will be terminated, i.e. client will receive backend response
544-
status and truncated response body.
545-
546-
Parameters:
547-
548-
* timeout [(duration string)](https://godoc.org/time#ParseDuration)
549-
550-
Example:
551-
552-
```
553-
* -> backendTimeout("10ms") -> "https://www.example.org";
554-
```
555-
556539
### latency
557540

558541
Enable adding artificial latency
@@ -705,6 +688,64 @@ Example:
705688
* -> logHeader("request", "response") -> "https://www.example.org";
706689
```
707690

691+
## Timeout
692+
693+
### backendTimeout
694+
695+
Configure backend timeout. Skipper responds with `504 Gateway Timeout` status if obtaining a connection,
696+
sending the request, and reading the backend response headers and body takes longer than the configured timeout.
697+
However, if response streaming has already started it will be terminated, i.e. client will receive backend response
698+
status and truncated response body.
699+
700+
Parameters:
701+
702+
* timeout [(duration string)](https://godoc.org/time#ParseDuration)
703+
704+
Example:
705+
706+
```
707+
* -> backendTimeout("10ms") -> "https://www.example.org";
708+
```
709+
710+
### readTimeout
711+
712+
Configure read timeout will set a read deadline on the server socket
713+
connected to the client connecting to the proxy. Skipper will log 499
714+
client timeout with context canceled. We are not able to differentiate
715+
between client hang up and read timeout.
716+
717+
Parameters:
718+
719+
* timeout [(duration string)](https://godoc.org/time#ParseDuration)
720+
721+
Example:
722+
723+
```
724+
* -> readTimeout("10ms") -> "https://www.example.org";
725+
```
726+
727+
### writeTimeout
728+
729+
Configure write timeout will set a write deadline on the server socket
730+
connected to the client connecting to the proxy. Skipper will show
731+
access logs as if the response was served as expected, but the client can
732+
show an error. You can observe an increase in streaming errors via
733+
metrics or a in opentracing proxy span you can see Tag
734+
`streamBody.byte` with value `streamBody error` or in debug logs
735+
something like
736+
`error while copying the response stream: write tcp 127.0.0.1:9090->127.0.0.1:38574: i/o timeout`.
737+
738+
Parameters:
739+
740+
* timeout [(duration string)](https://godoc.org/time#ParseDuration)
741+
742+
Example:
743+
744+
```
745+
* -> writeTimeout("10ms") -> "https://www.example.org";
746+
```
747+
748+
708749
## Shadow Traffic
709750
### tee
710751

filters/builtin/builtin.go

+2
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ func Filters() []filters.Spec {
156156
NewHeaderToQuery(),
157157
NewQueryToHeader(),
158158
NewBackendTimeout(),
159+
NewReadTimeout(),
160+
NewWriteTimeout(),
159161
NewSetDynamicBackendHostFromHeader(),
160162
NewSetDynamicBackendSchemeFromHeader(),
161163
NewSetDynamicBackendUrlFromHeader(),

filters/builtin/timeout.go

+57-6
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,56 @@ import (
66
"github.com/zalando/skipper/filters"
77
)
88

9+
type timeoutType int
10+
11+
const (
12+
backendTimeout timeoutType = iota + 1
13+
readTimeout
14+
writeTimeout
15+
)
16+
917
type timeout struct {
18+
typ timeoutType
1019
timeout time.Duration
1120
}
1221

1322
func NewBackendTimeout() filters.Spec {
14-
return &timeout{}
23+
return &timeout{
24+
typ: backendTimeout,
25+
}
1526
}
1627

17-
func (*timeout) Name() string { return filters.BackendTimeoutName }
28+
func NewReadTimeout() filters.Spec {
29+
return &timeout{
30+
typ: readTimeout,
31+
}
32+
}
1833

19-
func (*timeout) CreateFilter(args []interface{}) (filters.Filter, error) {
34+
func NewWriteTimeout() filters.Spec {
35+
return &timeout{
36+
typ: writeTimeout,
37+
}
38+
}
39+
40+
func (t *timeout) Name() string {
41+
switch t.typ {
42+
case backendTimeout:
43+
return filters.BackendTimeoutName
44+
case readTimeout:
45+
return filters.ReadTimeoutName
46+
case writeTimeout:
47+
return filters.WriteTimeoutName
48+
}
49+
return "unknownFilter"
50+
}
51+
52+
func (t *timeout) CreateFilter(args []interface{}) (filters.Filter, error) {
2053
if len(args) != 1 {
2154
return nil, filters.ErrInvalidFilterParameters
2255
}
2356

2457
var tf timeout
58+
tf.typ = t.typ
2559
switch v := args[0].(type) {
2660
case string:
2761
d, err := time.ParseDuration(v)
@@ -37,9 +71,26 @@ func (*timeout) CreateFilter(args []interface{}) (filters.Filter, error) {
3771
return &tf, nil
3872
}
3973

74+
// Request allows overwrite of timeout settings.
75+
//
76+
// Type backend timeout sets the timeout for the backend roundtrip.
77+
//
78+
// Type read timeout sets the timeout to read the request including the body.
79+
// It uses http.ResponseController to SetReadDeadline().
80+
//
81+
// Type write timeout allows to set a timeout for writing the response.
82+
// It uses http.ResponseController to SetWriteDeadline().
83+
//
84+
// All these timeouts are set at specific points in proxy.Proxy.
4085
func (t *timeout) Request(ctx filters.FilterContext) {
41-
// allows overwrite
42-
ctx.StateBag()[filters.BackendTimeout] = t.timeout
86+
switch t.typ {
87+
case backendTimeout:
88+
ctx.StateBag()[filters.BackendTimeout] = t.timeout
89+
case readTimeout:
90+
ctx.StateBag()[filters.ReadTimeout] = t.timeout
91+
case writeTimeout:
92+
ctx.StateBag()[filters.WriteTimeout] = t.timeout
93+
}
4394
}
4495

45-
func (t *timeout) Response(filters.FilterContext) {}
96+
func (*timeout) Response(filters.FilterContext) {}

0 commit comments

Comments
 (0)