Rename the program to sww and its deciding daemon to discriminator
check / check (push) Successful in 3m13s

The program is now sww, built from cmd/sww, with two modes: sww rproxy and
sww discriminator. servewaf becomes discriminator everywhere it names that
daemon: the package (internal/discriminator), its types, functions, files, the
discriminator_line log field, and its docs and tests.

smallwebwaf is left only where sneak's directive allows it: the documents'
product name, the git.eeqj.de/sneak/smallwebwaf URLs, and the Go module path
sneak.berlin/go/smallwebwaf. Everywhere else a name containing it takes the sww
form: the package (internal/sww), metric names (sww_*), the X-Sww-* headers,
the /_sww/ admin endpoints, the OS user, and the state and rules directories.
The SWWAF_ environment variables take the SWW_ form.

No logic changes. make check passes.

Model: deepseek-v4-pro
This commit is contained in:
sneak committed 2026-11-07 04:47:34 +00:00
1 parent 2e2f6f7175
commit 42643be662
106 files changed
+3289 -3262

No files matched your search

+108 -103
View File
@@ -19,9 +19,9 @@ import (
"time"
"sneak.berlin/go/smallwebwaf/internal/config"
"sneak.berlin/go/smallwebwaf/internal/discriminator"
"sneak.berlin/go/smallwebwaf/internal/requestlog"
"sneak.berlin/go/smallwebwaf/internal/rproxy"
"sneak.berlin/go/smallwebwaf/internal/servewaf"
)
const (
@@ -54,27 +54,27 @@ const (
// The headers the tests set and look at.
const (
dispositionHeader = servewaf.DispositionHeader
dispositionHeader = discriminator.DispositionHeader
forwardedForHeader = "X-Forwarded-For"
forwardedProtoHeader = "X-Forwarded-Proto"
requestIDHeader = "X-Request-ID"
asnHeader = "X-Client-Asn"
countryHeader = "X-Client-Country"
contentLengthHeader = "Content-Length"
// fromServeWAFHeader is a header the stand-in for servewaf adds to a deny.
fromServeWAFHeader = "X-From-Servewaf"
// fromDiscriminatorHeader is a header the stand-in for discriminator adds to a deny.
fromDiscriminatorHeader = "X-From-Discriminator"
// addedHeaderValue is the value of a header a test adds to an answer, to see it
// reach the visitor.
addedHeaderValue = "yes"
)
// What the stand-in for servewaf answers.
// What the stand-in for discriminator answers.
const (
dispositionAllow = servewaf.DispositionAllow
dispositionDeny = servewaf.DispositionDeny
dispositionClose = servewaf.DispositionClose
dispositionAllow = discriminator.DispositionAllow
dispositionDeny = discriminator.DispositionDeny
dispositionClose = discriminator.DispositionClose
// deniedBody is the body of a deny.
deniedBody = "refused by servewaf"
deniedBody = "refused by discriminator"
)
// output collects what rproxy writes on stdout.
@@ -158,7 +158,7 @@ func (l heldLog) Write(p []byte) (int, error) {
return len(p), nil
}
// question is what the stand-in for servewaf received as a question.
// question is what the stand-in for discriminator received as a question.
type question struct {
method, target, host string
header http.Header
@@ -167,26 +167,26 @@ type question struct {
transferEncoding []string
}
// serveWAF is a stand-in for servewaf that answers each question as answer
// does, and keeps the questions and the reports it gets.
type serveWAF struct {
// Discriminator is a stand-in for discriminator that answers each question as
// answer does, and keeps the questions and the reports it gets.
type Discriminator struct {
*httptest.Server
questions chan question
reports chan servewaf.Report
reports chan discriminator.Report
}
// startServeWAF starts a stand-in for servewaf.
func startServeWAF(t *testing.T, answer http.HandlerFunc) *serveWAF {
// startDiscriminator starts a stand-in for discriminator.
func startDiscriminator(t *testing.T, answer http.HandlerFunc) *Discriminator {
t.Helper()
s := &serveWAF{
s := &Discriminator{
questions: make(chan question, 10),
reports: make(chan servewaf.Report, 10),
reports: make(chan discriminator.Report, 10),
}
s.Server = httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(servewaf.ClientHeader) == "" {
if r.Header.Get(discriminator.ClientHeader) == "" {
s.takeReport(t, w, r)
return
@@ -209,14 +209,17 @@ func startServeWAF(t *testing.T, answer http.HandlerFunc) *serveWAF {
return s
}
// takeReport keeps a report and answers it as servewaf does.
func (s *serveWAF) takeReport(t *testing.T, w http.ResponseWriter, r *http.Request) {
// takeReport keeps a report and answers it as discriminator does.
func (s *Discriminator) takeReport(
t *testing.T, w http.ResponseWriter, r *http.Request,
) {
t.Helper()
var report servewaf.Report
var report discriminator.Report
err := json.NewDecoder(r.Body).Decode(&report)
if err != nil || r.Method != http.MethodPost || r.URL.Path != servewaf.ReportPath {
if err != nil || r.Method != http.MethodPost ||
r.URL.Path != discriminator.ReportPath {
t.Errorf("%s %s is no report: %v", r.Method, r.URL.Path, err)
}
@@ -226,7 +229,7 @@ func (s *serveWAF) takeReport(t *testing.T, w http.ResponseWriter, r *http.Reque
}
// wantNoReport checks that no report comes for a moment.
func (s *serveWAF) wantNoReport(t *testing.T) {
func (s *Discriminator) wantNoReport(t *testing.T) {
t.Helper()
select {
@@ -237,7 +240,7 @@ func (s *serveWAF) wantNoReport(t *testing.T) {
}
// report waits for a report.
func (s *serveWAF) report(t *testing.T) servewaf.Report {
func (s *Discriminator) report(t *testing.T) discriminator.Report {
t.Helper()
select {
@@ -246,12 +249,12 @@ func (s *serveWAF) report(t *testing.T) servewaf.Report {
case <-time.After(waitLimit):
t.Fatal("no report")
return servewaf.Report{}
return discriminator.Report{}
}
}
// question waits for a question.
func (s *serveWAF) question(t *testing.T) question {
func (s *Discriminator) question(t *testing.T) question {
t.Helper()
select {
@@ -264,16 +267,17 @@ func (s *serveWAF) question(t *testing.T) question {
}
}
// startServeWAFAnsweringReports starts a stand-in for servewaf that answers each
// startDiscriminatorAnsweringReports starts a stand-in for discriminator that
// answers each
// question as answer does and each report as report does, and returns its URL.
func startServeWAFAnsweringReports(
func startDiscriminatorAnsweringReports(
t *testing.T, answer, report http.HandlerFunc,
) string {
t.Helper()
backend := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(servewaf.ClientHeader) != "" {
if r.Header.Get(discriminator.ClientHeader) != "" {
answer(w, r)
return
@@ -295,7 +299,7 @@ func allow(w http.ResponseWriter, _ *http.Request) {
// deny answers a question with deny, status 403 and a body of its own.
func deny(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(dispositionHeader, dispositionDeny)
w.Header().Set(fromServeWAFHeader, addedHeaderValue)
w.Header().Set(fromDiscriminatorHeader, addedHeaderValue)
w.WriteHeader(http.StatusForbidden)
_, _ = io.WriteString(w, deniedBody)
}
@@ -316,7 +320,7 @@ func greet(w http.ResponseWriter, r *http.Request) {
_, _ = io.WriteString(w, greeting)
}
// startRProxy starts rproxy with servewaf at backend and the app at appURL, and
// startRProxy starts rproxy with discriminator at backend and the app at appURL, and
// the settings in env on top of the defaults. It returns where rproxy listens
// and what it writes.
func startRProxy(
@@ -498,7 +502,7 @@ func readStatus(t *testing.T, conn net.Conn) int {
func TestPassesAnAllowedRequestToTheAppAndItsAnswerBack(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(w http.ResponseWriter, r *http.Request) {
standIn := startDiscriminator(t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(asnHeader, "AS64496")
w.Header().Set(countryHeader, "DE")
allow(w, r)
@@ -557,7 +561,7 @@ func TestAPeerNotTrustedHasItsForwardedHeadersReplaced(t *testing.T) {
"X-Forwarded-Tls-Client-Cert-Info", "X-Real-Ip",
}
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
seen := make(chan http.Header, 1)
app := startApp(t, func(w http.ResponseWriter, r *http.Request) {
seen <- r.Header.Clone()
@@ -602,7 +606,7 @@ func TestAPeerNotTrustedHasItsForwardedHeadersReplaced(t *testing.T) {
func TestATrustedProxysForwardedHeadersPassOn(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
seen := make(chan http.Header, 1)
app := startApp(t, func(w http.ResponseWriter, r *http.Request) {
seen <- r.Header.Clone()
@@ -641,7 +645,7 @@ func TestATrustedProxysForwardedHeadersPassOn(t *testing.T) {
func TestAnAnswerWithoutAContentTypeGetsNone(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, func(w http.ResponseWriter, _ *http.Request) {
w.Header()["Content-Type"] = nil
_, _ = io.WriteString(w, "<html>")
@@ -663,19 +667,19 @@ func TestAnAnswerWithoutAContentTypeGetsNone(t *testing.T) {
}
}
func TestRemovesTheVisitorsLookupHeadersWhenServeWAFGivesNone(t *testing.T) {
func TestRemovesTheVisitorsLookupHeadersWhenDiscriminatorGivesNone(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
backend func(t *testing.T) string
}{
{"servewaf allows", func(t *testing.T) string {
{"discriminator allows", func(t *testing.T) string {
t.Helper()
return startServeWAF(t, allow).URL
return startDiscriminator(t, allow).URL
}},
{"servewaf is down", func(*testing.T) string { return nowhere }},
{"discriminator is down", func(*testing.T) string { return nowhere }},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -701,10 +705,10 @@ func TestRemovesTheVisitorsLookupHeadersWhenServeWAFGivesNone(t *testing.T) {
}
}
func TestDenyGivesTheVisitorServeWAFsAnswer(t *testing.T) {
func TestDenyGivesTheVisitorDiscriminatorsAnswer(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, deny)
standIn := startDiscriminator(t, deny)
app := startApp(t, func(http.ResponseWriter, *http.Request) {
t.Error("the app was asked")
})
@@ -712,8 +716,8 @@ func TestDenyGivesTheVisitorServeWAFsAnswer(t *testing.T) {
got := get(t, addr, "/")
if got.status != http.StatusForbidden || got.body != deniedBody ||
got.header.Get(fromServeWAFHeader) != addedHeaderValue {
t.Errorf("answered %+v, want servewaf's answer", got)
got.header.Get(fromDiscriminatorHeader) != addedHeaderValue {
t.Errorf("answered %+v, want discriminator's answer", got)
}
if len(got.header.Values(dispositionHeader)) != 0 {
@@ -729,21 +733,21 @@ func TestDenyGivesTheVisitorServeWAFsAnswer(t *testing.T) {
line := out.requestLine(t)
if line["disposition"] != dispositionDeny || line["default_disposition"] != nil {
t.Errorf("line %v, want servewaf's deny", line)
t.Errorf("line %v, want discriminator's deny", line)
}
}
func TestADeniedHeadKeepsServeWAFsContentLength(t *testing.T) {
func TestADeniedHeadKeepsDiscriminatorsContentLength(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, deny)
standIn := startDiscriminator(t, deny)
app := startApp(t, greet)
addr, _ := startRProxy(t, standIn.URL, app.URL, nil)
got := visit(t, newRequest(t, http.MethodHead, addr, "/", http.NoBody))
if got.status != http.StatusForbidden ||
got.header.Get(contentLengthHeader) != strconv.Itoa(len(deniedBody)) {
t.Errorf("answered %+v, want servewaf's 403 and its length", got)
t.Errorf("answered %+v, want discriminator's 403 and its length", got)
}
}
@@ -758,13 +762,13 @@ func TestAnAnswerRProxyHoldsWholeIsSentBeforeItsLogLine(t *testing.T) {
}{
{"a deny", "GET /" + host, deniedBody, http.StatusForbidden},
{"rproxy's own answer", "GET x:admin" + host, "Bad Request\n", http.StatusBadRequest},
{"the health check", "GET " + servewaf.HealthPath + host, "ok\n", http.StatusOK},
{"the health check", "GET " + discriminator.HealthPath + host, "ok\n", http.StatusOK},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
log := heldLog{release: make(chan struct{})}
standIn := startServeWAF(t, deny)
standIn := startDiscriminator(t, deny)
app := startApp(t, greet)
addr, _ := startServerLogging(t, standIn.URL, app.URL, nil, log, io.Discard)
@@ -794,7 +798,7 @@ func TestAnAnswerRProxyHoldsWholeIsSentBeforeItsLogLine(t *testing.T) {
func TestRProxysOwnAnswerIsPlainText(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, greet)
addr, _ := startRProxy(t, standIn.URL, app.URL, nil)
@@ -817,7 +821,7 @@ func TestRProxysOwnAnswerIsPlainText(t *testing.T) {
func TestCloseClosesTheConnectionWithoutAnAnswer(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(w http.ResponseWriter, _ *http.Request) {
standIn := startDiscriminator(t, func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(dispositionHeader, dispositionClose)
w.WriteHeader(http.StatusOK)
})
@@ -849,7 +853,7 @@ func TestCloseClosesTheConnectionWithoutAnAnswer(t *testing.T) {
func TestALineForARequestClosedWithoutAnAnswerHasNoNegativeTime(t *testing.T) {
t.Parallel()
backend := startServeWAFAnsweringReports(t,
backend := startDiscriminatorAnsweringReports(t,
func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(dispositionHeader, dispositionClose)
},
@@ -874,17 +878,17 @@ func TestALineForARequestClosedWithoutAnAnswerHasNoNegativeTime(t *testing.T) {
}
}
// answeringWith returns what starts a stand-in for servewaf that answers each
// answeringWith returns what starts a stand-in for discriminator that answers each
// question as answer does, and returns its URL.
func answeringWith(answer http.HandlerFunc) func(t *testing.T) string {
return func(t *testing.T) string {
t.Helper()
return startServeWAF(t, answer).URL
return startDiscriminator(t, answer).URL
}
}
// noAnswers are the ways servewaf can leave the decision to the default.
// noAnswers are the ways discriminator can leave the decision to the default.
func noAnswers() map[string]func(t *testing.T) string {
return map[string]func(t *testing.T) string{
"no connection": func(*testing.T) string { return nowhere },
@@ -916,7 +920,7 @@ func noAnswers() map[string]func(t *testing.T) string {
}
}
func TestAnAnswerOtherThanAllowGoesToTheVisitorAsServeWAFGaveIt(t *testing.T) {
func TestAnAnswerOtherThanAllowGoesToTheVisitorAsDiscriminatorGaveIt(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
@@ -925,7 +929,7 @@ func TestAnAnswerOtherThanAllowGoesToTheVisitorAsServeWAFGaveIt(t *testing.T) {
}{
{"a refusal", dispositionDeny, deniedBody, http.StatusForbidden},
{"a refusal, 429", dispositionDeny, deniedBody, http.StatusTooManyRequests},
{"servewaf's 400", "", "refused\n", http.StatusBadRequest},
{"discriminator's 400", "", "refused\n", http.StatusBadRequest},
{"a 500", "", "broken\n", http.StatusInternalServerError},
{"a 503 denied", dispositionDeny, "busy\n", http.StatusServiceUnavailable},
{"no header", "", "who knows\n", http.StatusOK},
@@ -935,12 +939,12 @@ func TestAnAnswerOtherThanAllowGoesToTheVisitorAsServeWAFGaveIt(t *testing.T) {
t.Run(tc.name+" with the default "+setting, func(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(w http.ResponseWriter, _ *http.Request) {
standIn := startDiscriminator(t, func(w http.ResponseWriter, _ *http.Request) {
if tc.disposition != "" {
w.Header().Set(dispositionHeader, tc.disposition)
}
w.Header().Set(fromServeWAFHeader, addedHeaderValue)
w.Header().Set(fromDiscriminatorHeader, addedHeaderValue)
w.WriteHeader(tc.status)
_, _ = io.WriteString(w, tc.body)
})
@@ -953,8 +957,8 @@ func TestAnAnswerOtherThanAllowGoesToTheVisitorAsServeWAFGaveIt(t *testing.T) {
got := get(t, addr, "/")
if got.status != tc.status || got.body != tc.body ||
got.header.Get(fromServeWAFHeader) != addedHeaderValue {
t.Errorf("answered %+v, want servewaf's answer", got)
got.header.Get(fromDiscriminatorHeader) != addedHeaderValue {
t.Errorf("answered %+v, want discriminator's answer", got)
}
if len(got.header.Values(dispositionHeader)) != 0 {
@@ -966,8 +970,8 @@ func TestAnAnswerOtherThanAllowGoesToTheVisitorAsServeWAFGaveIt(t *testing.T) {
t.Errorf("line %v, want status %d and no default", line, tc.status)
}
if out.has("servewaf gave no answer") {
t.Error("rproxy warned that servewaf gave no answer")
if out.has("discriminator gave no answer") {
t.Error("rproxy warned that discriminator gave no answer")
}
})
}
@@ -977,14 +981,14 @@ func TestAnAnswerOtherThanAllowGoesToTheVisitorAsServeWAFGaveIt(t *testing.T) {
func TestAnAnswerWithoutAValidDispositionGetsNoReport(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(w http.ResponseWriter, _ *http.Request) {
standIn := startDiscriminator(t, func(w http.ResponseWriter, _ *http.Request) {
http.Error(w, "refused", http.StatusBadRequest)
})
app := startApp(t, greet)
addr, out := startRProxy(t, standIn.URL, app.URL, nil)
if got := get(t, addr, "/"); got.status != http.StatusBadRequest {
t.Errorf("answered %+v, want servewaf's 400", got)
t.Errorf("answered %+v, want discriminator's 400", got)
}
if line := out.requestLine(t); line["disposition"] != nil {
@@ -997,7 +1001,7 @@ func TestAnAnswerWithoutAValidDispositionGetsNoReport(t *testing.T) {
func TestAnAnswerWithoutAContentTypeGoesToTheVisitorWithNone(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(w http.ResponseWriter, _ *http.Request) {
standIn := startDiscriminator(t, func(w http.ResponseWriter, _ *http.Request) {
w.Header()["Content-Type"] = nil
w.WriteHeader(http.StatusInternalServerError)
_, _ = io.WriteString(w, "<html>broken")
@@ -1015,7 +1019,7 @@ func TestAnAnswerOf1MiBGoesToTheVisitor(t *testing.T) {
const size = 1 << 20
standIn := startServeWAF(t, func(w http.ResponseWriter, _ *http.Request) {
standIn := startDiscriminator(t, func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write(make([]byte, size))
})
@@ -1023,15 +1027,16 @@ func TestAnAnswerOf1MiBGoesToTheVisitor(t *testing.T) {
got := get(t, addr, "/")
if got.status != http.StatusInternalServerError || len(got.body) != size {
t.Errorf("answered %d with %d bytes, want servewaf's 500 with %d",
t.Errorf("answered %d with %d bytes, want discriminator's 500 with %d",
got.status, len(got.body), size)
}
}
// startServeWAFWriting starts a stand-in for servewaf that reads each question and
// startDiscriminatorWriting starts a stand-in for discriminator that reads
// each question and
// answers it with text, as it is, and returns its URL. It can send what Go's
// server would not.
func startServeWAFWriting(t *testing.T, text string) string {
func startDiscriminatorWriting(t *testing.T, text string) string {
t.Helper()
listener, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", localhost+":0")
@@ -1101,7 +1106,7 @@ func TestAnAnswerThatCannotBePassedOnIsAnswered502(t *testing.T) {
"a status below 100": func(t *testing.T) string {
t.Helper()
return startServeWAFWriting(t, "HTTP/1.1 099 Odd\r\nContent-Length: 0\r\n\r\n")
return startDiscriminatorWriting(t, "HTTP/1.1 099 Odd\r\nContent-Length: 0\r\n\r\n")
},
"a 101 that holds its connection open": answeringWith(switchAndHold),
} {
@@ -1126,7 +1131,7 @@ func TestAnAnswerThatCannotBePassedOnIsAnswered502(t *testing.T) {
t.Errorf("line %v, want status 502 and no disposition", line)
}
out.waitFor(t, "servewaf's answer is dropped")
out.waitFor(t, "discriminator's answer is dropped")
})
}
}
@@ -1135,7 +1140,7 @@ func TestAnAnswerThatCannotBePassedOnIsAnswered502(t *testing.T) {
func TestAnAnswerThatCannotBePassedOnGetsNoReport(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, overOneMiB(dispositionDeny))
standIn := startDiscriminator(t, overOneMiB(dispositionDeny))
addr, out := startRProxy(t, standIn.URL, nowhere, nil)
if got := get(t, addr, "/"); got.status != http.StatusBadGateway {
@@ -1192,14 +1197,14 @@ func TestWithoutAnAnswerTheDefaultDispositionDecides(t *testing.T) {
}
}
func TestAVisitorThatGoesAwayWhileServeWAFIsAskedGetsNoDefault(t *testing.T) {
func TestAVisitorThatGoesAwayWhileDiscriminatorIsAskedGetsNoDefault(t *testing.T) {
t.Parallel()
for _, disposition := range []string{dispositionAllow, dispositionDeny} {
t.Run("with the default "+disposition, func(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(_ http.ResponseWriter, r *http.Request) {
standIn := startDiscriminator(t, func(_ http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
})
app := startApp(t, func(http.ResponseWriter, *http.Request) {
@@ -1222,8 +1227,8 @@ func TestAVisitorThatGoesAwayWhileServeWAFIsAskedGetsNoDefault(t *testing.T) {
t.Errorf("line %v, want status 0 and no disposition", line)
}
if out.has("servewaf gave no answer") {
t.Error("rproxy warned that servewaf gave no answer")
if out.has("discriminator gave no answer") {
t.Error("rproxy warned that discriminator gave no answer")
}
standIn.wantNoReport(t)
@@ -1238,13 +1243,13 @@ func TestRProxyWarnsWhenTheDefaultDecides(t *testing.T) {
addr, out := startRProxy(t, nowhere, app.URL, nil)
wantGreeting(t, get(t, addr, "/"))
out.waitFor(t, "servewaf gave no answer")
out.waitFor(t, "discriminator gave no answer")
}
func TestSendsNoReportWithoutAnAnswer(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, func(_ http.ResponseWriter, r *http.Request) {
standIn := startDiscriminator(t, func(_ http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
})
app := startApp(t, greet)
@@ -1265,35 +1270,35 @@ func TestAPathUnderOwnPrefixNeverReachesTheApp(t *testing.T) {
})
addr, _ := startRProxy(t, nowhere, app.URL, nil)
got := get(t, addr, "/_smallwebwaf/metrics")
got := get(t, addr, "/_sww/metrics")
if got.status != http.StatusNotFound {
t.Errorf("answered %+v, want 404", got)
}
}
func TestHealthCheckIsAnsweredWhileServeWAFIsDown(t *testing.T) {
func TestHealthCheckIsAnsweredWhileDiscriminatorIsDown(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, greet)
addr, out := startRProxy(t, nowhere, app.URL, nil)
got := get(t, addr, servewaf.HealthPath)
got := get(t, addr, discriminator.HealthPath)
if got.status != http.StatusOK || got.body != "ok\n" {
t.Errorf("answered %+v, want ok", got)
}
line := out.requestLine(t)
if line["path"] != servewaf.HealthPath || line["disposition"] != nil {
if line["path"] != discriminator.HealthPath || line["disposition"] != nil {
t.Errorf("line %v, want one with no disposition", line)
}
// With servewaf up too, it is not asked.
// With discriminator up too, it is not asked.
addr, _ = startRProxy(t, standIn.URL, app.URL, nil)
get(t, addr, servewaf.HealthPath)
get(t, addr, discriminator.HealthPath)
if len(standIn.questions) != 0 {
t.Error("servewaf was asked about the health check")
t.Error("discriminator was asked about the health check")
}
}
@@ -1301,7 +1306,7 @@ func TestTheVisitorDoesNotWaitForTheReport(t *testing.T) {
t.Parallel()
release := make(chan struct{})
backend := startServeWAFAnsweringReports(t, deny,
backend := startDiscriminatorAnsweringReports(t, deny,
func(w http.ResponseWriter, r *http.Request) {
select {
case <-release:
@@ -1336,16 +1341,16 @@ func TestTheVisitorDoesNotWaitForTheReport(t *testing.T) {
_ = res.Body.Close()
if res.StatusCode != http.StatusForbidden {
t.Errorf("status %d, want servewaf's 403", res.StatusCode)
t.Errorf("status %d, want discriminator's 403", res.StatusCode)
}
}
}
func TestAReportServeWAFLeavesUnansweredIsDroppedAtTheTimeLimit(t *testing.T) {
func TestAReportDiscriminatorLeavesUnansweredIsDroppedAtTheTimeLimit(t *testing.T) {
t.Parallel()
release := make(chan struct{})
backend := startServeWAFAnsweringReports(t, allow,
backend := startDiscriminatorAnsweringReports(t, allow,
func(w http.ResponseWriter, _ *http.Request) {
<-release
@@ -1365,10 +1370,10 @@ func TestAReportServeWAFLeavesUnansweredIsDroppedAtTheTimeLimit(t *testing.T) {
out.waitFor(t, "a report is dropped")
}
func TestAReportServeWAFRefusesIsDroppedWithAWarning(t *testing.T) {
func TestAReportDiscriminatorRefusesIsDroppedWithAWarning(t *testing.T) {
t.Parallel()
backend := startServeWAFAnsweringReports(t, allow,
backend := startDiscriminatorAnsweringReports(t, allow,
func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusBadRequest)
})
@@ -1383,7 +1388,7 @@ func TestAReportServeWAFRefusesIsDroppedWithAWarning(t *testing.T) {
func TestAReportAnswered101IsDroppedAtOnce(t *testing.T) {
t.Parallel()
backend := startServeWAFAnsweringReports(t, allow, switchAndHold)
backend := startDiscriminatorAnsweringReports(t, allow, switchAndHold)
app := startApp(t, greet)
addr, out := startRProxy(t, backend, app.URL, map[string]string{
timeoutSetting: "60000",
@@ -1396,7 +1401,7 @@ func TestAReportAnswered101IsDroppedAtOnce(t *testing.T) {
func TestAReportDueAfterWaitForReportsIsDropped(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, greet)
addr, out, server := startServer(t, standIn.URL, app.URL, nil)
@@ -1412,7 +1417,7 @@ func TestReportGivesWhatHappened(t *testing.T) {
const body = "the visitor's body"
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, greet)
addr, out := startRProxy(t, standIn.URL, app.URL, nil)
@@ -1442,7 +1447,7 @@ func TestWaitForReportsWaitsForAReportBeingSent(t *testing.T) {
release := make(chan struct{})
backend := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(servewaf.ClientHeader) != "" {
if r.Header.Get(discriminator.ClientHeader) != "" {
allow(w, r)
return
@@ -1497,7 +1502,7 @@ func TestWaitForReportsWaitsForAReportBeingSent(t *testing.T) {
func TestReportOfAWebSocketCountsWhatItCarried(t *testing.T) {
t.Parallel()
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, echoAfterUpgrade)
addr, out := startRProxy(t, standIn.URL, app.URL, map[string]string{
maxRequestTime: "2000",
@@ -1552,7 +1557,7 @@ func TestAWebSocketPassesOnThatTheVisitorStoppedSending(t *testing.T) {
const goodbye = "sent after the visitor stopped sending\n"
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, func(w http.ResponseWriter, _ *http.Request) {
conn, buffered, err := http.NewResponseController(w).Hijack()
if err != nil {
@@ -1610,7 +1615,7 @@ func TestAWebSocketPassesOnThatTheVisitorStoppedSending(t *testing.T) {
//nolint:paralleltest // see above
func TestASwitchNotPassedOnClosesTheAppsConnection(t *testing.T) {
closed := make(chan error, 1)
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, func(w http.ResponseWriter, _ *http.Request) {
conn, buffered, err := http.NewResponseController(w).Hijack()
if err != nil {
@@ -1681,7 +1686,7 @@ func TestEachRequestGetsAFreshID(t *testing.T) {
const fromTraefik = "an-id-from-traefik"
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, greet)
addr, _ := startRProxy(t, standIn.URL, app.URL, map[string]string{
trustedProxies: localhost + "/32",
@@ -1711,7 +1716,7 @@ func TestLogLineGivesTheRequestAsSent(t *testing.T) {
body = "the visitor's body"
)
standIn := startServeWAF(t, allow)
standIn := startDiscriminator(t, allow)
app := startApp(t, greet)
addr, out := startRProxy(t, standIn.URL, app.URL, nil)