Rename the program to sww and its deciding daemon to discriminator
check / check (push) Successful in 3m13s
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:
1 parent
2e2f6f7175
commit
42643be662
106 files changed
+3289
-3262
No files matched your search
+108
-103
@@ -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)
|
||||
|
||||
|
||||
Reference in new issue
Block a user