ronantakizawa/codereview-bench huggingface dataset:
Repository: caddyserver/caddy
File: modules/caddyhttp/reverseproxy/caddyfile.go (Go)

Code before:
				if d.NextArg() {
					return d.ArgErr()
				}
				h.BufferResponses = true

			case "max_buffer_size":
				if !d.NextArg() {
					return d.ArgErr()
				}
				size, err := strconv.Atoi(d.Val())
				if err != nil {
					return d.Errf("invalid size (bytes): %s", d.Val())
				}
				if d.NextArg() {
					return d.ArgErr()
				}
				h.MaxBufferSize = int64(size)

			case "trusted_proxies":
				for d.NextArg() {
					if d.Val() == "private_ranges" {
						h.TrustedProxies = append(h.TrustedProxies, []string{
							"192.168.0.0/16",
							"172.16.0.0/12",
							"10.0.0.0/8",
							"127.0.0.1",
							"fd00::/8",
							"::1",
						}...)
						continue
					}
					h.TrustedProxies = append(h.TrustedProxies, d.Val())
				}

			case "header_up":
				var err error

				if h.Headers == nil {
					h.Headers = new(headers.Handler)
				}
				if h.Headers.Request == nil {
					h.Headers.Request = new(headers.HeaderOps)
				}
				args := d.RemainingArgs()

				switch len(args) {
				case 1:
					err = headers.CaddyfileHeaderOp(h.Headers.Request, args[0], "", "")
				case 2:
					// some lint checks, I guess
					if strings.EqualFold(args[0], "host") && (args[1] == "{hostport}" || args[1] == "{http.request.hostport}") {

Review comment (suggestion):
```suggestion
							"127.0.0.1/32",
```

It could even be `127.0.0.0/8`: https://www.rfc-editor.org/rfc/rfc3330

Code after:
						HeaderOps: new(headers.HeaderOps),
					}
				}
				args := d.RemainingArgs()
				switch len(args) {
				case 1:
					err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], "", "")
				case 2:
					err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], "")
				case 3:
					err = headers.CaddyfileHeaderOp(h.Headers.Response.HeaderOps, args[0], args[1], args[2])
				default:
					return d.ArgErr()
				}

				if err != nil {
					return d.Err(err.Error())
				}

			case "transport":
				if !d.NextArg() {
					return d.ArgErr()
				}
				if h.TransportRaw != nil {
					return d.Err("transport already specified")
				}
				transportModuleName = d.Val()
				modID := "http.reverse_proxy.transport." + transportModuleName
				unm, err := caddyfile.UnmarshalModule(d, modID)
				if err != nil {
					return err
				}
				rt, ok := unm.(http.RoundTripper)
				if !ok {
					return d.Errf("module %s (%T) is not a RoundTripper", modID, unm)
				}
				transport = rt

			case "handle_response":
				// delegate the parsing of handle_response to the caller,
				// since we need the httpcaddyfile.Helper to parse subroutes.
				// See h.FinalizeUnmarshalCaddyfile
				h.handleResponseSegments = append(h.handleResponseSegments, d.NewFromNextSegment())

			case "replace_status":
				args := d.RemainingArgs()
				if len(args) != 2 {
					return d.Errf("must have two arguments: a response matcher and a status code")
				}

				if !strings.HasPrefix(args[0], matcherPrefix) {
Repository: caddyserver/caddy
File: listeners_test.go (Go)

Code before:
		expectPath     string
		expectFileMode string
		expectErr      bool
	}{
		{
			input:          "./foo.socket",
			expectPath:     "./foo.socket",
			expectFileMode: "-rw-rw----",
		},
		{
			input:          "./foo.socket:0660",
			expectPath:     "./foo.socket",
			expectFileMode: "-rw-rw----",
		},
		{
			input:          "./foo.socket:0666",
			expectPath:     "./foo.socket",
			expectFileMode: "-rw-rw-rw-",
		},
		{
			input:          "/var/run/foo:666",
			expectPath:     "/var/run/foo",
			expectFileMode: "-rw-rw-rw-",
		},
		{
			// symbolic permission representation is not supported
			input:     "./foo.socket:u=rw,g=rw,o=rw",
			expectErr: true,
		},
		{
			// octal (base-8) permission representation has to be between
			// `0` for no read, no write, no exec (`---`) and
			// `7` for read (4), write (2), exec (1) (`rwx` => `4+2+1 = 7`)
			input:     "./foo.socket:888",
			expectErr: true,
		},
		{
			// too many colons in address
			input:     "./foo.socket:123456:0660",
			expectErr: true,
		},
	} {
		actualPath, actualFileMode, err := splitUnixSocketPermissionsBits(tc.input)
		if tc.expectErr && err == nil {
			t.Errorf("Test %d: Expected error but got: %v", i, err)
		}
		if !tc.expectErr && err != nil {
			t.Errorf("Test %d: Expected no error but got: %v", i, err)
		}
		if actualPath != tc.expectPath {
			t.Errorf("Test %d: Expected path '%s' but got '%s'", i, tc.expectPath, actualPath)

Review comment (security):
```suggestion
			// symbolic permission representation is not supported for now
```
Just so it's clear that we're not opposed to this, even though there's a test case against it...

Code after:
			input:          ":",
			defaultNetwork: "udp",
			expectAddr: NetworkAddress{
				Network: "udp",
			},
		},
		{
			input:          "[::]",
			defaultNetwork: "udp",
			defaultPort:    53,
			expectAddr: NetworkAddress{
				Network:   "udp",
				Host:      "::",
				StartPort: 53,
				EndPort:   53,
			},
		},
		{
			input:          ":1234",
			defaultNetwork: "udp",
			expectAddr: NetworkAddress{
				Network:   "udp",
				Host:      "",
				StartPort: 1234,
				EndPort:   1234,
			},
		},
		{
			input:          "udp/:1234",
			defaultNetwork: "udp",
			expectAddr: NetworkAddress{
				Network:   "udp",
				Host:      "",
				StartPort: 1234,
				EndPort:   1234,
			},
		},
		{
			input:          "tcp6/:1234",
			defaultNetwork: "tcp",
			expectAddr: NetworkAddress{
				Network:   "tcp6",
				Host:      "",
				StartPort: 1234,
				EndPort:   1234,
			},
		},
		{
			input:          "tcp4/localhost:1234",
			defaultNetwork: "tcp",
			expectAddr: NetworkAddress{
Repository: caddyserver/caddy
File: modules/caddyhttp/http2listener.go (Go)

Code before:
package caddyhttp

import (
	"context"
	"crypto/tls"
	weakrand "math/rand"
	"net"
	"net/http"
	"sync/atomic"
	"time"

	"golang.org/x/net/http2"
)

type http2Listener struct {
	cnt uint64
	net.Listener
	server   *http.Server
	h2server *http2.Server
}

type connectionStateConn interface {
	net.Conn
	ConnectionState() tls.ConnectionState
}

func (h *http2Listener) Accept() (net.Conn, error) {
	for {
		conn, err := h.Listener.Accept()
		if err != nil {
			return nil, err
		}

		if csc, ok := conn.(connectionStateConn); ok {
			// *tls.Conn will return empty string because it's only populated after handshake is complete
			if csc.ConnectionState().NegotiatedProtocol == http2.NextProtoTLS {
				go h.serveHttp2(csc)
				continue
			}
		}

		return conn, nil
	}
}

func (h *http2Listener) serveHttp2(csc connectionStateConn) {

Review comment (suggestion):
A comment explaining the usefulness of this type instead of just the http2 package types would be useful!

Code after:
package caddyhttp

import (
	"context"
	"crypto/tls"
	weakrand "math/rand"
	"net"
	"net/http"
	"sync/atomic"
	"time"

	"golang.org/x/net/http2"
)

// http2Listener wraps the listener to solve the following problems:
// 1. server h2 natively without using h2c hack when listener handles tls connection but
// don't return *tls.Conn
// 2. graceful shutdown. the shutdown logic is copied from stdlib http.Server, it's an extra maintenance burden but
// whatever, the shutdown logic maybe extracted to be used with h2c graceful shutdown. http2.Server supports graceful shutdown
// sending GO_AWAY frame to connected clients, but doesn't track connection status. It requires explicit call of http2.ConfigureServer
type http2Listener struct {
	cnt uint64
	net.Listener
	server   *http.Server
	h2server *http2.Server
}

type connectionStateConn interface {
	net.Conn
	ConnectionState() tls.ConnectionState
}

func (h *http2Listener) Accept() (net.Conn, error) {
	for {
		conn, err := h.Listener.Accept()
		if err != nil {
			return nil, err
		}

		if csc, ok := conn.(connectionStateConn); ok {
			// *tls.Conn will return empty string because it's only populated after handshake is complete
			if csc.ConnectionState().NegotiatedProtocol == http2.NextProtoTLS {
				go h.serveHttp2(csc)
				continue
			}
		}
Repository: caddyserver/caddy
File: modules/caddyhttp/reverseproxy/metrics.go (Go)

Code before:
func initReverseProxyMetrics() {
	const ns, sub = "caddy", "reverseproxy"

	upstreamsLabels := []string{"upstream"}
	reverseProxyMetrics.upstreamsHealthy = promauto.NewGaugeVec(prometheus.GaugeOpts{
		Namespace: ns,
		Subsystem: sub,
		Name:      "upstreams_healthy",
		Help:      "Health status of reverse proxy upstreams.",
	}, upstreamsLabels)
}

type metricsUpstreamsHealthyUpdater struct {
	handler *Handler
}

func newMetricsUpstreamsHealthyUpdater(handler *Handler) *metricsUpstreamsHealthyUpdater {
	reverseProxyMetrics.init.Do(func() {
		initReverseProxyMetrics()
	})

	return &metricsUpstreamsHealthyUpdater{handler}
}

func (m *metricsUpstreamsHealthyUpdater) Init() {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				m.handler.HealthChecks.Active.logger.Error("upstreams healthy metrics updater panicked",
					zap.Any("error", err),
					zap.ByteString("stack", debug.Stack()))
			}
		}()

		m.update()

		ticker := time.NewTicker(time.Second)
		for {
			select {
			case <-ticker.C:
				m.update()
			case <-m.handler.ctx.Done():
				ticker.Stop()
				return
			}
		}
	}()
}

func (m *metricsUpstreamsHealthyUpdater) update() {
	for _, upstream := range m.handler.Upstreams {

Review comment (suggestion):
Consider a recover() in this function to ensure the server won't go down in case of a panic in the prometheus libs.

Code after:
	const ns, sub = "caddy", "reverse_proxy"

	upstreamsLabels := []string{"upstream"}
	reverseProxyMetrics.upstreamsHealthy = promauto.NewGaugeVec(prometheus.GaugeOpts{
		Namespace: ns,
		Subsystem: sub,
		Name:      "upstreams_healthy",
		Help:      "Health status of reverse proxy upstreams.",
	}, upstreamsLabels)

	reverseProxyMetrics.logger = handler.logger.Named("reverse_proxy.metrics")
}

type metricsUpstreamsHealthyUpdater struct {
	handler *Handler
}

func newMetricsUpstreamsHealthyUpdater(handler *Handler) *metricsUpstreamsHealthyUpdater {
	reverseProxyMetrics.init.Do(func() {
		initReverseProxyMetrics(handler)
	})

	return &metricsUpstreamsHealthyUpdater{handler}
}

func (m *metricsUpstreamsHealthyUpdater) Init() {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				reverseProxyMetrics.logger.Error("upstreams healthy metrics updater panicked",
					zap.Any("error", err),
					zap.ByteString("stack", debug.Stack()))
			}
		}()

		m.update()

		ticker := time.NewTicker(10 * time.Second)
		for {
			select {
			case <-ticker.C:
				m.update()
			case <-m.handler.ctx.Done():
				ticker.Stop()
				return
			}
		}
	}()
}

func (m *metricsUpstreamsHealthyUpdater) update() {
Repository: caddyserver/caddy
File: caddyconfig/httploader.go (Go)

Code before:
// LoadConfig loads a Caddy config.
func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
	repl := caddy.NewReplacer()

	client, err := hl.makeClient(ctx)
	if err != nil {
		return nil, err
	}

	method := repl.ReplaceAll(hl.Method, "")
	if method == "" {
		method = http.MethodGet
	}

	url := repl.ReplaceAll(hl.URL, "")
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		return nil, err
	}
	for key, vals := range hl.Headers {
		for _, val := range vals {
			req.Header.Add(repl.ReplaceAll(key, ""), repl.ReplaceKnown(val, ""))
		}
	}

	caddy.Log().Info("attempting to load config via http loader url")
	resp, err := doHttpCallWithRetries(ctx, client, req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("server responded with HTTP %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	result, warnings, err := adaptByContentType(resp.Header.Get("Content-Type"), body)
	if err != nil {
		return nil, err
	}
	for _, warn := range warnings {
		ctx.Logger().Warn(warn.String())
	}

	return result, nil
}


Review comment (bug):
```suggestion
```

This log doesn't seem particularly useful or informative; if we do keep it, maybe move it to Debug level and add info like which URL it is trying.

Code after:
// LoadConfig loads a Caddy config.
func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
	repl := caddy.NewReplacer()

	client, err := hl.makeClient(ctx)
	if err != nil {
		return nil, err
	}

	method := repl.ReplaceAll(hl.Method, "")
	if method == "" {
		method = http.MethodGet
	}

	url := repl.ReplaceAll(hl.URL, "")
	req, err := http.NewRequest(method, url, nil)
	if err != nil {
		return nil, err
	}
	for key, vals := range hl.Headers {
		for _, val := range vals {
			req.Header.Add(repl.ReplaceAll(key, ""), repl.ReplaceKnown(val, ""))
		}
	}

	resp, err := doHttpCallWithRetries(ctx, client, req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("server responded with HTTP %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	result, warnings, err := adaptByContentType(resp.Header.Get("Content-Type"), body)
	if err != nil {
		return nil, err
	}
	for _, warn := range warnings {
		ctx.Logger().Warn(warn.String())
	}

	return result, nil
}

func attemptHttpCall(client *http.Client, request *http.Request) (*http.Response, error) {
Repository: caddyserver/caddy
File: modules/caddyhttp/tracing/module_test.go (Go)

Code before:
	}

	req := createRequestWithContext("GET", "https://example.com/foo")
	w := httptest.NewRecorder()

	expectErr := errors.New("test error")

	var handler caddyhttp.HandlerFunc = func(writer http.ResponseWriter, request *http.Request) error {
		return expectErr
	}

	ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
	defer cancel()

	if err := ot.Provision(ctx); err != nil {
		t.Errorf("Provision error: %v", err)
		t.FailNow()
	}

	if err := ot.ServeHTTP(w, req, handler); err == nil || !errors.Is(err, expectErr) {
		t.Errorf("expected error, got: %v", err)
	}
}

func createRequestWithContext(method string, url string) *http.Request {
	r, _ := http.NewRequest("GET", "/", nil)
	repl := caddy.NewReplacer()
	ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
	r = r.WithContext(ctx)
	return r
}


Review comment (suggestion):
This doesn't look right. The variable `url` isn't being used, and the request always goes to `/`.

Code after:
	}

	req := createRequestWithContext("GET", "https://example.com/foo")
	w := httptest.NewRecorder()

	expectErr := errors.New("test error")

	var handler caddyhttp.HandlerFunc = func(writer http.ResponseWriter, request *http.Request) error {
		return expectErr
	}

	ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
	defer cancel()

	if err := ot.Provision(ctx); err != nil {
		t.Errorf("Provision error: %v", err)
		t.FailNow()
	}

	if err := ot.ServeHTTP(w, req, handler); err == nil || !errors.Is(err, expectErr) {
		t.Errorf("expected error, got: %v", err)
	}
}

func createRequestWithContext(method string, url string) *http.Request {
	r, _ := http.NewRequest(method, url, nil)
	repl := caddy.NewReplacer()
	ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
	r = r.WithContext(ctx)
	return r
}
Repository: caddyserver/caddy
File: caddyconfig/httpcaddyfile/options.go (Go)

Code before:
					ond.RateLimit = new(caddytls.RateLimit)
				}
				ond.RateLimit.Burst = burst

			default:
				return nil, d.Errf("unrecognized parameter '%s'", d.Val())
			}
		}
	}
	if ond == nil {
		return nil, d.Err("expected at least one config parameter for on_demand_tls")
	}
	return ond, nil
}

func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
	d.Next() // consume parameter name
	if !d.Next() {
		return "", d.ArgErr()
	}
	val := d.Val()
	if d.Next() {
		return "", d.ArgErr()
	}
	if val != "off" {
		return "", d.Errf("persist_config must be one of 'off'")
	}
	return val, nil
}

func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
	d.Next() // consume parameter name
	if !d.Next() {
		return "", d.ArgErr()
	}
	val := d.Val()
	if d.Next() {
		return "", d.ArgErr()
	}
	if val != "off" && val != "disable_redirects" && val != "disable_certs" && val != "ignore_loaded_certs" {
		return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'")
	}
	return val, nil
}

func parseServerOptions(d *caddyfile.Dispenser, _ any) (any, error) {
	return unmarshalCaddyfileServerOptions(d)
}

func parseOCSPStaplingOptions(d *caddyfile.Dispenser, _ any) (any, error) {
	d.Next() // consume option name

Review comment (suggestion):
For now, the wording shouldn't imply there can be other options

```suggestion
		return "", d.Errf("persist_config must be 'off'")
```

Code after:
					ond.RateLimit = new(caddytls.RateLimit)
				}
				ond.RateLimit.Burst = burst

			default:
				return nil, d.Errf("unrecognized parameter '%s'", d.Val())
			}
		}
	}
	if ond == nil {
		return nil, d.Err("expected at least one config parameter for on_demand_tls")
	}
	return ond, nil
}

func parseOptPersistConfig(d *caddyfile.Dispenser, _ any) (any, error) {
	d.Next() // consume parameter name
	if !d.Next() {
		return "", d.ArgErr()
	}
	val := d.Val()
	if d.Next() {
		return "", d.ArgErr()
	}
	if val != "off" {
		return "", d.Errf("persist_config must be 'off'")
	}
	return val, nil
}

func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
	d.Next() // consume parameter name
	if !d.Next() {
		return "", d.ArgErr()
	}
	val := d.Val()
	if d.Next() {
		return "", d.ArgErr()
	}
	if val != "off" && val != "disable_redirects" && val != "disable_certs" && val != "ignore_loaded_certs" {
		return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'")
	}
	return val, nil
}

func parseServerOptions(d *caddyfile.Dispenser, _ any) (any, error) {
	return unmarshalCaddyfileServerOptions(d)
}

func parseOCSPStaplingOptions(d *caddyfile.Dispenser, _ any) (any, error) {
	d.Next() // consume option name
Repository: caddyserver/caddy
File: modules/caddyhttp/reverseproxy/caddyfile.go (Go)

Code before:
				}
				dur, err := caddy.ParseDuration(d.Val())
				if err != nil {
					return d.Errf("bad timeout value '%s': %v", d.Val(), err)
				}
				u.DialTimeout = caddy.Duration(dur)

			case "dial_fallback_delay":
				if !d.NextArg() {
					return d.ArgErr()
				}
				dur, err := caddy.ParseDuration(d.Val())
				if err != nil {
					return d.Errf("bad delay value '%s': %v", d.Val(), err)
				}
				u.FallbackDelay = caddy.Duration(dur)

			case "versions":
				args := d.RemainingArgs()
				if len(args) == 0 {
					return d.Errf("Must specify at least one version")
				}

				// Set up defaults; if versions are specified,
				// both versions start as false, then flipped
				// to true otherwise.
				falseBool := false
				u.Versions = ipVersions{
					IPv4: &falseBool,
					IPv6: &falseBool,
				}

				trueBool := true
				for _, arg := range args {
					switch arg {
					case "ipv4":
						u.Versions.IPv4 = &trueBool
					case "ipv6":
						u.Versions.IPv6 = &trueBool
					default:
						return d.Errf("Unsupported version: '%s'", arg)
					}
				}

			default:
				return d.Errf("unrecognized a option '%s'", d.Val())
			}
		}
	}

	return nil

Review comment (suggestion):
Doing this, the one that isn't in the Caddyfile config still appears in the JSON output. I think this should only set the version to true if it appears in the config.

Essentially, you should do:
```
if u.Versions == nil {
	u.Versions = ipVersions{}
}
```

Which leaves them nil, and then in the for-switch just set entries to true.

Code after:
				}
				dur, err := caddy.ParseDuration(d.Val())
				if err != nil {
					return d.Errf("bad timeout value '%s': %v", d.Val(), err)
				}
				u.DialTimeout = caddy.Duration(dur)

			case "dial_fallback_delay":
				if !d.NextArg() {
					return d.ArgErr()
				}
				dur, err := caddy.ParseDuration(d.Val())
				if err != nil {
					return d.Errf("bad delay value '%s': %v", d.Val(), err)
				}
				u.FallbackDelay = caddy.Duration(dur)

			case "versions":
				args := d.RemainingArgs()
				if len(args) == 0 {
					return d.Errf("must specify at least one version")
				}

				if u.Versions == nil {
					u.Versions = &ipVersions{}
				}

				trueBool := true
				for _, arg := range args {
					switch arg {
					case "ipv4":
						u.Versions.IPv4 = &trueBool
					case "ipv6":
						u.Versions.IPv6 = &trueBool
					default:
						return d.Errf("unsupported version: '%s'", arg)
					}
				}

			default:
				return d.Errf("unrecognized a option '%s'", d.Val())
			}
		}
	}

	return nil
}

// UnmarshalCaddyfile deserializes Caddyfile tokens into h.
//
//	dynamic multi {
Repository: caddyserver/caddy
File: modules/caddyhttp/reverseproxy/hosts.go (Go)

Code before:

// GetDialInfo gets the upstream dialing info out of the context,
// and returns true if there was a valid value; false otherwise.
func GetDialInfo(ctx context.Context) (DialInfo, bool) {
	dialInfo, ok := caddyhttp.GetVar(ctx, dialInfoVarKey).(DialInfo)
	return dialInfo, ok
}

// 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()

// dialInfoVarKey is the key used for the variable that holds
// the dial info for the upstream connection.
const dialInfoVarKey = "reverse_proxy.dial_info"

// proxyProtocolInfoVarKey is the key used for the variable that holds
// the proxy protocol info for the upstream connection.
const proxyProtocolInfoVarKey = "reverse_proxy.proxy_protocol_info"

// ProxyProtocolInfo contains information needed to write proxy protocol to a
// connection to an upstream host.
type ProxyProtocolInfo struct {
	// assume network is tcp because golang http transport can only support tcp now
	IP   net.IP
	Port int
}


Review comment (suggestion):
We should change the type to [netip.AddrPort](https://pkg.go.dev/net/netip#AddrPort)

Code after:
}

// GetDialInfo gets the upstream dialing info out of the context,
// and returns true if there was a valid value; false otherwise.
func GetDialInfo(ctx context.Context) (DialInfo, bool) {
	dialInfo, ok := caddyhttp.GetVar(ctx, dialInfoVarKey).(DialInfo)
	return dialInfo, ok
}

// 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()

// dialInfoVarKey is the key used for the variable that holds
// the dial info for the upstream connection.
const dialInfoVarKey = "reverse_proxy.dial_info"

// proxyProtocolInfoVarKey is the key used for the variable that holds
// the proxy protocol info for the upstream connection.
const proxyProtocolInfoVarKey = "reverse_proxy.proxy_protocol_info"

// ProxyProtocolInfo contains information needed to write proxy protocol to a
// connection to an upstream host.
type ProxyProtocolInfo struct {
	AddrPort netip.AddrPort
}