mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-07 19:38:49 +03:00
httpserver: Proper HTTP->HTTPS for wildcard sites (fixes #1625)
This commit is contained in:
parent
1bae36ef29
commit
5d7db89a90
2 changed files with 104 additions and 63 deletions
|
@ -146,13 +146,20 @@ func redirPlaintextHost(cfg *SiteConfig) *SiteConfig {
|
||||||
}
|
}
|
||||||
redirMiddleware := func(next Handler) Handler {
|
redirMiddleware := func(next Handler) Handler {
|
||||||
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
|
// Construct the URL to which to redirect. Note that the Host in a request might
|
||||||
|
// contain a port, but we just need the hostname; we'll set the port if needed.
|
||||||
toURL := "https://"
|
toURL := "https://"
|
||||||
|
requestHost, _, err := net.SplitHostPort(r.Host)
|
||||||
|
if err != nil {
|
||||||
|
requestHost = r.Host // Host did not contain a port; great
|
||||||
|
}
|
||||||
if redirPort == "" {
|
if redirPort == "" {
|
||||||
toURL += cfg.Addr.Host // don't use r.Host as it may have a port included
|
toURL += requestHost
|
||||||
} else {
|
} else {
|
||||||
toURL += net.JoinHostPort(cfg.Addr.Host, redirPort)
|
toURL += net.JoinHostPort(requestHost, redirPort)
|
||||||
}
|
}
|
||||||
toURL += r.URL.RequestURI()
|
toURL += r.URL.RequestURI()
|
||||||
|
|
||||||
w.Header().Set("Connection", "close")
|
w.Header().Set("Connection", "close")
|
||||||
http.Redirect(w, r, toURL, http.StatusMovedPermanently)
|
http.Redirect(w, r, toURL, http.StatusMovedPermanently)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package httpserver
|
package httpserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -9,75 +11,107 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRedirPlaintextHost(t *testing.T) {
|
func TestRedirPlaintextHost(t *testing.T) {
|
||||||
cfg := redirPlaintextHost(&SiteConfig{
|
for i, testcase := range []struct {
|
||||||
Addr: Address{
|
Host string // used for the site config
|
||||||
|
Port string
|
||||||
|
ListenHost string
|
||||||
|
RequestHost string // if different from Host
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Host: "foohost",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Host: "foohost",
|
||||||
|
Port: "80",
|
||||||
|
},
|
||||||
|
{
|
||||||
Host: "foohost",
|
Host: "foohost",
|
||||||
Port: "1234",
|
Port: "1234",
|
||||||
},
|
},
|
||||||
ListenHost: "93.184.216.34",
|
{
|
||||||
TLS: new(caddytls.Config),
|
Host: "foohost",
|
||||||
})
|
ListenHost: "93.184.216.34",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Host: "foohost",
|
||||||
|
Port: "1234",
|
||||||
|
ListenHost: "93.184.216.34",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Host: "foohost",
|
||||||
|
Port: "443", // since this is the default HTTPS port, should not be included in Location value
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Host: "*.example.com",
|
||||||
|
RequestHost: "foo.example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Host: "*.example.com",
|
||||||
|
Port: "1234",
|
||||||
|
RequestHost: "foo.example.com:1234",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
cfg := redirPlaintextHost(&SiteConfig{
|
||||||
|
Addr: Address{
|
||||||
|
Host: testcase.Host,
|
||||||
|
Port: testcase.Port,
|
||||||
|
},
|
||||||
|
ListenHost: testcase.ListenHost,
|
||||||
|
TLS: new(caddytls.Config),
|
||||||
|
})
|
||||||
|
|
||||||
// Check host and port
|
// Check host and port
|
||||||
if actual, expected := cfg.Addr.Host, "foohost"; actual != expected {
|
if actual, expected := cfg.Addr.Host, testcase.Host; actual != expected {
|
||||||
t.Errorf("Expected redir config to have host %s but got %s", expected, actual)
|
t.Errorf("Test %d: Expected redir config to have host %s but got %s", i, expected, actual)
|
||||||
}
|
}
|
||||||
if actual, expected := cfg.ListenHost, "93.184.216.34"; actual != expected {
|
if actual, expected := cfg.ListenHost, testcase.ListenHost; actual != expected {
|
||||||
t.Errorf("Expected redir config to have bindhost %s but got %s", expected, actual)
|
t.Errorf("Test %d: Expected redir config to have bindhost %s but got %s", i, expected, actual)
|
||||||
}
|
}
|
||||||
if actual, expected := cfg.Addr.Port, "80"; actual != expected {
|
if actual, expected := cfg.Addr.Port, HTTPPort; actual != expected {
|
||||||
t.Errorf("Expected redir config to have port '%s' but got '%s'", expected, actual)
|
t.Errorf("Test %d: Expected redir config to have port '%s' but got '%s'", i, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure redirect handler is set up properly
|
// Make sure redirect handler is set up properly
|
||||||
if cfg.middleware == nil || len(cfg.middleware) != 1 {
|
if cfg.middleware == nil || len(cfg.middleware) != 1 {
|
||||||
t.Fatalf("Redir config middleware not set up properly; got: %#v", cfg.middleware)
|
t.Fatalf("Test %d: Redir config middleware not set up properly; got: %#v", i, cfg.middleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := cfg.middleware[0](nil)
|
handler := cfg.middleware[0](nil)
|
||||||
|
|
||||||
// Check redirect for correctness
|
// Check redirect for correctness, first by inspecting error and status code
|
||||||
rec := httptest.NewRecorder()
|
requestHost := testcase.Host // hostname of request might be different than in config (e.g. wildcards)
|
||||||
req, err := http.NewRequest("GET", "http://foohost/bar?q=1", nil)
|
if testcase.RequestHost != "" {
|
||||||
if err != nil {
|
requestHost = testcase.RequestHost
|
||||||
t.Fatal(err)
|
}
|
||||||
}
|
rec := httptest.NewRecorder()
|
||||||
status, err := handler.ServeHTTP(rec, req)
|
req, err := http.NewRequest("GET", "http://"+requestHost+"/bar?q=1", nil)
|
||||||
if status != 0 {
|
if err != nil {
|
||||||
t.Errorf("Expected status return to be 0, but was %d", status)
|
t.Fatalf("Test %d: %v", i, err)
|
||||||
}
|
}
|
||||||
if err != nil {
|
status, err := handler.ServeHTTP(rec, req)
|
||||||
t.Errorf("Expected returned error to be nil, but was %v", err)
|
if status != 0 {
|
||||||
}
|
t.Errorf("Test %d: Expected status return to be 0, but was %d", i, status)
|
||||||
if rec.Code != http.StatusMovedPermanently {
|
}
|
||||||
t.Errorf("Expected status %d but got %d", http.StatusMovedPermanently, rec.Code)
|
if err != nil {
|
||||||
}
|
t.Errorf("Test %d: Expected returned error to be nil, but was %v", i, err)
|
||||||
if got, want := rec.Header().Get("Location"), "https://foohost:1234/bar?q=1"; got != want {
|
}
|
||||||
t.Errorf("Expected Location: '%s' but got '%s'", want, got)
|
if rec.Code != http.StatusMovedPermanently {
|
||||||
}
|
t.Errorf("Test %d: Expected status %d but got %d", http.StatusMovedPermanently, i, rec.Code)
|
||||||
|
}
|
||||||
|
|
||||||
// browsers can infer a default port from scheme, so make sure the port
|
// Now check the Location value. It should mirror the hostname and port of the request
|
||||||
// doesn't get added in explicitly for default ports like 443 for https.
|
// unless the port is redundant, in which case it should be dropped.
|
||||||
cfg = redirPlaintextHost(&SiteConfig{Addr: Address{Host: "foohost", Port: "443"}, TLS: new(caddytls.Config)})
|
locationHost, _, err := net.SplitHostPort(requestHost)
|
||||||
handler = cfg.middleware[0](nil)
|
if err != nil {
|
||||||
|
locationHost = requestHost
|
||||||
rec = httptest.NewRecorder()
|
}
|
||||||
req, err = http.NewRequest("GET", "http://foohost/bar?q=1", nil)
|
expectedLoc := fmt.Sprintf("https://%s/bar?q=1", locationHost)
|
||||||
if err != nil {
|
if testcase.Port != "" && testcase.Port != DefaultHTTPSPort {
|
||||||
t.Fatal(err)
|
expectedLoc = fmt.Sprintf("https://%s:%s/bar?q=1", locationHost, testcase.Port)
|
||||||
}
|
}
|
||||||
status, err = handler.ServeHTTP(rec, req)
|
if got, want := rec.Header().Get("Location"), expectedLoc; got != want {
|
||||||
if status != 0 {
|
t.Errorf("Test %d: Expected Location: '%s' but got '%s'", i, want, got)
|
||||||
t.Errorf("Expected status return to be 0, but was %d", status)
|
}
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected returned error to be nil, but was %v", err)
|
|
||||||
}
|
|
||||||
if rec.Code != http.StatusMovedPermanently {
|
|
||||||
t.Errorf("Expected status %d but got %d", http.StatusMovedPermanently, rec.Code)
|
|
||||||
}
|
|
||||||
if got, want := rec.Header().Get("Location"), "https://foohost/bar?q=1"; got != want {
|
|
||||||
t.Errorf("Expected Location: '%s' but got '%s'", want, got)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue