summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/caddyhttp.go
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2020-03-15 21:26:17 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2020-03-15 21:26:17 -0600
commitf596fd77bb6880485d2bfc6b18a775b5572da260 (patch)
tree95039c9e11ac312efcc110c89a1e6ee6869c6d11 /modules/caddyhttp/caddyhttp.go
parent0433f9d075452d78ac93c72bad37856dc1b5b6f7 (diff)
caddyhttp: Add support for listener wrapper modules
Wrapping listeners is useful for composing custom behavior related to accepting, closing, reading/writing connections (etc) below the application layer; for example, the PROXY protocol.
Diffstat (limited to 'modules/caddyhttp/caddyhttp.go')
-rw-r--r--modules/caddyhttp/caddyhttp.go68
1 files changed, 68 insertions, 0 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index 99f215f..718025e 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -40,6 +40,11 @@ func init() {
if err != nil {
caddy.Log().Fatal(err.Error())
}
+
+ err = caddy.RegisterModule(tlsPlaceholderWrapper{})
+ if err != nil {
+ caddy.Log().Fatal(err.Error())
+ }
}
// App is a robust, production-ready HTTP server.
@@ -181,6 +186,7 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.StrictSNIHost = &trueBool
}
+ // process each listener address
for i := range srv.Listen {
lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
if err != nil {
@@ -190,6 +196,37 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.Listen[i] = lnOut
}
+ // set up each listener modifier
+ if srv.ListenerWrappersRaw != nil {
+ vals, err := ctx.LoadModule(srv, "ListenerWrappersRaw")
+ if err != nil {
+ return fmt.Errorf("loading listener wrapper modules: %v", err)
+ }
+ var hasTLSPlaceholder bool
+ for i, val := range vals.([]interface{}) {
+ if _, ok := val.(*tlsPlaceholderWrapper); ok {
+ if i == 0 {
+ // putting the tls placeholder wrapper first is nonsensical because
+ // that is the default, implicit setting: without it, all wrappers
+ // will go after the TLS listener anyway
+ return fmt.Errorf("it is unnecessary to specify the TLS listener wrapper in the first position because that is the default")
+ }
+ if hasTLSPlaceholder {
+ return fmt.Errorf("TLS listener wrapper can only be specified once")
+ }
+ hasTLSPlaceholder = true
+ }
+ srv.listenerWrappers = append(srv.listenerWrappers, val.(caddy.ListenerWrapper))
+ }
+ // if any wrappers were configured but the TLS placeholder wrapper is
+ // absent, prepend it so all defined wrappers come after the TLS
+ // handshake; this simplifies logic when starting the server, since we
+ // can simply assume the TLS placeholder will always be there
+ if !hasTLSPlaceholder && len(srv.listenerWrappers) > 0 {
+ srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
+ }
+ }
+
// pre-compile the primary handler chain, and be sure to wrap it in our
// route handler so that important security checks are done, etc.
primaryRoute := emptyHandler
@@ -265,12 +302,23 @@ func (app *App) Start() error {
return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err)
}
for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ {
+ // create the listener for this socket
hostport := listenAddr.JoinHostPort(portOffset)
ln, err := caddy.Listen(listenAddr.Network, hostport)
if err != nil {
return fmt.Errorf("%s: listening on %s: %v", listenAddr.Network, hostport, err)
}
+ // wrap listener before TLS (up to the TLS placeholder wrapper)
+ var lnWrapperIdx int
+ for i, lnWrapper := range srv.listenerWrappers {
+ if _, ok := lnWrapper.(*tlsPlaceholderWrapper); ok {
+ lnWrapperIdx = i + 1 // mark the next wrapper's spot
+ break
+ }
+ ln = lnWrapper.WrapListener(ln)
+ }
+
// enable TLS if there is a policy and if this is not the HTTP port
useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
if useTLS {
@@ -303,6 +351,11 @@ func (app *App) Start() error {
/////////
}
+ // finish wrapping listener where we left off before TLS
+ for i := lnWrapperIdx; i < len(srv.listenerWrappers); i++ {
+ ln = srv.listenerWrappers[i].WrapListener(ln)
+ }
+
app.logger.Debug("starting server loop",
zap.String("address", lnAddr),
zap.Bool("http3", srv.ExperimentalHTTP3),
@@ -544,6 +597,19 @@ func StatusCodeMatches(actual, configured int) bool {
return false
}
+// tlsPlaceholderWrapper is a no-op listener wrapper that marks
+// where the TLS listener should be in a chain of listener wrappers.
+type tlsPlaceholderWrapper struct{}
+
+func (tlsPlaceholderWrapper) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ ID: "caddy.listeners.tls",
+ New: func() caddy.Module { return new(tlsPlaceholderWrapper) },
+ }
+}
+
+func (tlsPlaceholderWrapper) WrapListener(ln net.Listener) net.Listener { return ln }
+
const (
// DefaultHTTPPort is the default port for HTTP.
DefaultHTTPPort = 80
@@ -557,4 +623,6 @@ var (
_ caddy.App = (*App)(nil)
_ caddy.Provisioner = (*App)(nil)
_ caddy.Validator = (*App)(nil)
+
+ _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
)