@marcel91200 / caddy Mirror · read-only / commits / f596fd77
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

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.

Matthew Holt committed Mar 15, 2020 at 21:26 UTC f596fd77bb6880485d2bfc6b18a775b5572da260
3 files changed +87
listeners.go
+13
@@ -386,6 +386,19 @@ func JoinNetworkAddress(network, host, port string) string {
386 return a
387 }
388
389 +// ListenerWrapper is a type that wraps a listener
390 +// so it can modify the input listener's methods.
391 +// Modules that implement this interface are found
392 +// in the caddy.listeners namespace. Usually, to
393 +// wrap a listener, you will define your own struct
394 +// type that embeds the input listener, then
395 +// implement your own methods that you want to wrap,
396 +// calling the underlying listener's methods where
397 +// appropriate.
398 +type ListenerWrapper interface {
399 + WrapListener(net.Listener) net.Listener
400 +}
401 +
402 var (
403 listeners = make(map[string]*globalListener)
404 listenersMu sync.Mutex
modules/caddyhttp/caddyhttp.go
+68
@@ -40,6 +40,11 @@ func init() {
40 if err != nil {
41 caddy.Log().Fatal(err.Error())
42 }
43 +
44 + err = caddy.RegisterModule(tlsPlaceholderWrapper{})
45 + if err != nil {
46 + caddy.Log().Fatal(err.Error())
47 + }
48 }
49
50 // App is a robust, production-ready HTTP server.
@@ -181,6 +186,7 @@ func (app *App) Provision(ctx caddy.Context) error {
186 srv.StrictSNIHost = &trueBool
187 }
188
189 + // process each listener address
190 for i := range srv.Listen {
191 lnOut, err := repl.ReplaceOrErr(srv.Listen[i], true, true)
192 if err != nil {
@@ -190,6 +196,37 @@ func (app *App) Provision(ctx caddy.Context) error {
196 srv.Listen[i] = lnOut
197 }
198
199 + // set up each listener modifier
200 + if srv.ListenerWrappersRaw != nil {
201 + vals, err := ctx.LoadModule(srv, "ListenerWrappersRaw")
202 + if err != nil {
203 + return fmt.Errorf("loading listener wrapper modules: %v", err)
204 + }
205 + var hasTLSPlaceholder bool
206 + for i, val := range vals.([]interface{}) {
207 + if _, ok := val.(*tlsPlaceholderWrapper); ok {
208 + if i == 0 {
209 + // putting the tls placeholder wrapper first is nonsensical because
210 + // that is the default, implicit setting: without it, all wrappers
211 + // will go after the TLS listener anyway
212 + return fmt.Errorf("it is unnecessary to specify the TLS listener wrapper in the first position because that is the default")
213 + }
214 + if hasTLSPlaceholder {
215 + return fmt.Errorf("TLS listener wrapper can only be specified once")
216 + }
217 + hasTLSPlaceholder = true
218 + }
219 + srv.listenerWrappers = append(srv.listenerWrappers, val.(caddy.ListenerWrapper))
220 + }
221 + // if any wrappers were configured but the TLS placeholder wrapper is
222 + // absent, prepend it so all defined wrappers come after the TLS
223 + // handshake; this simplifies logic when starting the server, since we
224 + // can simply assume the TLS placeholder will always be there
225 + if !hasTLSPlaceholder && len(srv.listenerWrappers) > 0 {
226 + srv.listenerWrappers = append([]caddy.ListenerWrapper{new(tlsPlaceholderWrapper)}, srv.listenerWrappers...)
227 + }
228 + }
229 +
230 // pre-compile the primary handler chain, and be sure to wrap it in our
231 // route handler so that important security checks are done, etc.
232 primaryRoute := emptyHandler
@@ -265,12 +302,23 @@ func (app *App) Start() error {
302 return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err)
303 }
304 for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ {
305 + // create the listener for this socket
306 hostport := listenAddr.JoinHostPort(portOffset)
307 ln, err := caddy.Listen(listenAddr.Network, hostport)
308 if err != nil {
309 return fmt.Errorf("%s: listening on %s: %v", listenAddr.Network, hostport, err)
310 }
311
312 + // wrap listener before TLS (up to the TLS placeholder wrapper)
313 + var lnWrapperIdx int
314 + for i, lnWrapper := range srv.listenerWrappers {
315 + if _, ok := lnWrapper.(*tlsPlaceholderWrapper); ok {
316 + lnWrapperIdx = i + 1 // mark the next wrapper's spot
317 + break
318 + }
319 + ln = lnWrapper.WrapListener(ln)
320 + }
321 +
322 // enable TLS if there is a policy and if this is not the HTTP port
323 useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort()
324 if useTLS {
@@ -303,6 +351,11 @@ func (app *App) Start() error {
351 /////////
352 }
353
354 + // finish wrapping listener where we left off before TLS
355 + for i := lnWrapperIdx; i < len(srv.listenerWrappers); i++ {
356 + ln = srv.listenerWrappers[i].WrapListener(ln)
357 + }
358 +
359 app.logger.Debug("starting server loop",
360 zap.String("address", lnAddr),
361 zap.Bool("http3", srv.ExperimentalHTTP3),
@@ -544,6 +597,19 @@ func StatusCodeMatches(actual, configured int) bool {
597 return false
598 }
599
600 +// tlsPlaceholderWrapper is a no-op listener wrapper that marks
601 +// where the TLS listener should be in a chain of listener wrappers.
602 +type tlsPlaceholderWrapper struct{}
603 +
604 +func (tlsPlaceholderWrapper) CaddyModule() caddy.ModuleInfo {
605 + return caddy.ModuleInfo{
606 + ID: "caddy.listeners.tls",
607 + New: func() caddy.Module { return new(tlsPlaceholderWrapper) },
608 + }
609 +}
610 +
611 +func (tlsPlaceholderWrapper) WrapListener(ln net.Listener) net.Listener { return ln }
612 +
613 const (
614 // DefaultHTTPPort is the default port for HTTP.
615 DefaultHTTPPort = 80
@@ -557,4 +623,6 @@ var (
623 _ caddy.App = (*App)(nil)
624 _ caddy.Provisioner = (*App)(nil)
625 _ caddy.Validator = (*App)(nil)
626 +
627 + _ caddy.ListenerWrapper = (*tlsPlaceholderWrapper)(nil)
628 )
modules/caddyhttp/server.go
+6
@@ -16,6 +16,7 @@ package caddyhttp
16
17 import (
18 "context"
19 + "encoding/json"
20 "fmt"
21 "net"
22 "net/http"
@@ -38,6 +39,10 @@ type Server struct {
39 // that may include port ranges.
40 Listen []string `json:"listen,omitempty"`
41
42 + // A list of listener wrapper modules, which can modify the behavior
43 + // of the base listener. They are applied in the given order.
44 + ListenerWrappersRaw []json.RawMessage `json:"listener_wrappers,omitempty" caddy:"namespace=caddy.listeners inline_key=wrapper"`
45 +
46 // How long to allow a read from a client's upload. Setting this
47 // to a short, non-zero value can mitigate slowloris attacks, but
48 // may also affect legitimately slow clients.
@@ -106,6 +111,7 @@ type Server struct {
111
112 primaryHandlerChain Handler
113 errorHandlerChain Handler
114 + listenerWrappers []caddy.ListenerWrapper
115
116 tlsApp *caddytls.TLS
117 logger *zap.Logger