mirror of
https://github.com/caddyserver/caddy.git
synced 2025-12-08 06:09:53 +00:00
Merge b57aaaddaf into 31960dc998
This commit is contained in:
commit
301549e303
4 changed files with 79 additions and 11 deletions
|
|
@ -102,6 +102,33 @@ func (adminUpstreams) handleUpstreams(w http.ResponseWriter, r *http.Request) er
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
// Iterate over the inflight hosts
|
||||||
|
inflightHosts.Range(func(key, val any) bool {
|
||||||
|
address, ok := key.(string)
|
||||||
|
if !ok {
|
||||||
|
rangeErr = caddy.APIError{
|
||||||
|
HTTPStatus: http.StatusInternalServerError,
|
||||||
|
Err: fmt.Errorf("could not type assert upstream address"),
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream, ok := val.(*Host)
|
||||||
|
if !ok {
|
||||||
|
rangeErr = caddy.APIError{
|
||||||
|
HTTPStatus: http.StatusInternalServerError,
|
||||||
|
Err: fmt.Errorf("could not type assert upstream struct"),
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
results = append(results, upstreamStatus{
|
||||||
|
Address: address,
|
||||||
|
NumRequests: upstream.NumRequests(),
|
||||||
|
Fails: upstream.Fails(),
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
// If an error happened during the range, return it
|
// If an error happened during the range, return it
|
||||||
if rangeErr != nil {
|
if rangeErr != nil {
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,16 @@ func (u *Upstream) fillHost() {
|
||||||
u.Host = host
|
u.Host = host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Upstream) fillInfilghtHost(numRemaiRequests int) {
|
||||||
|
host := new(Host)
|
||||||
|
existingHost, loaded := inflightHosts.LoadOrStore(u.String(), host)
|
||||||
|
if loaded {
|
||||||
|
host = existingHost.(*Host)
|
||||||
|
}
|
||||||
|
_ = host.countRequest(numRemaiRequests)
|
||||||
|
u.Host = host
|
||||||
|
}
|
||||||
|
|
||||||
// Host is the basic, in-memory representation of the state of a remote host.
|
// Host is the basic, in-memory representation of the state of a remote host.
|
||||||
// Its fields are accessed atomically and Host values must not be copied.
|
// Its fields are accessed atomically and Host values must not be copied.
|
||||||
type Host struct {
|
type Host struct {
|
||||||
|
|
@ -268,6 +278,10 @@ func GetDialInfo(ctx context.Context) (DialInfo, bool) {
|
||||||
// through config reloads.
|
// through config reloads.
|
||||||
var hosts = caddy.NewUsagePool()
|
var hosts = caddy.NewUsagePool()
|
||||||
|
|
||||||
|
// inflightHosts is the global repository for hosts that are
|
||||||
|
// currently in use by inflight upstream request.
|
||||||
|
var inflightHosts = caddy.NewUsagePool()
|
||||||
|
|
||||||
// dialInfoVarKey is the key used for the variable that holds
|
// dialInfoVarKey is the key used for the variable that holds
|
||||||
// the dial info for the upstream connection.
|
// the dial info for the upstream connection.
|
||||||
const dialInfoVarKey = "reverse_proxy.dial_info"
|
const dialInfoVarKey = "reverse_proxy.dial_info"
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,9 @@ func (h *Handler) Cleanup() error {
|
||||||
|
|
||||||
// remove hosts from our config from the pool
|
// remove hosts from our config from the pool
|
||||||
for _, upstream := range h.Upstreams {
|
for _, upstream := range h.Upstreams {
|
||||||
|
if upstream.NumRequests() > 0 {
|
||||||
|
upstream.fillInfilghtHost(upstream.NumRequests())
|
||||||
|
}
|
||||||
_, _ = hosts.Delete(upstream.String())
|
_, _ = hosts.Delete(upstream.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -474,8 +477,16 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
||||||
}
|
}
|
||||||
|
|
||||||
var done bool
|
var done bool
|
||||||
done, proxyErr = h.proxyLoopIteration(clonedReq, r, w, proxyErr, start, retries, repl, reqHeader, reqHost, next)
|
done, dialInfo, proxyErr := h.proxyLoopIteration(clonedReq, r, w, proxyErr, start, retries, repl, reqHeader, reqHost, next)
|
||||||
if done {
|
if done {
|
||||||
|
key := dialInfo.Address
|
||||||
|
val := inflightHosts.Load(key)
|
||||||
|
if val != nil {
|
||||||
|
host, _ := val.(*Host)
|
||||||
|
if host.NumRequests() <= 0 {
|
||||||
|
_, _ = inflightHosts.Delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if h.VerboseLogs {
|
if h.VerboseLogs {
|
||||||
|
|
@ -506,7 +517,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
|
||||||
// be assigned to the proxyErr value for the next iteration of the loop (or the error handled after break).
|
// be assigned to the proxyErr value for the next iteration of the loop (or the error handled after break).
|
||||||
func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w http.ResponseWriter, proxyErr error, start time.Time, retries int,
|
func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w http.ResponseWriter, proxyErr error, start time.Time, retries int,
|
||||||
repl *caddy.Replacer, reqHeader http.Header, reqHost string, next caddyhttp.Handler,
|
repl *caddy.Replacer, reqHeader http.Header, reqHost string, next caddyhttp.Handler,
|
||||||
) (bool, error) {
|
) (bool, *DialInfo, error) {
|
||||||
// get the updated list of upstreams
|
// get the updated list of upstreams
|
||||||
upstreams := h.Upstreams
|
upstreams := h.Upstreams
|
||||||
if h.DynamicUpstreams != nil {
|
if h.DynamicUpstreams != nil {
|
||||||
|
|
@ -540,9 +551,9 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
||||||
proxyErr = caddyhttp.Error(http.StatusServiceUnavailable, errNoUpstream)
|
proxyErr = caddyhttp.Error(http.StatusServiceUnavailable, errNoUpstream)
|
||||||
}
|
}
|
||||||
if !h.LoadBalancing.tryAgain(h.ctx, start, retries, proxyErr, r, h.logger) {
|
if !h.LoadBalancing.tryAgain(h.ctx, start, retries, proxyErr, r, h.logger) {
|
||||||
return true, proxyErr
|
return true, nil, proxyErr
|
||||||
}
|
}
|
||||||
return false, proxyErr
|
return false, nil, proxyErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// the dial address may vary per-request if placeholders are
|
// the dial address may vary per-request if placeholders are
|
||||||
|
|
@ -550,7 +561,7 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
||||||
// DialInfo struct should have valid network address syntax
|
// DialInfo struct should have valid network address syntax
|
||||||
dialInfo, err := upstream.fillDialInfo(repl)
|
dialInfo, err := upstream.fillDialInfo(repl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, fmt.Errorf("making dial info: %v", err)
|
return true, nil, fmt.Errorf("making dial info: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c := h.logger.Check(zapcore.DebugLevel, "selected upstream"); c != nil {
|
if c := h.logger.Check(zapcore.DebugLevel, "selected upstream"); c != nil {
|
||||||
|
|
@ -590,7 +601,7 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
||||||
if proxyErr == nil || errors.Is(proxyErr, context.Canceled) {
|
if proxyErr == nil || errors.Is(proxyErr, context.Canceled) {
|
||||||
// context.Canceled happens when the downstream client
|
// context.Canceled happens when the downstream client
|
||||||
// cancels the request, which is not our failure
|
// cancels the request, which is not our failure
|
||||||
return true, nil
|
return true, &dialInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the roundtrip was successful, don't retry the request or
|
// if the roundtrip was successful, don't retry the request or
|
||||||
|
|
@ -598,7 +609,7 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
||||||
// occur after the roundtrip if, for example, a response handler
|
// occur after the roundtrip if, for example, a response handler
|
||||||
// after the roundtrip returns an error)
|
// after the roundtrip returns an error)
|
||||||
if succ, ok := proxyErr.(roundtripSucceededError); ok {
|
if succ, ok := proxyErr.(roundtripSucceededError); ok {
|
||||||
return true, succ.error
|
return true, &dialInfo, succ.error
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember this failure (if enabled)
|
// remember this failure (if enabled)
|
||||||
|
|
@ -606,10 +617,10 @@ func (h *Handler) proxyLoopIteration(r *http.Request, origReq *http.Request, w h
|
||||||
|
|
||||||
// if we've tried long enough, break
|
// if we've tried long enough, break
|
||||||
if !h.LoadBalancing.tryAgain(h.ctx, start, retries, proxyErr, r, h.logger) {
|
if !h.LoadBalancing.tryAgain(h.ctx, start, retries, proxyErr, r, h.logger) {
|
||||||
return true, proxyErr
|
return true, &dialInfo, proxyErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, proxyErr
|
return false, &dialInfo, proxyErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mapping of the canonical form of the headers, to the RFC 6455 form,
|
// Mapping of the canonical form of the headers, to the RFC 6455 form,
|
||||||
|
|
@ -851,8 +862,14 @@ func (h Handler) addForwardedHeaders(req *http.Request) error {
|
||||||
func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origReq *http.Request, repl *caddy.Replacer, di DialInfo, next caddyhttp.Handler) error {
|
func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origReq *http.Request, repl *caddy.Replacer, di DialInfo, next caddyhttp.Handler) error {
|
||||||
_ = di.Upstream.Host.countRequest(1)
|
_ = di.Upstream.Host.countRequest(1)
|
||||||
//nolint:errcheck
|
//nolint:errcheck
|
||||||
defer di.Upstream.Host.countRequest(-1)
|
defer func() {
|
||||||
|
di.Upstream.Host.countRequest(-1)
|
||||||
|
inflightHost := inflightHosts.Load(di.Address)
|
||||||
|
if inflightHost != nil {
|
||||||
|
host, _ := inflightHost.(*Host)
|
||||||
|
host.countRequest(-1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
// point the request to this upstream
|
// point the request to this upstream
|
||||||
h.directRequest(req, di)
|
h.directRequest(req, di)
|
||||||
|
|
||||||
|
|
|
||||||
10
usagepool.go
10
usagepool.go
|
|
@ -194,6 +194,16 @@ func (up *UsagePool) Delete(key any) (deleted bool, err error) {
|
||||||
return deleted, err
|
return deleted, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (up *UsagePool) Load(key any) any {
|
||||||
|
up.RLock()
|
||||||
|
defer up.RUnlock()
|
||||||
|
upv, loaded := up.pool[key]
|
||||||
|
if loaded {
|
||||||
|
return upv.value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// References returns the number of references (count of usages) to a
|
// References returns the number of references (count of usages) to a
|
||||||
// key in the pool, and true if the key exists, or false otherwise.
|
// key in the pool, and true if the key exists, or false otherwise.
|
||||||
func (up *UsagePool) References(key any) (int, bool) {
|
func (up *UsagePool) References(key any) (int, bool) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue