| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | package caddyhttp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-03-26 19:42:52 -06:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 	"crypto/tls" | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | 	"log" | 
					
						
							| 
									
										
										
										
											2019-04-11 20:42:55 -06:00
										 |  |  | 	mathrand "math/rand" | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 	"net" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"bitbucket.org/lightcodelabs/caddy2" | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 	"bitbucket.org/lightcodelabs/caddy2/modules/caddytls" | 
					
						
							|  |  |  | 	"github.com/mholt/certmagic" | 
					
						
							| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 	mathrand.Seed(time.Now().UnixNano()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | 	err := caddy2.RegisterModule(caddy2.Module{ | 
					
						
							|  |  |  | 		Name: "http", | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | 		New:  func() (interface{}, error) { return new(App), nil }, | 
					
						
							| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | // App is the HTTP app for Caddy. | 
					
						
							|  |  |  | type App struct { | 
					
						
							|  |  |  | 	HTTPPort    int                `json:"http_port"` | 
					
						
							|  |  |  | 	HTTPSPort   int                `json:"https_port"` | 
					
						
							|  |  |  | 	GracePeriod caddy2.Duration    `json:"grace_period"` | 
					
						
							|  |  |  | 	Servers     map[string]*Server `json:"servers"` | 
					
						
							| 
									
										
										
										
											2019-03-26 19:42:52 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	servers []*http.Server | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ctx caddy2.Context | 
					
						
							| 
									
										
										
										
											2019-03-26 12:00:54 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | // Provision sets up the app. | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func (app *App) Provision(ctx caddy2.Context) error { | 
					
						
							|  |  |  | 	app.ctx = ctx | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 	repl := caddy2.NewReplacer() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	for _, srv := range app.Servers { | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 		// TODO: Test this function to ensure these replacements are performed | 
					
						
							|  |  |  | 		for i := range srv.Listen { | 
					
						
							|  |  |  | 			srv.Listen[i] = repl.ReplaceAll(srv.Listen[i], "") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		err := srv.Routes.Provision(ctx) | 
					
						
							| 
									
										
										
										
											2019-04-11 20:42:55 -06:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("setting up server routes: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		err = srv.Errors.Routes.Provision(ctx) | 
					
						
							| 
									
										
										
										
											2019-04-11 20:42:55 -06:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("setting up server error handling routes: %v", err) | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | // Validate ensures the app's configuration is valid. | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func (app *App) Validate() error { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 	// each server must use distinct listener addresses | 
					
						
							|  |  |  | 	lnAddrs := make(map[string]string) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	for srvName, srv := range app.Servers { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 		for _, addr := range srv.Listen { | 
					
						
							|  |  |  | 			netw, expanded, err := parseListenAddr(addr) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("invalid listener address '%s': %v", addr, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, a := range expanded { | 
					
						
							|  |  |  | 				if sn, ok := lnAddrs[netw+a]; ok { | 
					
						
							|  |  |  | 					return fmt.Errorf("listener address repeated: %s (already claimed by server '%s')", a, sn) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				lnAddrs[netw+a] = srvName | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 	// each server's max rehandle value must be valid | 
					
						
							|  |  |  | 	for srvName, srv := range app.Servers { | 
					
						
							|  |  |  | 		if srv.MaxRehandles < 0 { | 
					
						
							|  |  |  | 			return fmt.Errorf("%s: invalid max_rehandles value: %d", srvName, srv.MaxRehandles) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | // Start runs the app. It sets up automatic HTTPS if enabled. | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func (app *App) Start() error { | 
					
						
							|  |  |  | 	err := app.automaticHTTPS() | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("enabling automatic HTTPS: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	for srvName, srv := range app.Servers { | 
					
						
							| 
									
										
										
										
											2019-03-26 19:42:52 -06:00
										 |  |  | 		s := &http.Server{ | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 			ReadTimeout:       time.Duration(srv.ReadTimeout), | 
					
						
							|  |  |  | 			ReadHeaderTimeout: time.Duration(srv.ReadHeaderTimeout), | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 			Handler:           srv, | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, lnAddr := range srv.Listen { | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 			network, addrs, err := parseListenAddr(lnAddr) | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 				return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err) | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			for _, addr := range addrs { | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 				ln, err := caddy2.Listen(network, addr) | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 					return fmt.Errorf("%s: listening on %s: %v", network, addr, err) | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// enable HTTP/2 by default | 
					
						
							|  |  |  | 				for _, pol := range srv.TLSConnPolicies { | 
					
						
							|  |  |  | 					if len(pol.ALPN) == 0 { | 
					
						
							|  |  |  | 						pol.ALPN = append(pol.ALPN, defaultALPN...) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// enable TLS | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 				httpPort := app.HTTPPort | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 				if httpPort == 0 { | 
					
						
							|  |  |  | 					httpPort = DefaultHTTPPort | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				_, port, _ := net.SplitHostPort(addr) | 
					
						
							|  |  |  | 				if len(srv.TLSConnPolicies) > 0 && port != strconv.Itoa(httpPort) { | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 					tlsCfg, err := srv.TLSConnPolicies.TLSConfig(app.ctx) | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return fmt.Errorf("%s/%s: making TLS configuration: %v", network, addr, err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					ln = tls.NewListener(ln, tlsCfg) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 				go s.Serve(ln) | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 				app.servers = append(app.servers, s) | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | // Stop gracefully shuts down the HTTP server. | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func (app *App) Stop() error { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 	ctx := context.Background() | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	if app.GracePeriod > 0 { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 		var cancel context.CancelFunc | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		ctx, cancel = context.WithTimeout(ctx, time.Duration(app.GracePeriod)) | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 		defer cancel() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	for _, s := range app.servers { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 		err := s.Shutdown(ctx) | 
					
						
							| 
									
										
										
										
											2019-03-26 19:42:52 -06:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func (app *App) automaticHTTPS() error { | 
					
						
							|  |  |  | 	tlsAppIface, err := app.ctx.App("tls") | 
					
						
							| 
									
										
										
										
											2019-04-29 09:22:00 -06:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("getting tls app: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tlsApp := tlsAppIface.(*caddytls.TLS) | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 	lnAddrMap := make(map[string]struct{}) | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | 	var redirRoutes RouteList | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 	for srvName, srv := range app.Servers { | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 		srv.tlsApp = tlsApp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if srv.DisableAutoHTTPS { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-29 09:22:00 -06:00
										 |  |  | 		// find all qualifying domain names, de-duplicated | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 		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{}{} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-29 09:22:00 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if len(domainSet) > 0 { | 
					
						
							|  |  |  | 			// marshal the domains into a slice | 
					
						
							|  |  |  | 			var domains []string | 
					
						
							|  |  |  | 			for d := range domainSet { | 
					
						
							|  |  |  | 				domains = append(domains, d) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// manage their certificates | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 			err := tlsApp.Manage(domains) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("%s: managing certificate for %s: %s", srvName, domains, err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-04-29 09:22:00 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// tell the server to use TLS | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 			srv.TLSConnPolicies = caddytls.ConnectionPolicies{ | 
					
						
							| 
									
										
										
										
											2019-04-29 09:22:00 -06:00
										 |  |  | 				{ALPN: defaultALPN}, | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-04-29 09:22:00 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 			if srv.DisableAutoHTTPSRedir { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// create HTTP->HTTPS redirects | 
					
						
							|  |  |  | 			for _, addr := range srv.Listen { | 
					
						
							|  |  |  | 				netw, host, port, err := splitListenAddr(addr) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return fmt.Errorf("%s: invalid listener address: %v", srvName, addr) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 				httpRedirLnAddr := joinListenAddr(netw, host, strconv.Itoa(app.HTTPPort)) | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 				lnAddrMap[httpRedirLnAddr] = struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if parts := strings.SplitN(port, "-", 2); len(parts) == 2 { | 
					
						
							|  |  |  | 					port = parts[0] | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				redirTo := "https://{request.host}" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 				httpsPort := app.HTTPSPort | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 				if httpsPort == 0 { | 
					
						
							|  |  |  | 					httpsPort = DefaultHTTPSPort | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if port != strconv.Itoa(httpsPort) { | 
					
						
							|  |  |  | 					redirTo += ":" + port | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				redirTo += "{request.uri}" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 14:14:05 -06:00
										 |  |  | 				redirRoutes = append(redirRoutes, ServerRoute{ | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 					matchers: []RequestMatcher{ | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 						matchProtocol("http"), | 
					
						
							|  |  |  | 						matchHost(domains), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					responder: Static{ | 
					
						
							|  |  |  | 						StatusCode: http.StatusTemporaryRedirect, // TODO: use permanent redirect instead | 
					
						
							|  |  |  | 						Headers: http.Header{ | 
					
						
							|  |  |  | 							"Location":   []string{redirTo}, | 
					
						
							|  |  |  | 							"Connection": []string{"close"}, | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 						Close: true, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(lnAddrMap) > 0 { | 
					
						
							|  |  |  | 		var lnAddrs []string | 
					
						
							|  |  |  | 	mapLoop: | 
					
						
							|  |  |  | 		for addr := range lnAddrMap { | 
					
						
							|  |  |  | 			netw, addrs, err := parseListenAddr(addr) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, a := range addrs { | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 				if app.listenerTaken(netw, a) { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 					continue mapLoop | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			lnAddrs = append(lnAddrs, addr) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | 		app.Servers["auto_https_redirects"] = &Server{ | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 			Listen:           lnAddrs, | 
					
						
							|  |  |  | 			Routes:           redirRoutes, | 
					
						
							|  |  |  | 			DisableAutoHTTPS: true, | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | func (app *App) listenerTaken(network, address string) bool { | 
					
						
							|  |  |  | 	for _, srv := range app.Servers { | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 		for _, addr := range srv.Listen { | 
					
						
							|  |  |  | 			netw, addrs, err := parseListenAddr(addr) | 
					
						
							|  |  |  | 			if err != nil || netw != network { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, a := range addrs { | 
					
						
							|  |  |  | 				if a == address { | 
					
						
							|  |  |  | 					return true | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 13:54:48 -06:00
										 |  |  | var defaultALPN = []string{"h2", "http/1.1"} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | // RequestMatcher is a type that can match to a request. | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | // A route matcher MUST NOT modify the request. | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | type RequestMatcher interface { | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	Match(*http.Request) bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Middleware chains one Handler to the next by being passed | 
					
						
							|  |  |  | // the next Handler in the chain. | 
					
						
							|  |  |  | type Middleware func(HandlerFunc) HandlerFunc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MiddlewareHandler is a Handler that includes a reference | 
					
						
							|  |  |  | // to the next middleware handler in the chain. Middleware | 
					
						
							|  |  |  | // handlers MUST NOT call Write() or WriteHeader() on the | 
					
						
							|  |  |  | // response writer; doing so will panic. See Handler godoc | 
					
						
							|  |  |  | // for more information. | 
					
						
							|  |  |  | type MiddlewareHandler interface { | 
					
						
							|  |  |  | 	ServeHTTP(http.ResponseWriter, *http.Request, Handler) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Handler is like http.Handler except ServeHTTP may return an error. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Middleware and responder handlers both implement this method. | 
					
						
							|  |  |  | // Middleware must not call Write or WriteHeader on the ResponseWriter; | 
					
						
							|  |  |  | // doing so will cause a panic. Responders should write to the response | 
					
						
							|  |  |  | // if there was not an error. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // If any handler encounters an error, it should be returned for proper | 
					
						
							|  |  |  | // handling. Return values should be propagated down the middleware chain | 
					
						
							|  |  |  | // by returning it unchanged. | 
					
						
							|  |  |  | type Handler interface { | 
					
						
							|  |  |  | 	ServeHTTP(http.ResponseWriter, *http.Request) error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // HandlerFunc is a convenience type like http.HandlerFunc. | 
					
						
							|  |  |  | type HandlerFunc func(http.ResponseWriter, *http.Request) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServeHTTP implements the Handler interface. | 
					
						
							|  |  |  | func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) error { | 
					
						
							|  |  |  | 	return f(w, r) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-11 20:42:55 -06:00
										 |  |  | // emptyHandler is used as a no-op handler, which is | 
					
						
							|  |  |  | // sometimes better than a nil Handler pointer. | 
					
						
							|  |  |  | var emptyHandler HandlerFunc = func(w http.ResponseWriter, r *http.Request) error { return nil } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | func parseListenAddr(a string) (network string, addrs []string, err error) { | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 	var host, port string | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | 	network, host, port, err = splitListenAddr(a) | 
					
						
							|  |  |  | 	if network == "" { | 
					
						
							|  |  |  | 		network = "tcp" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-26 15:45:51 -06:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ports := strings.SplitN(port, "-", 2) | 
					
						
							|  |  |  | 	if len(ports) == 1 { | 
					
						
							|  |  |  | 		ports = append(ports, ports[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var start, end int | 
					
						
							|  |  |  | 	start, err = strconv.Atoi(ports[0]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	end, err = strconv.Atoi(ports[1]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if end < start { | 
					
						
							|  |  |  | 		err = fmt.Errorf("end port must be greater than start port") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for p := start; p <= end; p++ { | 
					
						
							|  |  |  | 		addrs = append(addrs, net.JoinHostPort(host, fmt.Sprintf("%d", p))) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 09:56:13 -06:00
										 |  |  | func splitListenAddr(a string) (network, host, port string, err error) { | 
					
						
							|  |  |  | 	if idx := strings.Index(a, "/"); idx >= 0 { | 
					
						
							|  |  |  | 		network = strings.ToLower(strings.TrimSpace(a[:idx])) | 
					
						
							|  |  |  | 		a = a[idx+1:] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	host, port, err = net.SplitHostPort(a) | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func joinListenAddr(network, host, port string) string { | 
					
						
							|  |  |  | 	var a string | 
					
						
							|  |  |  | 	if network != "" { | 
					
						
							|  |  |  | 		a = network + "/" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	a += host | 
					
						
							|  |  |  | 	if port != "" { | 
					
						
							|  |  |  | 		a += ":" + port | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return a | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// DefaultHTTPPort is the default port for HTTP. | 
					
						
							|  |  |  | 	DefaultHTTPPort = 80 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// DefaultHTTPSPort is the default port for HTTPS. | 
					
						
							|  |  |  | 	DefaultHTTPSPort = 443 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | // Interface guard | 
					
						
							| 
									
										
										
										
											2019-05-16 16:05:38 -06:00
										 |  |  | var _ caddy2.App = (*App)(nil) |