diff options
Diffstat (limited to 'modules/caddyhttp')
-rw-r--r-- | modules/caddyhttp/caddyhttp.go | 109 | ||||
-rw-r--r-- | modules/caddyhttp/caddylog/log.go | 2 | ||||
-rw-r--r-- | modules/caddyhttp/routes.go | 19 | ||||
-rw-r--r-- | modules/caddyhttp/staticfiles/staticfiles.go | 4 |
4 files changed, 110 insertions, 24 deletions
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go index 5f1587d..437e48f 100644 --- a/modules/caddyhttp/caddyhttp.go +++ b/modules/caddyhttp/caddyhttp.go @@ -2,6 +2,7 @@ package caddyhttp import ( "context" + "crypto/tls" "fmt" "log" mathrand "math/rand" @@ -12,9 +13,13 @@ import ( "time" "bitbucket.org/lightcodelabs/caddy2" + "bitbucket.org/lightcodelabs/caddy2/modules/caddytls" + "github.com/mholt/certmagic" ) func init() { + mathrand.Seed(time.Now().UnixNano()) + err := caddy2.RegisterModule(caddy2.Module{ Name: "http", New: func() (interface{}, error) { return new(httpModuleConfig), nil }, @@ -22,17 +27,15 @@ func init() { if err != nil { log.Fatal(err) } - - mathrand.Seed(time.Now().UnixNano()) } type httpModuleConfig struct { - Servers map[string]httpServerConfig `json:"servers"` + Servers map[string]*httpServerConfig `json:"servers"` servers []*http.Server } -func (hc *httpModuleConfig) Run() error { +func (hc *httpModuleConfig) Provision() error { // TODO: Either prevent overlapping listeners on different servers, or combine them into one for _, srv := range hc.Servers { err := srv.Routes.setup() @@ -43,7 +46,18 @@ func (hc *httpModuleConfig) Run() error { if err != nil { return fmt.Errorf("setting up server error handling routes: %v", err) } + } + return nil +} + +func (hc *httpModuleConfig) Start(handle caddy2.Handle) error { + err := hc.automaticHTTPS(handle) + if err != nil { + return fmt.Errorf("enabling automatic HTTPS: %v", err) + } + + for srvName, srv := range hc.Servers { s := &http.Server{ ReadTimeout: time.Duration(srv.ReadTimeout), ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout), @@ -53,13 +67,30 @@ func (hc *httpModuleConfig) Run() error { for _, lnAddr := range srv.Listen { network, addrs, err := parseListenAddr(lnAddr) if err != nil { - return fmt.Errorf("parsing listen address '%s': %v", lnAddr, err) + return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err) } for _, addr := range addrs { ln, err := caddy2.Listen(network, addr) if err != nil { return fmt.Errorf("%s: listening on %s: %v", network, addr, err) } + + // enable HTTP/2 by default + for _, pol := range srv.TLSConnPolicies { + if len(pol.ALPN) == 0 { + pol.ALPN = append(pol.ALPN, defaultALPN...) + } + } + + // enable TLS + if len(srv.TLSConnPolicies) > 0 { + tlsCfg, err := srv.TLSConnPolicies.TLSConfig(handle) + if err != nil { + return fmt.Errorf("%s/%s: making TLS configuration: %v", network, addr, err) + } + ln = tls.NewListener(ln, tlsCfg) + } + go s.Serve(ln) hc.servers = append(hc.servers, s) } @@ -69,7 +100,7 @@ func (hc *httpModuleConfig) Run() error { return nil } -func (hc *httpModuleConfig) Cancel() error { +func (hc *httpModuleConfig) Stop() error { for _, s := range hc.servers { err := s.Shutdown(context.Background()) // TODO if err != nil { @@ -79,13 +110,63 @@ func (hc *httpModuleConfig) Cancel() error { return nil } +func (hc *httpModuleConfig) automaticHTTPS(handle caddy2.Handle) error { + tlsApp := handle.App("tls").(*caddytls.TLS) + + for srvName, srv := range hc.Servers { + srv.tlsApp = tlsApp + + if srv.DisableAutoHTTPS { + continue + } + + domainSet := make(map[string]struct{}) + for _, route := range srv.Routes { + for _, m := range route.matchers { + if hm, ok := m.(*matchHost); ok { + for _, d := range *hm { + if !certmagic.HostQualifies(d) { + continue + } + domainSet[d] = struct{}{} + } + } + } + } + var domains []string + for d := range domainSet { + domains = append(domains, d) + } + if len(domains) > 0 { + err := tlsApp.Manage(domains) + if err != nil { + return fmt.Errorf("%s: managing certificate for %s: %s", srvName, domains, err) + } + // TODO: Connection policies... redirects... man... + srv.TLSConnPolicies = caddytls.ConnectionPolicies{ + { + ALPN: defaultALPN, + }, + } + } + } + + return nil +} + +var defaultALPN = []string{"h2", "http/1.1"} + type httpServerConfig struct { - Listen []string `json:"listen"` - ReadTimeout caddy2.Duration `json:"read_timeout"` - ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"` - HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state - Routes routeList `json:"routes"` - Errors httpErrorConfig `json:"errors"` + Listen []string `json:"listen"` + ReadTimeout caddy2.Duration `json:"read_timeout"` + ReadHeaderTimeout caddy2.Duration `json:"read_header_timeout"` + HiddenFiles []string `json:"hidden_files"` // TODO:... experimenting with shared/common state + Routes routeList `json:"routes"` + Errors httpErrorConfig `json:"errors"` + TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies"` + DisableAutoHTTPS bool `json:"disable_auto_https"` + + tlsApp *caddytls.TLS } type httpErrorConfig struct { @@ -95,6 +176,10 @@ type httpErrorConfig struct { // ServeHTTP is the entry point for all HTTP requests. func (s httpServerConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if s.tlsApp.HandleHTTPChallenge(w, r) { + return + } + stack := s.Routes.buildMiddlewareChain(w, r) err := executeMiddlewareChain(w, r, stack) if err != nil { diff --git a/modules/caddyhttp/caddylog/log.go b/modules/caddyhttp/caddylog/log.go index dc940b3..dfc9da5 100644 --- a/modules/caddyhttp/caddylog/log.go +++ b/modules/caddyhttp/caddylog/log.go @@ -64,4 +64,4 @@ func (l *Log) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.H } // Interface guard -var _ caddyhttp.MiddlewareHandler = &Log{} +var _ caddyhttp.MiddlewareHandler = (*Log)(nil) diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go index 95b6ee8..cc26436 100644 --- a/modules/caddyhttp/routes.go +++ b/modules/caddyhttp/routes.go @@ -32,17 +32,13 @@ func (routes routeList) buildMiddlewareChain(w http.ResponseWriter, r *http.Requ var responder Handler mrw := &middlewareResponseWriter{ResponseWriterWrapper: &ResponseWriterWrapper{w}} +routeLoop: for _, route := range routes { - matched := len(route.matchers) == 0 for _, m := range route.matchers { - if m.Match(r) { - matched = true - break + if !m.Match(r) { + continue routeLoop } } - if !matched { - continue - } for _, m := range route.middleware { mid = append(mid, func(next HandlerFunc) HandlerFunc { return func(w http.ResponseWriter, r *http.Request) error { @@ -53,6 +49,8 @@ func (routes routeList) buildMiddlewareChain(w http.ResponseWriter, r *http.Requ if responder == nil { responder = route.responder } + // TODO: Should exclusive apply to only middlewares, or responder too? + // i.e. what if they haven't set a responder yet, but the first middleware chain is exclusive... if route.Exclusive { break } @@ -83,24 +81,27 @@ func (routes routeList) setup() error { } routes[i].matchers = append(routes[i].matchers, val.(RouteMatcher)) } + routes[i].Matchers = nil // allow GC to deallocate - TODO: Does this help? // middleware for j, rawMsg := range route.Apply { - mid, err := caddy2.LoadModuleInlineName("http.middleware", rawMsg) + mid, err := caddy2.LoadModuleInline("middleware", "http.middleware", rawMsg) if err != nil { return fmt.Errorf("loading middleware module in position %d: %v", j, err) } routes[i].middleware = append(routes[i].middleware, mid.(MiddlewareHandler)) } + routes[i].Apply = nil // allow GC to deallocate - TODO: Does this help? // responder if route.Respond != nil { - resp, err := caddy2.LoadModuleInlineName("http.responders", route.Respond) + resp, err := caddy2.LoadModuleInline("responder", "http.responders", route.Respond) if err != nil { return fmt.Errorf("loading responder module: %v", err) } routes[i].responder = resp.(Handler) } + routes[i].Respond = nil // allow GC to deallocate - TODO: Does this help? } return nil } diff --git a/modules/caddyhttp/staticfiles/staticfiles.go b/modules/caddyhttp/staticfiles/staticfiles.go index d1a7a7e..2a6fe37 100644 --- a/modules/caddyhttp/staticfiles/staticfiles.go +++ b/modules/caddyhttp/staticfiles/staticfiles.go @@ -10,7 +10,7 @@ import ( func init() { caddy2.RegisterModule(caddy2.Module{ Name: "http.responders.static_files", - New: func() (interface{}, error) { return &StaticFiles{}, nil }, + New: func() (interface{}, error) { return new(StaticFiles), nil }, }) } @@ -25,4 +25,4 @@ func (sf StaticFiles) ServeHTTP(w http.ResponseWriter, r *http.Request) error { } // Interface guard -var _ caddyhttp.Handler = StaticFiles{} +var _ caddyhttp.Handler = (*StaticFiles)(nil) |