summaryrefslogtreecommitdiff
path: root/modules/caddyhttp
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-04-25 13:54:48 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-04-25 13:54:48 -0600
commit2d056fbe66849f041a233a0d961639fae3835cbb (patch)
treedc78505933861e01f615470ffc1dd56a852da0b8 /modules/caddyhttp
parent545f28008e0175491af030f8689cab2112fda9ed (diff)
Initial commit of Storage, TLS, and automatic HTTPS implementations
Diffstat (limited to 'modules/caddyhttp')
-rw-r--r--modules/caddyhttp/caddyhttp.go109
-rw-r--r--modules/caddyhttp/caddylog/log.go2
-rw-r--r--modules/caddyhttp/routes.go19
-rw-r--r--modules/caddyhttp/staticfiles/staticfiles.go4
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)