summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/reverseproxy/hosts.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-09-03 16:56:09 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-09-03 16:56:09 -0600
commit652460e03e11a037d9f86b09b3546c9e42733d2d (patch)
tree9b1042f3fc880f87ca25949618fea45e6960080a /modules/caddyhttp/reverseproxy/hosts.go
parent4a1e1649bc985e9658d326ed433a101d7d79ae30 (diff)
Some cleanup and godoc
Diffstat (limited to 'modules/caddyhttp/reverseproxy/hosts.go')
-rw-r--r--modules/caddyhttp/reverseproxy/hosts.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/modules/caddyhttp/reverseproxy/hosts.go b/modules/caddyhttp/reverseproxy/hosts.go
new file mode 100644
index 0000000..5100936
--- /dev/null
+++ b/modules/caddyhttp/reverseproxy/hosts.go
@@ -0,0 +1,161 @@
+// Copyright 2015 Matthew Holt and The Caddy Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package reverseproxy
+
+import (
+ "fmt"
+ "net/url"
+ "sync/atomic"
+
+ "github.com/caddyserver/caddy/v2"
+)
+
+// Host represents a remote host which can be proxied to.
+// Its methods must be safe for concurrent use.
+type Host interface {
+ // NumRequests returns the numnber of requests
+ // currently in process with the host.
+ NumRequests() int
+
+ // Fails returns the count of recent failures.
+ Fails() int
+
+ // Unhealthy returns true if the backend is unhealthy.
+ Unhealthy() bool
+
+ // CountRequest counts the given number of requests
+ // as currently in process with the host. The count
+ // should not go below 0.
+ CountRequest(int) error
+
+ // CountFail counts the given number of failures
+ // with the host. The count should not go below 0.
+ CountFail(int) error
+
+ // SetHealthy marks the host as either healthy (true)
+ // or unhealthy (false). If the given status is the
+ // same, this should be a no-op. It returns true if
+ // the given status was different, false otherwise.
+ SetHealthy(bool) (bool, error)
+}
+
+// UpstreamPool is a collection of upstreams.
+type UpstreamPool []*Upstream
+
+// Upstream bridges this proxy's configuration to the
+// state of the backend host it is correlated with.
+type Upstream struct {
+ Host `json:"-"`
+
+ Address string `json:"address,omitempty"`
+ MaxRequests int `json:"max_requests,omitempty"`
+
+ // TODO: This could be really useful, to bind requests
+ // with certain properties to specific backends
+ // HeaderAffinity string
+ // IPAffinity string
+
+ healthCheckPolicy *PassiveHealthChecks
+ hostURL *url.URL
+}
+
+// Available returns true if the remote host
+// is available to receive requests.
+func (u *Upstream) Available() bool {
+ return u.Healthy() && !u.Full()
+}
+
+// Healthy returns true if the remote host
+// is currently known to be healthy or "up".
+func (u *Upstream) Healthy() bool {
+ healthy := !u.Host.Unhealthy()
+ if healthy && u.healthCheckPolicy != nil {
+ healthy = u.Host.Fails() < u.healthCheckPolicy.MaxFails
+ }
+ return healthy
+}
+
+// Full returns true if the remote host
+// cannot receive more requests at this time.
+func (u *Upstream) Full() bool {
+ return u.MaxRequests > 0 && u.Host.NumRequests() >= u.MaxRequests
+}
+
+// URL returns the upstream host's endpoint URL.
+func (u *Upstream) URL() *url.URL {
+ return u.hostURL
+}
+
+// upstreamHost is the basic, in-memory representation
+// of the state of a remote host. It implements the
+// Host interface.
+type upstreamHost struct {
+ 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)
+ fails int64
+ unhealthy int32
+}
+
+// NumRequests returns the number of active requests to the upstream.
+func (uh *upstreamHost) NumRequests() int {
+ return int(atomic.LoadInt64(&uh.numRequests))
+}
+
+// Fails returns the number of recent failures with the upstream.
+func (uh *upstreamHost) Fails() int {
+ return int(atomic.LoadInt64(&uh.fails))
+}
+
+// Unhealthy returns whether the upstream is healthy.
+func (uh *upstreamHost) Unhealthy() bool {
+ return atomic.LoadInt32(&uh.unhealthy) == 1
+}
+
+// CountRequest mutates the active request count by
+// delta. It returns an error if the adjustment fails.
+func (uh *upstreamHost) CountRequest(delta int) error {
+ result := atomic.AddInt64(&uh.numRequests, int64(delta))
+ if result < 0 {
+ return fmt.Errorf("count below 0: %d", result)
+ }
+ return nil
+}
+
+// CountFail mutates the recent failures count by
+// delta. It returns an error if the adjustment fails.
+func (uh *upstreamHost) CountFail(delta int) error {
+ result := atomic.AddInt64(&uh.fails, int64(delta))
+ if result < 0 {
+ return fmt.Errorf("count below 0: %d", result)
+ }
+ return nil
+}
+
+// SetHealthy sets the upstream has healthy or unhealthy
+// and returns true if the value was different from before,
+// or an error if the adjustment failed.
+func (uh *upstreamHost) SetHealthy(healthy bool) (bool, error) {
+ var unhealthy, compare int32 = 1, 0
+ if healthy {
+ unhealthy, compare = 0, 1
+ }
+ swapped := atomic.CompareAndSwapInt32(&uh.unhealthy, compare, unhealthy)
+ return swapped, nil
+}
+
+// hosts is the global repository for hosts that are
+// currently in use by active configuration(s). This
+// allows the state of remote hosts to be preserved
+// through config reloads.
+var hosts = caddy.NewUsagePool()