@marcel91200 / caddy Mirror · read-only / commits / e2f913bb
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

reverseproxy: Minor fixes and cleanup

Now use context cancellation to stop active health checker, which is simpler than and just as effective as using a separate stop channel.

Matthew Holt committed Aug 7, 2020 at 18:02 UTC e2f913bb7f813ac2f79cd3644066eff3815accd4
3 files changed +47 -45
modules/caddyhttp/reverseproxy/healthchecks.go
+4 -7
@@ -78,7 +78,6 @@ type ActiveHealthChecks struct {
78 // body of a healthy backend.
79 ExpectBody string `json:"expect_body,omitempty"`
80
81 - stopChan chan struct{}
81 httpClient *http.Client
82 bodyRegexp *regexp.Regexp
83 logger *zap.Logger
@@ -137,8 +136,7 @@ func (h *Handler) activeHealthChecker() {
136 select {
137 case <-ticker.C:
138 h.doActiveHealthCheckForAllHosts()
140 - case <-h.HealthChecks.Active.stopChan:
141 - // TODO: consider using a Context for cancellation instead
139 + case <-h.ctx.Done():
140 ticker.Stop()
141 return
142 }
@@ -341,8 +339,8 @@ func (h *Handler) countFailure(upstream *Upstream) {
339 if err != nil {
340 h.HealthChecks.Passive.logger.Error("could not count failure",
341 zap.String("host", upstream.Dial),
344 - zap.Error(err),
345 - )
342 + zap.Error(err))
343 + return
344 }
345
346 // forget it later
@@ -357,8 +355,7 @@ func (h *Handler) countFailure(upstream *Upstream) {
355 if err != nil {
356 h.HealthChecks.Passive.logger.Error("could not forget failure",
357 zap.String("host", upstream.Dial),
360 - zap.Error(err),
361 - )
358 + zap.Error(err))
359 }
360 }(upstream.Host, failDuration)
361 }
modules/caddyhttp/reverseproxy/hosts.go
+1 -1
@@ -177,7 +177,7 @@ func (u *Upstream) fillDialInfo(r *http.Request) (DialInfo, error) {
177 // of the state of a remote host. It implements the
178 // Host interface.
179 type upstreamHost struct {
180 - numRequests int64 // must be first field to be 64-bit aligned on 32-bit systems (see https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
180 + numRequests int64 // must be 64-bit aligned on 32-bit systems (see https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
181 fails int64
182 unhealthy int32
183 }
modules/caddyhttp/reverseproxy/reverseproxy.go
+42 -37
@@ -112,6 +112,7 @@ type Handler struct {
112 Transport http.RoundTripper `json:"-"`
113 CB CircuitBreaker `json:"-"`
114
115 + ctx caddy.Context
116 logger *zap.Logger
117 }
118
@@ -125,6 +126,7 @@ func (Handler) CaddyModule() caddy.ModuleInfo {
126
127 // Provision ensures that h is set up properly before use.
128 func (h *Handler) Provision(ctx caddy.Context) error {
129 + h.ctx = ctx
130 h.logger = ctx.Logger(h)
131
132 // start by loading modules
@@ -235,36 +237,43 @@ func (h *Handler) Provision(ctx caddy.Context) error {
237 }
238 }
239
238 - // if active health checks are enabled, configure them and start a worker
239 - if h.HealthChecks != nil &&
240 - h.HealthChecks.Active != nil &&
241 - (h.HealthChecks.Active.Path != "" || h.HealthChecks.Active.Port != 0) {
242 - h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
243 -
244 - timeout := time.Duration(h.HealthChecks.Active.Timeout)
245 - if timeout == 0 {
246 - timeout = 5 * time.Second
240 + if h.HealthChecks != nil {
241 + // set defaults on passive health checks, if necessary
242 + if h.HealthChecks.Passive != nil {
243 + if h.HealthChecks.Passive.FailDuration > 0 && h.HealthChecks.Passive.MaxFails == 0 {
244 + h.HealthChecks.Passive.MaxFails = 1
245 + }
246 }
247
249 - h.HealthChecks.Active.stopChan = make(chan struct{})
250 - h.HealthChecks.Active.httpClient = &http.Client{
251 - Timeout: timeout,
252 - Transport: h.Transport,
253 - }
248 + // if active health checks are enabled, configure them and start a worker
249 + if h.HealthChecks.Active != nil &&
250 + (h.HealthChecks.Active.Path != "" || h.HealthChecks.Active.Port != 0) {
251 + h.HealthChecks.Active.logger = h.logger.Named("health_checker.active")
252
255 - if h.HealthChecks.Active.Interval == 0 {
256 - h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
257 - }
253 + timeout := time.Duration(h.HealthChecks.Active.Timeout)
254 + if timeout == 0 {
255 + timeout = 5 * time.Second
256 + }
257
259 - if h.HealthChecks.Active.ExpectBody != "" {
260 - var err error
261 - h.HealthChecks.Active.bodyRegexp, err = regexp.Compile(h.HealthChecks.Active.ExpectBody)
262 - if err != nil {
263 - return fmt.Errorf("expect_body: compiling regular expression: %v", err)
258 + h.HealthChecks.Active.httpClient = &http.Client{
259 + Timeout: timeout,
260 + Transport: h.Transport,
261 + }
262 +
263 + if h.HealthChecks.Active.Interval == 0 {
264 + h.HealthChecks.Active.Interval = caddy.Duration(30 * time.Second)
265 + }
266 +
267 + if h.HealthChecks.Active.ExpectBody != "" {
268 + var err error
269 + h.HealthChecks.Active.bodyRegexp, err = regexp.Compile(h.HealthChecks.Active.ExpectBody)
270 + if err != nil {
271 + return fmt.Errorf("expect_body: compiling regular expression: %v", err)
272 + }
273 }
265 - }
274
267 - go h.activeHealthChecker()
275 + go h.activeHealthChecker()
276 + }
277 }
278
279 // set up any response routes
@@ -280,14 +289,6 @@ func (h *Handler) Provision(ctx caddy.Context) error {
289
290 // Cleanup cleans up the resources made by h during provisioning.
291 func (h *Handler) Cleanup() error {
283 - // stop the active health checker
284 - if h.HealthChecks != nil &&
285 - h.HealthChecks.Active != nil &&
286 - h.HealthChecks.Active.stopChan != nil {
287 - // TODO: consider using context cancellation, could be much simpler
288 - close(h.HealthChecks.Active.stopChan)
289 - }
290 -
292 // TODO: Close keepalive connections on reload? https://github.com/caddyserver/caddy/pull/2507/files#diff-70219fd88fe3f36834f474ce6537ed26R762
293
294 // remove hosts from our config from the pool
@@ -351,7 +352,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
352 if proxyErr == nil {
353 proxyErr = fmt.Errorf("no upstreams available")
354 }
354 - if !h.LoadBalancing.tryAgain(start, proxyErr, r) {
355 + if !h.LoadBalancing.tryAgain(h.ctx, start, proxyErr, r) {
356 break
357 }
358 continue
@@ -410,7 +411,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
411 h.countFailure(upstream)
412
413 // if we've tried long enough, break
413 - if !h.LoadBalancing.tryAgain(start, proxyErr, r) {
414 + if !h.LoadBalancing.tryAgain(h.ctx, start, proxyErr, r) {
415 break
416 }
417 }
@@ -661,7 +662,7 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, di Dia
662 // long enough before the next retry (i.e. no more sleeping is
663 // needed). If false is returned, the handler should stop trying to
664 // proxy the request.
664 -func (lb LoadBalancing) tryAgain(start time.Time, proxyErr error, req *http.Request) bool {
665 +func (lb LoadBalancing) tryAgain(ctx caddy.Context, start time.Time, proxyErr error, req *http.Request) bool {
666 // if we've tried long enough, break
667 if time.Since(start) >= time.Duration(lb.TryDuration) {
668 return false
@@ -687,8 +688,12 @@ func (lb LoadBalancing) tryAgain(start time.Time, proxyErr error, req *http.Requ
688 }
689
690 // otherwise, wait and try the next available host
690 - time.Sleep(time.Duration(lb.TryInterval))
691 - return true
691 + select {
692 + case <-time.After(time.Duration(lb.TryInterval)):
693 + return true
694 + case <-ctx.Done():
695 + return false
696 + }
697 }
698
699 // directRequest modifies only req.URL so that it points to the upstream