internal/server/raw_route_http_only_test.go
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: Apache-2.0
package server
import (
"net/http"
"net/http/httptest"
"net/url"
"path/filepath"
"strconv"
"testing"
appdb "github.com/X4Applegate/caddyui/internal/db"
"github.com/X4Applegate/caddyui/internal/models"
)
// Issue #121: an Advanced route whose Caddyfile uses explicit http:// site
// addresses is a deliberate plain-HTTP route. It must not keep a certificate,
// must not be force-redirected to HTTPS, and must not park the user on the
// "deploying" page that waits for an ACME certificate that will never exist.
func adaptedRouteFor(host string) string {
return `{"result":{"apps":{"http":{"servers":{"srv0":{"listen":[":80"],"routes":[` +
`{"match":[{"host":["` + host + `"]}],"handle":[{"handler":"static_response","body":"OK"}]}]}}}}}}`
}
func newHTTPOnlyFixture(t *testing.T, host string) (*Server, int64, string) {
t.Helper()
admin, fake := newLayer4FakeAdmin(t, `{"apps":{"http":{"servers":{"srv0":{"listen":[":443"],"routes":[]}}}}}`)
fake.adaptBody = adaptedRouteFor(host)
conn, err := appdb.Open(filepath.Join(t.TempDir(), "caddyui.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = conn.Close() })
id, err := models.CreateCaddyServer(conn, &models.CaddyServer{Name: "Primary", AdminURL: admin.URL, Type: models.CaddyServerTypeManaged})
if err != nil {
t.Fatal(err)
}
return newRenderingTestServer(t, conn), id, admin.URL
}
func TestHTTPOnlyAdvancedRouteDropsCertificateAndForceSSLAndSkipsDeployingPage(t *testing.T) {
s, serverID, _ := newHTTPOnlyFixture(t, "127.0.0.1")
form := url.Values{
"label": {"health"},
"caddyfile_src": {"# local health check\nhttp://127.0.0.1 {\n\trespond /health \"OK\" 200\n}"},
"enabled": {"on"},
"ssl_forced": {"on"}, // contradicts the explicit http:// address
"certificate_id": {"5"}, // a certificate cannot bind to an http:// route
}
rec := httptest.NewRecorder()
s.createRawRoute(rec, cookieReq(t, "/raw-routes", form, serverID))
if rec.Code != http.StatusSeeOther {
t.Fatalf("want 303, got %d: %s", rec.Code, rec.Body.String())
}
if loc := rec.Header().Get("Location"); loc != "/raw-routes" {
t.Fatalf("redirected to %q, want the route list — an http:// route has no certificate for the deploying page to wait on", loc)
}
rows, err := models.ListRawRoutes(s.DB, serverID, 0, true, nil)
if err != nil || len(rows) != 1 {
t.Fatalf("saved routes = %d (%v), want 1", len(rows), err)
}
if rows[0].CertificateID != 0 || rows[0].ForceSSL {
t.Fatalf("saved certificate_id=%d force_ssl=%v, want 0 and false for an explicit http:// route", rows[0].CertificateID, rows[0].ForceSSL)
}
}
// An ordinary Advanced route is untouched: it keeps Force SSL and still goes
// to the deploying page.
func TestHTTPSAdvancedRouteKeepsForceSSLAndDeployingPage(t *testing.T) {
s, serverID, _ := newHTTPOnlyFixture(t, "app.example.com")
form := url.Values{
"label": {"app"},
"caddyfile_src": {"app.example.com {\n\trespond \"hi\"\n}"},
"enabled": {"on"},
"ssl_forced": {"on"},
}
rec := httptest.NewRecorder()
s.createRawRoute(rec, cookieReq(t, "/raw-routes", form, serverID))
if rec.Code != http.StatusSeeOther {
t.Fatalf("want 303, got %d: %s", rec.Code, rec.Body.String())
}
rows, _ := models.ListRawRoutes(s.DB, serverID, 0, true, nil)
if len(rows) != 1 || !rows[0].ForceSSL {
t.Fatalf("an https route must keep Force SSL, got %+v", rows)
}
if loc := rec.Header().Get("Location"); loc != "/raw-routes/"+strconv.FormatInt(rows[0].ID, 10)+"/deploying" {
t.Fatalf("redirected to %q, want the deploying page for an https route", loc)
}
}
func TestHTTPOnlyAdvancedRouteEditAndDeployingPageSkipTheCertificateStep(t *testing.T) {
s, serverID, _ := newHTTPOnlyFixture(t, "127.0.0.1")
form := url.Values{
"label": {"health"},
"caddyfile_src": {"http://127.0.0.1 {\n\trespond /health \"OK\" 200\n}"},
"enabled": {"on"},
}
rec := httptest.NewRecorder()
s.createRawRoute(rec, cookieReq(t, "/raw-routes", form, serverID))
rows, _ := models.ListRawRoutes(s.DB, serverID, 0, true, nil)
if len(rows) != 1 {
t.Fatalf("setup: want 1 route, got %d", len(rows))
}
sid := strconv.FormatInt(rows[0].ID, 10)
// Editing it goes straight back to the list too.
form.Set("label", "health v2")
rec = httptest.NewRecorder()
s.updateRawRoute(rec, withChiURLParam(cookieReq(t, "/raw-routes/"+sid, form, serverID), "id", sid))
if rec.Code != http.StatusSeeOther || rec.Header().Get("Location") != "/raw-routes" {
t.Fatalf("edit: want 303 to /raw-routes, got %d %q", rec.Code, rec.Header().Get("Location"))
}
// And if someone opens the deploying page directly, it bounces to the list
// instead of polling for an ACME certificate.
rec = httptest.NewRecorder()
req := withChiURLParam(cookieReq(t, "/raw-routes/"+sid+"/deploying", nil, serverID), "id", sid)
s.rawRouteDeploying(rec, req)
if rec.Code != http.StatusSeeOther || rec.Header().Get("Location") != "/raw-routes" {
t.Fatalf("deploying page: want 303 to /raw-routes, got %d %q", rec.Code, rec.Header().Get("Location"))
}
}