Skip to content

Commit e3ae04f

Browse files
authored
Add timeout knobs (#25)
* Add timeout knobs * Add missing godoc and unexport unnecessarily exported constants
1 parent 3906d25 commit e3ae04f

6 files changed

Lines changed: 169 additions & 44 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,9 @@ Usage of ./observatorium:
4848
-web.listen string
4949
The address on which internal server runs. (default ":8080")
5050
-web.timeout duration
51-
The maximum duration before timing out the request, and closing idle connections. (default 5m0s)
51+
The maximum duration before timing out the request, and closing idle connections. (default 2m0s)
52+
-web.timeout.read duration
53+
The maximum duration before reading the entire request, including the body. (default 2m0s)
54+
-web.timeout.write duration
55+
The maximum duration before timing out writes of the response. (default 2m0s)
5256
```

internal/proxy/option.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@ package proxy
33
import "time"
44

55
type options struct {
6-
bufferCount int
7-
bufferSizeBytes int
8-
flushInterval time.Duration
6+
bufferCount int
7+
bufferSizeBytes int
8+
maxIdleConns int
9+
flushInterval time.Duration
10+
timeout time.Duration
11+
keepAlive time.Duration
12+
idleConnTimeout time.Duration
13+
tlsHandshakeTimeout time.Duration
14+
expectContinueTimeout time.Duration
915
}
1016

1117
// Option overrides behavior of Proxy.
@@ -19,23 +25,65 @@ func (f optionFunc) apply(o *options) {
1925
f(o)
2026
}
2127

22-
// WithBufferCount TODO
28+
// WithBufferCount sets the buffer count option for the reverse proxy.
2329
func WithBufferCount(i int) Option {
2430
return optionFunc(func(o *options) {
2531
o.bufferCount = i
2632
})
2733
}
2834

29-
// WithBufferSizeBytes TODO
35+
// WithBufferSizeBytes sets the buffer size bytes option for the reverse proxy.
3036
func WithBufferSizeBytes(i int) Option {
3137
return optionFunc(func(o *options) {
3238
o.bufferSizeBytes = i
3339
})
3440
}
3541

36-
// WithFlushInterval TODO
42+
// WithFlushInterval sets the flush interval option for the reverse proxy.
3743
func WithFlushInterval(t time.Duration) Option {
3844
return optionFunc(func(o *options) {
3945
o.flushInterval = t
4046
})
4147
}
48+
49+
// WithMaxIdsConns sets the max idle conns for the underlying reverse proxy transport.
50+
func WithMaxIdsConns(i int) Option {
51+
return optionFunc(func(o *options) {
52+
o.maxIdleConns = i
53+
})
54+
}
55+
56+
// WithIdleConnTimeout sets the idle timeout duration for the underlying reverse proxy transport.
57+
func WithIdleConnTimeout(t time.Duration) Option {
58+
return optionFunc(func(o *options) {
59+
o.idleConnTimeout = t
60+
})
61+
}
62+
63+
// WithTimeout sets the timeout duration for the underlying reverse proxy connection.
64+
func WithTimeout(t time.Duration) Option {
65+
return optionFunc(func(o *options) {
66+
o.timeout = t
67+
})
68+
}
69+
70+
// WithKeepAlive sets the keep alive duration for the underlying reverse proxy connection.
71+
func WithKeepAlive(t time.Duration) Option {
72+
return optionFunc(func(o *options) {
73+
o.keepAlive = t
74+
})
75+
}
76+
77+
// WithTLSHandshakeTimeout sets the max TLS handshake timeout duration for the underlying reverse proxy transport.
78+
func WithTLSHandshakeTimeout(t time.Duration) Option {
79+
return optionFunc(func(o *options) {
80+
o.tlsHandshakeTimeout = t
81+
})
82+
}
83+
84+
// WithExpectContinueTimeout sets the max expected continue timeout duration for the underlying reverse proxy transport.
85+
func WithExpectContinueTimeout(t time.Duration) Option {
86+
return optionFunc(func(o *options) {
87+
o.expectContinueTimeout = t
88+
})
89+
}

internal/proxy/proxy.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"io/ioutil"
66
stdlog "log"
7+
"net"
78
"net/http"
89
"net/http/httputil"
910
"net/url"
@@ -17,12 +18,26 @@ import (
1718
)
1819

1920
const (
20-
// DefaultBufferCount TODO
21+
// DefaultBufferCount is the default value for the maximum size of the buffer pool for the reverse proxy.
2122
DefaultBufferCount = 2 * 1024
22-
// DefaultBufferSizeBytes TODO
23+
// DefaultBufferSizeBytes is the default value for the length of the buffers in the buffer pool for the reverse proxy.
2324
DefaultBufferSizeBytes = 32 * 1024
24-
// DefaultFlushInterval TODO
25+
// DefaultFlushInterval is the default value for the flush interval of reverse proxy to flush to the client while copying the response body.
2526
DefaultFlushInterval = time.Duration(-1)
27+
28+
// defaultTimeout is the default value for the maximum amount of time a dial will wait for a connect to complete.
29+
defaultTimeout = 30 * time.Second
30+
// defaultKeepAlive is the default value for the interval between keep-alive probes for an active network connection.
31+
defaultKeepAlive = 30 * time.Second
32+
// defaultMaxIdleConns is the default value for the maximum idle (keep-alive) connections to keep per-host.
33+
defaultMaxIdleConns = 100
34+
// defaultIdleConnTimeout is the default value for the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
35+
defaultIdleConnTimeout = 90 * time.Second
36+
// defaultTLSHandshakeTimeout is the default value for the maximum amount of time waiting to wait for a TLS handshake.
37+
defaultTLSHandshakeTimeout = 10 * time.Second
38+
// defaultExpectContinueTimeout is the default value for the amount of time to wait for a server's first response headers after fully writing the request headers,
39+
// if the request has an "Expect: 100-continue" header.
40+
defaultExpectContinueTimeout = 1 * time.Second
2641
)
2742

2843
type Proxy struct {
@@ -32,9 +47,15 @@ type Proxy struct {
3247

3348
func New(logger log.Logger, prefix string, endpoint *url.URL, opts ...Option) *Proxy {
3449
options := options{
35-
bufferCount: DefaultBufferCount,
36-
bufferSizeBytes: DefaultBufferSizeBytes,
37-
flushInterval: DefaultFlushInterval,
50+
bufferCount: DefaultBufferCount,
51+
bufferSizeBytes: DefaultBufferSizeBytes,
52+
flushInterval: DefaultFlushInterval,
53+
maxIdleConns: defaultMaxIdleConns,
54+
timeout: defaultTimeout,
55+
keepAlive: defaultKeepAlive,
56+
idleConnTimeout: defaultIdleConnTimeout,
57+
tlsHandshakeTimeout: defaultTLSHandshakeTimeout,
58+
expectContinueTimeout: defaultExpectContinueTimeout,
3859
}
3960

4061
for _, o := range opts {
@@ -64,6 +85,18 @@ func New(logger log.Logger, prefix string, endpoint *url.URL, opts ...Option) *P
6485
Director: director,
6586
ErrorLog: stdErrLogger,
6687
FlushInterval: options.flushInterval,
88+
Transport: &http.Transport{
89+
Proxy: http.ProxyFromEnvironment,
90+
Dial: (&net.Dialer{
91+
Timeout: options.timeout,
92+
KeepAlive: options.keepAlive,
93+
DualStack: true,
94+
}).Dial,
95+
MaxIdleConns: options.maxIdleConns,
96+
IdleConnTimeout: options.idleConnTimeout,
97+
TLSHandshakeTimeout: options.tlsHandshakeTimeout,
98+
ExpectContinueTimeout: options.expectContinueTimeout,
99+
},
67100
}
68101

69102
return &Proxy{logger: logger, reverseProxy: &rev}

internal/server/option.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ import (
99
)
1010

1111
type options struct {
12-
gracePeriod time.Duration
13-
timeout time.Duration
14-
tlsConfig *tls.Config
12+
gracePeriod time.Duration
13+
timeout time.Duration
14+
requestTimeout time.Duration
15+
readTimeout time.Duration
16+
writeTimeout time.Duration
17+
18+
tlsConfig *tls.Config
1519

1620
metricsUIEndpoint *url.URL
1721
metricsReadEndpoint *url.URL
@@ -34,24 +38,38 @@ func (f optionFunc) apply(o *options) {
3438
f(o)
3539
}
3640

37-
// WithGracePeriod TODO
41+
// WithGracePeriod sets graceful shutdown period for the server.
3842
func WithGracePeriod(t time.Duration) Option {
3943
return optionFunc(func(o *options) {
4044
o.gracePeriod = t
4145
})
4246
}
4347

44-
// WithListen TODO
48+
// WithListen sets the port to listen for the server.
4549
func WithListen(s string) Option {
4650
return optionFunc(func(o *options) {
4751
o.listen = s
4852
})
4953
}
5054

51-
// WithTimeout TODO
52-
func WithTimeout(t time.Duration) Option {
55+
// WithRequestTimeout sets the timeout duration for an individual request.
56+
func WithRequestTimeout(t time.Duration) Option {
57+
return optionFunc(func(o *options) {
58+
o.requestTimeout = t
59+
})
60+
}
61+
62+
// WithReadTimeout sets the read timeout duration for the underlying HTTP server.
63+
func WithReadTimeout(t time.Duration) Option {
64+
return optionFunc(func(o *options) {
65+
o.readTimeout = t
66+
})
67+
}
68+
69+
// WithWriteTimeout sets the write timeout duration for the underlying HTTP server.
70+
func WithWriteTimeout(t time.Duration) Option {
5371
return optionFunc(func(o *options) {
54-
o.timeout = t
72+
o.writeTimeout = t
5573
})
5674
}
5775

@@ -69,28 +87,28 @@ func WithMetricUIEndpoint(u *url.URL) Option {
6987
})
7088
}
7189

72-
// WithMetricReadEndpoint TODO
90+
// WithMetricReadEndpoint sets the URL to proxy metrics read request to.
7391
func WithMetricReadEndpoint(u *url.URL) Option {
7492
return optionFunc(func(o *options) {
7593
o.metricsReadEndpoint = u
7694
})
7795
}
7896

79-
// WithMetricWriteEndpoint TODO
97+
// WithMetricWriteEndpoint sets the URL to proxy metrics write request to.
8098
func WithMetricWriteEndpoint(u *url.URL) Option {
8199
return optionFunc(func(o *options) {
82100
o.metricsWriteEndpoint = u
83101
})
84102
}
85103

86-
// WithProfile TODO
104+
// WithProfile sets the option to enable/disable profiler endpoint.
87105
func WithProfile(p bool) Option {
88106
return optionFunc(func(o *options) {
89107
o.profile = p
90108
})
91109
}
92110

93-
// WithProxyOptions TODO
111+
// WithProxyOptions sets the proxy options fot the underlying reverse proxy.
94112
func WithProxyOptions(opts ...proxy.Option) Option {
95113
return optionFunc(func(o *options) {
96114
o.proxyOptions = opts

internal/server/server.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@ import (
1717
"github.com/prometheus/client_golang/prometheus/promhttp"
1818
)
1919

20+
// DefaultGracePeriod is the default value of the duration gracefully shuts down the server without interrupting any active connections.
2021
const DefaultGracePeriod = 5 * time.Second
21-
const DefaultTimeout = 5 * time.Minute
2222

23-
// Server TODO
23+
// DefaultRequestTimeout is the default value of the timeout duration per request.
24+
const DefaultRequestTimeout = 2 * time.Minute
25+
26+
// DefaultReadTimeout is the default value of the maximum duration for reading the entire request, including the body.
27+
const DefaultReadTimeout = 2 * time.Minute
28+
29+
// DefaultWriteTimeout is the default value of the maximum duration before timing out writes of the response.
30+
const DefaultWriteTimeout = 2 * time.Minute
31+
32+
// Server defines parameters for running an HTTP server.
2433
type Server struct {
2534
logger log.Logger
2635
prober *prober.Prober
@@ -29,7 +38,7 @@ type Server struct {
2938
opts options
3039
}
3140

32-
// New creates a new Server
41+
// New creates a new Server.
3342
func New(logger log.Logger, reg *prometheus.Registry, opts ...Option) Server {
3443
options := options{
3544
gracePeriod: DefaultGracePeriod,
@@ -45,7 +54,7 @@ func New(logger log.Logger, reg *prometheus.Registry, opts ...Option) Server {
4554
r.Use(middleware.RealIP)
4655
r.Use(middleware.Recoverer)
4756
r.Use(middleware.StripSlashes)
48-
r.Use(middleware.Timeout(options.timeout))
57+
r.Use(middleware.Timeout(options.requestTimeout))
4958

5059
if options.profile {
5160
r.Mount("/debug", middleware.Profiler())
@@ -109,15 +118,17 @@ func New(logger log.Logger, reg *prometheus.Registry, opts ...Option) Server {
109118
logger: logger,
110119
prober: p,
111120
srv: &http.Server{
112-
Addr: options.listen,
113-
Handler: r,
114-
TLSConfig: options.tlsConfig,
121+
Addr: options.listen,
122+
Handler: r,
123+
TLSConfig: options.tlsConfig,
124+
ReadTimeout: options.readTimeout,
125+
WriteTimeout: options.writeTimeout,
115126
},
116127
opts: options,
117128
}
118129
}
119130

120-
// ListenAndServe TODO
131+
// ListenAndServe listens on the TCP network address and handles connections with given server configuration.
121132
func (s *Server) ListenAndServe() error {
122133
level.Info(s.logger).Log("msg", "starting the HTTP server", "address", s.opts.listen)
123134
s.prober.Ready()
@@ -130,7 +141,7 @@ func (s *Server) ListenAndServe() error {
130141
return s.srv.ListenAndServe()
131142
}
132143

133-
// Shutdown TODO
144+
// Shutdown gracefully shuts down the server.
134145
func (s *Server) Shutdown(err error) {
135146
s.prober.NotReady(err)
136147

0 commit comments

Comments
 (0)