Skip to content

Commit 97000a0

Browse files
authored
MEDIUM: Add options to configure haproxy timeouts (#67)
The options are available for both incomming requests and upstreams via the "read_timeout" and "connect_timeout" options. Default values (60s and 30s) are kept if the options are not set.
1 parent 83f8b09 commit 97000a0

File tree

7 files changed

+99
-48
lines changed

7 files changed

+99
-48
lines changed

consul/config.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"crypto/x509"
55
"fmt"
66
"reflect"
7+
"time"
78
)
89

910
type Config struct {
@@ -19,6 +20,8 @@ type Upstream struct {
1920
LocalBindAddress string
2021
LocalBindPort int
2122
Protocol string
23+
ConnectTimeout time.Duration
24+
ReadTimeout time.Duration
2225

2326
TLS
2427

@@ -46,11 +49,14 @@ func (n UpstreamNode) Equal(o UpstreamNode) bool {
4649
}
4750

4851
type Downstream struct {
49-
LocalBindAddress string
50-
LocalBindPort int
51-
Protocol string
52-
TargetAddress string
53-
TargetPort int
52+
LocalBindAddress string
53+
LocalBindPort int
54+
Protocol string
55+
TargetAddress string
56+
TargetPort int
57+
ConnectTimeout time.Duration
58+
ReadTimeout time.Duration
59+
5460
EnableForwardFor bool
5561
AppNameHeaderName string
5662

consul/watcher.go

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ import (
99

1010
"github.com/hashicorp/consul/api"
1111
"github.com/hashicorp/consul/command/connect/proxy"
12+
log "github.com/sirupsen/logrus"
1213
)
1314

1415
const (
15-
defaultDownstreamBindAddr = "0.0.0.0"
16-
defaultUpstreamBindAddr = "127.0.0.1"
16+
DefaultDownstreamBindAddr = "0.0.0.0"
17+
DefaultUpstreamBindAddr = "127.0.0.1"
18+
DefaultReadTimeout = 60 * time.Second
19+
DefaultConnectTimeout = 30 * time.Second
1720

1821
errorWaitTime = 5 * time.Second
1922
preparedQueryPollInterval = 30 * time.Second
@@ -26,6 +29,8 @@ type upstream struct {
2629
Datacenter string
2730
Protocol string
2831
Nodes []*api.ServiceEntry
32+
ReadTimeout time.Duration
33+
ConnectTimeout time.Duration
2934

3035
done bool
3136
}
@@ -38,6 +43,8 @@ type downstream struct {
3843
TargetPort int
3944
EnableForwardFor bool
4045
AppNameHeaderName string
46+
ReadTimeout time.Duration
47+
ConnectTimeout time.Duration
4148
}
4249

4350
type certLeaf struct {
@@ -115,9 +122,11 @@ func (w *Watcher) Run() error {
115122
}
116123

117124
func (w *Watcher) handleProxyChange(first bool, srv *api.AgentService) {
118-
w.downstream.LocalBindAddress = defaultDownstreamBindAddr
125+
w.downstream.LocalBindAddress = DefaultDownstreamBindAddr
119126
w.downstream.LocalBindPort = srv.Port
120-
w.downstream.TargetAddress = defaultUpstreamBindAddr
127+
w.downstream.TargetAddress = DefaultUpstreamBindAddr
128+
w.downstream.ReadTimeout = DefaultReadTimeout
129+
w.downstream.ConnectTimeout = DefaultConnectTimeout
121130

122131
if srv.Proxy != nil && srv.Proxy.Config != nil {
123132
if c, ok := srv.Proxy.Config["protocol"].(string); ok {
@@ -135,6 +144,22 @@ func (w *Watcher) handleProxyChange(first bool, srv *api.AgentService) {
135144
if a, ok := srv.Proxy.Config["appname_header"].(string); ok {
136145
w.downstream.AppNameHeaderName = a
137146
}
147+
if a, ok := srv.Proxy.Config["connect_timeout"].(string); ok {
148+
to, err := time.ParseDuration(a)
149+
if err != nil {
150+
log.Errorf("bad connect_timeout value in config: %s. Using default: %s", err, DefaultConnectTimeout)
151+
} else {
152+
w.downstream.ConnectTimeout = to
153+
}
154+
}
155+
if a, ok := srv.Proxy.Config["read_timeout"].(string); ok {
156+
to, err := time.ParseDuration(a)
157+
if err != nil {
158+
log.Errorf("bad read_timeout value in config: %s. Using default: %s", err, DefaultReadTimeout)
159+
} else {
160+
w.downstream.ReadTimeout = to
161+
}
162+
}
138163
}
139164

140165
keep := make(map[string]bool)
@@ -168,20 +193,46 @@ func (w *Watcher) handleProxyChange(first bool, srv *api.AgentService) {
168193
}
169194
}
170195

171-
func (w *Watcher) startUpstreamService(up api.Upstream, name string) {
172-
w.log.Infof("consul: watching upstream for service %s", up.DestinationName)
173-
196+
func (w *Watcher) buildUpstream(up api.Upstream, name string) *upstream {
174197
u := &upstream{
175198
LocalBindAddress: up.LocalBindAddress,
176199
LocalBindPort: up.LocalBindPort,
177200
Name: name,
178201
Datacenter: up.Datacenter,
202+
ReadTimeout: DefaultReadTimeout,
203+
ConnectTimeout: DefaultConnectTimeout,
179204
}
180205

181206
if p, ok := up.Config["protocol"].(string); ok {
182207
u.Protocol = p
183208
}
184209

210+
if a, ok := up.Config["read_timeout"].(string); ok {
211+
to, err := time.ParseDuration(a)
212+
if err != nil {
213+
log.Errorf("upstream %s: bad read_timeout value in config: %s. Using default: %s", name, err, DefaultReadTimeout)
214+
} else {
215+
u.ReadTimeout = to
216+
}
217+
}
218+
219+
if a, ok := up.Config["connect_timeout"].(string); ok {
220+
to, err := time.ParseDuration(a)
221+
if err != nil {
222+
log.Errorf("upstream %s: bad connect_timeout value in config: %s. Using default: %s", name, err, DefaultConnectTimeout)
223+
} else {
224+
u.ConnectTimeout = to
225+
}
226+
}
227+
228+
return u
229+
}
230+
231+
func (w *Watcher) startUpstreamService(up api.Upstream, name string) {
232+
w.log.Infof("consul: watching upstream for service %s", up.DestinationName)
233+
234+
u := w.buildUpstream(up, name)
235+
185236
w.lock.Lock()
186237
w.upstreams[name] = u
187238
w.lock.Unlock()
@@ -219,16 +270,7 @@ func (w *Watcher) startUpstreamService(up api.Upstream, name string) {
219270
func (w *Watcher) startUpstreamPreparedQuery(up api.Upstream, name string) {
220271
w.log.Infof("consul: watching upstream for prepared_query %s", up.DestinationName)
221272

222-
u := &upstream{
223-
LocalBindAddress: up.LocalBindAddress,
224-
LocalBindPort: up.LocalBindPort,
225-
Name: name,
226-
Datacenter: up.Datacenter,
227-
}
228-
229-
if p, ok := up.Config["protocol"].(string); ok {
230-
u.Protocol = p
231-
}
273+
u := w.buildUpstream(up, name)
232274

233275
interval := preparedQueryPollInterval
234276
if p, ok := up.Config["poll_interval"].(string); ok {
@@ -429,6 +471,8 @@ func (w *Watcher) genCfg() Config {
429471
TargetAddress: w.downstream.TargetAddress,
430472
TargetPort: w.downstream.TargetPort,
431473
Protocol: w.downstream.Protocol,
474+
ConnectTimeout: w.downstream.ConnectTimeout,
475+
ReadTimeout: w.downstream.ReadTimeout,
432476
EnableForwardFor: w.downstream.EnableForwardFor,
433477
AppNameHeaderName: w.downstream.AppNameHeaderName,
434478

@@ -446,6 +490,8 @@ func (w *Watcher) genCfg() Config {
446490
LocalBindAddress: up.LocalBindAddress,
447491
LocalBindPort: up.LocalBindPort,
448492
Protocol: up.Protocol,
493+
ConnectTimeout: up.ConnectTimeout,
494+
ReadTimeout: up.ConnectTimeout,
449495
TLS: TLS{
450496
CAs: w.certCAs,
451497
Cert: w.leaf.Cert,

haproxy/state/downstream.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func generateDownstream(opts Options, certStore CertificateStore, cfg consul.Dow
2828
Frontend: models.Frontend{
2929
Name: feName,
3030
DefaultBackend: beName,
31-
ClientTimeout: &clientTimeout,
31+
ClientTimeout: int64p(int(cfg.ReadTimeout.Milliseconds())),
3232
Mode: feMode,
3333
Httplog: opts.LogRequests,
3434
},
@@ -85,8 +85,8 @@ func generateDownstream(opts Options, certStore CertificateStore, cfg consul.Dow
8585
be := Backend{
8686
Backend: models.Backend{
8787
Name: beName,
88-
ServerTimeout: &serverTimeout,
89-
ConnectTimeout: &connectTimeout,
88+
ServerTimeout: int64p(int(cfg.ReadTimeout.Milliseconds())),
89+
ConnectTimeout: int64p(int(cfg.ConnectTimeout.Milliseconds())),
9090
Mode: beMode,
9191
Forwardfor: forwardFor,
9292
},

haproxy/state/snapshot_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ func GetTestConsulConfig() consul.Config {
1818
TargetAddress: "128.0.0.5",
1919
TargetPort: 8888,
2020
AppNameHeaderName: "X-App",
21+
ConnectTimeout: consul.DefaultConnectTimeout,
22+
ReadTimeout: consul.DefaultReadTimeout,
2123
},
2224
Upstreams: []consul.Upstream{
2325
consul.Upstream{
2426
Name: "service_1",
2527
LocalBindAddress: "127.0.0.1",
2628
LocalBindPort: 10000,
29+
ConnectTimeout: consul.DefaultConnectTimeout,
30+
ReadTimeout: consul.DefaultReadTimeout,
2731
Nodes: []consul.UpstreamNode{
2832
consul.UpstreamNode{
2933
Host: "1.2.3.4",
@@ -50,7 +54,7 @@ func GetTestHAConfig(baseCfg string, certVersion string) State {
5054
Frontend: models.Frontend{
5155
Name: "front_downstream",
5256
DefaultBackend: "back_downstream",
53-
ClientTimeout: &clientTimeout,
57+
ClientTimeout: int64p(int(consul.DefaultReadTimeout.Milliseconds())),
5458
Mode: models.FrontendModeHTTP,
5559
Httplog: true,
5660
},
@@ -91,7 +95,7 @@ func GetTestHAConfig(baseCfg string, certVersion string) State {
9195
Frontend: models.Frontend{
9296
Name: "front_service_1",
9397
DefaultBackend: "back_service_1",
94-
ClientTimeout: &clientTimeout,
98+
ClientTimeout: int64p(int(consul.DefaultReadTimeout.Milliseconds())),
9599
Mode: models.FrontendModeHTTP,
96100
Httplog: true,
97101
},
@@ -115,8 +119,8 @@ func GetTestHAConfig(baseCfg string, certVersion string) State {
115119
Backend{
116120
Backend: models.Backend{
117121
Name: "back_downstream",
118-
ServerTimeout: &serverTimeout,
119-
ConnectTimeout: &connectTimeout,
122+
ServerTimeout: int64p(int(consul.DefaultReadTimeout.Milliseconds())),
123+
ConnectTimeout: int64p(int(consul.DefaultConnectTimeout.Milliseconds())),
120124
Mode: models.BackendModeHTTP,
121125
},
122126
Servers: []models.Server{
@@ -146,8 +150,8 @@ func GetTestHAConfig(baseCfg string, certVersion string) State {
146150
Backend{
147151
Backend: models.Backend{
148152
Name: "back_service_1",
149-
ServerTimeout: &serverTimeout,
150-
ConnectTimeout: &connectTimeout,
153+
ServerTimeout: int64p(int(consul.DefaultReadTimeout.Milliseconds())),
154+
ConnectTimeout: int64p(int(consul.DefaultConnectTimeout.Milliseconds())),
151155
Mode: models.BackendModeHTTP,
152156
Balance: &models.Balance{
153157
Algorithm: models.BalanceAlgorithmLeastconn,
@@ -189,8 +193,8 @@ func GetTestHAConfig(baseCfg string, certVersion string) State {
189193
Backend{
190194
Backend: models.Backend{
191195
Name: "spoe_back",
192-
ServerTimeout: &spoeTimeout,
193-
ConnectTimeout: &spoeTimeout,
196+
ServerTimeout: int64p(int(spoeTimeout.Milliseconds())),
197+
ConnectTimeout: int64p(int(spoeTimeout.Milliseconds())),
194198
Mode: models.BackendModeTCP,
195199
},
196200
Servers: []models.Server{

haproxy/state/states.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ package state
33
import (
44
"fmt"
55
"sort"
6+
"time"
67

78
"github.com/haproxytech/haproxy-consul-connect/consul"
89
"github.com/haproxytech/models"
910
)
1011

12+
const (
13+
spoeTimeout = 30 * time.Second
14+
)
15+
1116
type Options struct {
1217
EnableIntentions bool
1318
LogRequests bool
@@ -44,8 +49,8 @@ func Generate(opts Options, certStore CertificateStore, oldState State, cfg cons
4449
newState.Backends = append(newState.Backends, Backend{
4550
Backend: models.Backend{
4651
Name: "spoe_back",
47-
ServerTimeout: int64p(30000),
48-
ConnectTimeout: int64p(30000),
52+
ServerTimeout: int64p(int(spoeTimeout.Milliseconds())),
53+
ConnectTimeout: int64p(int(spoeTimeout.Milliseconds())),
4954
Mode: models.BackendModeTCP,
5055
},
5156
Servers: []models.Server{

haproxy/state/timeouts.go

Lines changed: 0 additions & 10 deletions
This file was deleted.

haproxy/state/upstream.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func generateUpstream(opts Options, certStore CertificateStore, cfg consul.Upstr
2424
Frontend: models.Frontend{
2525
Name: feName,
2626
DefaultBackend: beName,
27-
ClientTimeout: &clientTimeout,
27+
ClientTimeout: int64p(int(cfg.ReadTimeout.Milliseconds())),
2828
Mode: feMode,
2929
Httplog: opts.LogRequests,
3030
},
@@ -48,8 +48,8 @@ func generateUpstream(opts Options, certStore CertificateStore, cfg consul.Upstr
4848
be := Backend{
4949
Backend: models.Backend{
5050
Name: beName,
51-
ServerTimeout: &serverTimeout,
52-
ConnectTimeout: &connectTimeout,
51+
ServerTimeout: int64p(int(cfg.ReadTimeout.Milliseconds())),
52+
ConnectTimeout: int64p(int(cfg.ConnectTimeout.Milliseconds())),
5353
Balance: &models.Balance{
5454
Algorithm: models.BalanceAlgorithmLeastconn,
5555
},

0 commit comments

Comments
 (0)