Read every setting through viper and rename the settings (closes #211)
check / check (push) Waiting to run

Every setting of waf and rproxy is read through viper, in place of the
os.LookupEnv function Main passed down. NewViper turns AutomaticEnv and
AllowEmptyEnv on, so a setting that is set but empty reads as set. A
setting may also be given as a file, the _FILE form, which rproxy's
settings now have too; rproxy still writes nothing to disk.

The settings are renamed: waf's take the prefix SWW_WAF_, rproxy's take
SWW_RPROXY_, and the shared SWW_COMMAND keeps SWW_. The Core Rule Set's
settings take SWW_WAF_CORE_RULE_SET_ rather than SWW_WAF_WAF_.

Model: deepseek-v4-pro (first version); opus-5-5 (rework)
This commit is contained in:
sneak committed 2026-11-11 11:18:21 +00:00
1 parent b6aa2cbc4b
commit c6ab946c86
124 files changed
+2293 -2174

No files matched your search

+18 -18
View File
@@ -34,14 +34,14 @@ const plainText = "text/plain; charset=utf-8"
// minSendTime is the least time to send an answer rproxy gives itself, or the
// status and headers of the app's answer. A shorter time can run out before the
// first byte is written, on a busy host or as SWRP_MAX_RESPONSE_TIME_MS runs out,
// first byte is written, on a busy host or as SWW_RPROXY_MAX_RESPONSE_TIME_MS runs out,
// and the visitor then gets a closed connection and no answer.
const minSendTime = 10 * time.Second
// errResponseTooLate refuses an app's answer that arrives once
// SWRP_MAX_RESPONSE_TIME_MS has run out.
// SWW_RPROXY_MAX_RESPONSE_TIME_MS has run out.
var errResponseTooLate = errors.New(
"the answer arrived after SWRP_MAX_RESPONSE_TIME_MS ran out")
"the answer arrived after SWW_RPROXY_MAX_RESPONSE_TIME_MS ran out")
// request is one request on its way through rproxy, from the moment its headers
// have been read to its log line and report.
@@ -61,10 +61,10 @@ type request struct {
start time.Time
// disposition is what is done with the request: waf's valid disposition,
// or SWRP_DEFAULT_DISPOSITION. It is "" when neither gave one.
// or SWW_RPROXY_DEFAULT_DISPOSITION. It is "" when neither gave one.
disposition string
// answered is true when waf gave disposition, which makes a report due,
// and byDefault when SWRP_DEFAULT_DISPOSITION did.
// and byDefault when SWW_RPROXY_DEFAULT_DISPOSITION did.
answered bool
byDefault bool
// asn and country are waf's X-Client-ASN and X-Client-Country, for the
@@ -127,10 +127,10 @@ func (rq *request) answerHealth() {
}
// readFirstPartOfBody reads up to the first 64 KiB of the body into memory,
// within SWRP_MAX_REQUEST_TIME_MS, and reports whether the request goes on. A
// within SWW_RPROXY_MAX_REQUEST_TIME_MS, and reports whether the request goes on. A
// visitor too slow to send it is answered 408. For a malformed chunk, or a
// visitor that went away or stopped its sending side, the connection is closed
// without an answer. A body over SWRP_MAX_REQUEST_SIZE goes on with the part
// without an answer. A body over SWW_RPROXY_MAX_REQUEST_SIZE goes on with the part
// read, so that waf is asked first; passToApp then refuses it.
func (rq *request) readFirstPartOfBody() bool {
if rq.body == nil {
@@ -198,7 +198,7 @@ func (rq *request) forward(ctx context.Context) {
}
// rewrite makes the request the app receives: the visitor's request, unchanged,
// sent to SWRP_UPSTREAM_URL, with the forwarded headers and the request's id set,
// sent to SWW_RPROXY_UPSTREAM_URL, with the forwarded headers and the request's id set,
// without any X-Client-ASN or X-Client-Country the visitor sent, and with
// waf's.
func (rq *request) rewrite(pr *httputil.ProxyRequest) {
@@ -247,7 +247,7 @@ func (rq *request) modifyResponse(res *http.Response) error {
requestEnd := rq.endRequest()
// An answer that arrives once SWRP_MAX_RESPONSE_TIME_MS has run out is not
// An answer that arrives once SWW_RPROXY_MAX_RESPONSE_TIME_MS has run out is not
// passed on: the visitor is answered 504.
limit := rq.h.config.MaxResponseTime
if limit > 0 && !time.Now().Before(requestEnd.Add(limit)) {
@@ -273,7 +273,7 @@ func (rq *request) modifyResponse(res *http.Response) error {
rq.closeAfterAnswer()
// An answer that arrives before the app has been sent the whole request ends
// the request, and SWRP_MAX_RESPONSE_TIME_MS starts.
// the request, and SWW_RPROXY_MAX_RESPONSE_TIME_MS starts.
rq.startResponseTimer(requestEnd)
res.Body = &responseBody{body: limitBody(res.Body, maxBytes), rq: rq}
@@ -361,7 +361,7 @@ func (rq *request) closeAfterAnswer() {
// prepareOwnAnswer readies the connection for an answer rproxy gives itself: the
// reading of a body that has not arrived whole ends at deadline, and the answer
// has SWRP_MAX_RESPONSE_TIME_MS from now to reach the visitor, even when the app
// has SWW_RPROXY_MAX_RESPONSE_TIME_MS from now to reach the visitor, even when the app
// used that time up first, and at least minSendTime.
func (rq *request) prepareOwnAnswer(deadline time.Time) {
rq.stopReadingBody(deadline)
@@ -470,7 +470,7 @@ func (rq *request) bytesEachWay() (int64, int64) {
}
// requestDeadline is when the visitor must have sent its whole request, or zero
// when SWRP_MAX_REQUEST_TIME_MS is off.
// when SWW_RPROXY_MAX_REQUEST_TIME_MS is off.
func (rq *request) requestDeadline() time.Time {
limit := rq.h.config.MaxRequestTime
if limit == 0 {
@@ -511,7 +511,7 @@ func (rq *request) stopPassingOnBody(deadline time.Time) {
rq.stopReadingBody(deadline)
}
// startRequestTimer starts SWRP_MAX_REQUEST_TIME_MS, which runs from the
// startRequestTimer starts SWW_RPROXY_MAX_REQUEST_TIME_MS, which runs from the
// request's headers being read until the app has been sent the whole request.
func (rq *request) startRequestTimer() {
deadline := rq.requestDeadline()
@@ -525,7 +525,7 @@ func (rq *request) startRequestTimer() {
rq.requestTimer = time.AfterFunc(time.Until(deadline), rq.requestTimedOut)
}
// requestTimedOut is called when SWRP_MAX_REQUEST_TIME_MS runs out while the
// requestTimedOut is called when SWW_RPROXY_MAX_REQUEST_TIME_MS runs out while the
// request is still on its way to the app. The answer names the side rproxy was
// waiting on at that moment: 408 when it was waiting for the visitor to send more
// of its body, 504 when it was waiting for the app to be reached or to take what
@@ -552,7 +552,7 @@ func (rq *request) requestTimedOut() {
}
// wroteRequest is called once the app has been sent the whole request:
// SWRP_MAX_REQUEST_TIME_MS ends and SWRP_MAX_RESPONSE_TIME_MS starts.
// SWW_RPROXY_MAX_REQUEST_TIME_MS ends and SWW_RPROXY_MAX_RESPONSE_TIME_MS starts.
func (rq *request) wroteRequest(info httptrace.WroteRequestInfo) {
if info.Err != nil {
return // the transport gives up, or tries again
@@ -567,7 +567,7 @@ func (rq *request) wroteRequest(info httptrace.WroteRequestInfo) {
rq.startResponseTimer(requestEnd)
}
// startResponseTimer starts SWRP_MAX_RESPONSE_TIME_MS, which runs from
// startResponseTimer starts SWW_RPROXY_MAX_RESPONSE_TIME_MS, which runs from
// requestEnd, unless it has started already.
func (rq *request) startResponseTimer(requestEnd time.Time) {
limit := rq.h.config.MaxResponseTime
@@ -586,7 +586,7 @@ func (rq *request) startResponseTimer(requestEnd time.Time) {
rq.responseTimedOut)
}
// responseTimedOut is called when SWRP_MAX_RESPONSE_TIME_MS runs out before the
// responseTimedOut is called when SWW_RPROXY_MAX_RESPONSE_TIME_MS runs out before the
// app has sent its whole answer.
func (rq *request) responseTimedOut() {
rq.mu.Lock()
@@ -604,7 +604,7 @@ func (rq *request) responseReceived() {
// setResponseDeadline sends the status and headers of the app's answer, with
// at least minSendTime, before ReverseProxy reads the answer's body, and then
// the rest has until SWRP_MAX_RESPONSE_TIME_MS after the end of the request.
// the rest has until SWW_RPROXY_MAX_RESPONSE_TIME_MS after the end of the request.
func (rq *request) setResponseDeadline() {
limit := rq.h.config.MaxResponseTime
if limit == 0 {