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: Implement better logic for inserting the HTTP->HTTPS redirs (#4033)
* caddyhttp: Implement better logic for inserting the HTTP->HTTPS redirs * caddyhttp: Add integration test
Francis Lavoie committed
Apr 19, 2021 at 21:54 UTC
d789596bc0b014c99d75c00fe8e55c40ee3d58e3
3 files changed
+69
-14
caddytest/integration/autohttps_test.go
+23
@@ -80,3 +80,26 @@ func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
80
`, "json")
81
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
82
}
83
+
84
+func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
85
+ tester := caddytest.NewTester(t)
86
+ tester.InitServer(`
87
+ {
88
+ http_port 9080
89
+ https_port 9443
90
+ local_certs
91
+ }
92
+ http://:9080 {
93
+ respond "Foo"
94
+ }
95
+ http://baz.localhost:9080 {
96
+ respond "Baz"
97
+ }
98
+ bar.localhost {
99
+ respond "Bar"
100
+ }
101
+ `, "caddyfile")
102
+ tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
103
+ tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
104
+ tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Baz")
105
+}
modules/caddyhttp/autohttps.go
+15
-14
@@ -342,21 +342,22 @@ redirServersLoop:
342
for redirServerAddr, routes := range redirServers {
343
// for each redirect listener, see if there's already a
344
// server configured to listen on that exact address; if so,
345
- // simply add the redirect route to the end of its route
346
- // list; otherwise, we'll create a new server for all the
347
- // listener addresses that are unused and serve the
348
- // remaining redirects from it
349
- for srvName, srv := range app.Servers {
345
+ // insert the redirect route to the end of its route list
346
+ // after any other routes with host matchers; otherwise,
347
+ // we'll create a new server for all the listener addresses
348
+ // that are unused and serve the remaining redirects from it
349
+ for _, srv := range app.Servers {
350
if srv.hasListenerAddress(redirServerAddr) {
351
- // user has configured a server for the same address
352
- // that the redirect runs from; simply append our
353
- // redirect route to the existing routes, with a
354
- // caveat that their config might override ours
355
- app.logger.Warn("user server is listening on same interface as automatic HTTP->HTTPS redirects; user-configured routes might override these redirects",
356
- zap.String("server_name", srvName),
357
- zap.String("interface", redirServerAddr),
358
- )
359
- srv.Routes = append(srv.Routes, appendCatchAll(routes)...)
351
+ // find the index of the route after the last route with a host
352
+ // matcher, then insert the redirects there, but before any
353
+ // user-defined catch-all routes
354
+ // see https://github.com/caddyserver/caddy/issues/3212
355
+ insertIndex := srv.findLastRouteWithHostMatcher()
356
+ srv.Routes = append(srv.Routes[:insertIndex], append(routes, srv.Routes[insertIndex:]...)...)
357
+
358
+ // append our catch-all route in case the user didn't define their own
359
+ srv.Routes = appendCatchAll(srv.Routes)
360
+
361
continue redirServersLoop
362
}
363
}
modules/caddyhttp/server.go
+31
@@ -373,6 +373,37 @@ func (s *Server) hasTLSClientAuth() bool {
373
return false
374
}
375
376
+// findLastRouteWithHostMatcher returns the index of the last route
377
+// in the server which has a host matcher. Used during Automatic HTTPS
378
+// to determine where to insert the HTTP->HTTPS redirect route, such
379
+// that it is after any other host matcher but before any "catch-all"
380
+// route without a host matcher.
381
+func (s *Server) findLastRouteWithHostMatcher() int {
382
+ lastIndex := len(s.Routes)
383
+ for i, route := range s.Routes {
384
+ // since we want to break out of an inner loop, use a closure
385
+ // to allow us to use 'return' when we found a host matcher
386
+ found := (func() bool {
387
+ for _, sets := range route.MatcherSets {
388
+ for _, matcher := range sets {
389
+ switch matcher.(type) {
390
+ case *MatchHost:
391
+ return true
392
+ }
393
+ }
394
+ }
395
+ return false
396
+ })()
397
+
398
+ // if we found the host matcher, change the lastIndex to
399
+ // just after the current route
400
+ if found {
401
+ lastIndex = i + 1
402
+ }
403
+ }
404
+ return lastIndex
405
+}
406
+
407
// HTTPErrorConfig determines how to handle errors
408
// from the HTTP handlers.
409
type HTTPErrorConfig struct {