Commit 9a22cda1 authored by Aaron Taylor's avatar Aaron Taylor Committed by Matt Holt

httpserver: give each req context a Replacer that preserves custom values (#1937)

This allows custom replacements to be defined in a way that propagates
throughout all plugins.
parent 169ab3ac
...@@ -214,6 +214,9 @@ func SameNext(next1, next2 Handler) bool { ...@@ -214,6 +214,9 @@ func SameNext(next1, next2 Handler) bool {
// Context key constants. // Context key constants.
const ( const (
// ReplacerCtxKey is the context key for a per-request replacer.
ReplacerCtxKey caddy.CtxKey = "replacer"
// RemoteUserCtxKey is the key for the remote user of the request, if any (basicauth). // RemoteUserCtxKey is the key for the remote user of the request, if any (basicauth).
RemoteUserCtxKey caddy.CtxKey = "remote_user" RemoteUserCtxKey caddy.CtxKey = "remote_user"
......
...@@ -102,6 +102,18 @@ func (lw *limitWriter) String() string { ...@@ -102,6 +102,18 @@ func (lw *limitWriter) String() string {
// emptyValue should be the string that is used in place // emptyValue should be the string that is used in place
// of empty string (can still be empty string). // of empty string (can still be empty string).
func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Replacer { func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Replacer {
repl := &replacer{
request: r,
responseRecorder: rr,
emptyValue: emptyValue,
}
// extract customReplacements from a request replacer when present.
if existing, ok := r.Context().Value(ReplacerCtxKey).(*replacer); ok {
repl.requestBody = existing.requestBody
repl.customReplacements = existing.customReplacements
} else {
// if there is no existing replacer, build one from scratch.
rb := newLimitWriter(MaxLogBodySize) rb := newLimitWriter(MaxLogBodySize)
if r.Body != nil { if r.Body != nil {
r.Body = struct { r.Body = struct {
...@@ -109,13 +121,11 @@ func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Repla ...@@ -109,13 +121,11 @@ func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Repla
io.Closer io.Closer
}{io.TeeReader(r.Body, rb), io.Closer(r.Body)} }{io.TeeReader(r.Body, rb), io.Closer(r.Body)}
} }
return &replacer{ repl.requestBody = rb
request: r, repl.customReplacements = make(map[string]string)
requestBody: rb,
responseRecorder: rr,
customReplacements: make(map[string]string),
emptyValue: emptyValue,
} }
return repl
} }
func canLogRequest(r *http.Request) bool { func canLogRequest(r *http.Request) bool {
......
...@@ -356,6 +356,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -356,6 +356,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c := context.WithValue(r.Context(), OriginalURLCtxKey, urlCopy) c := context.WithValue(r.Context(), OriginalURLCtxKey, urlCopy)
r = r.WithContext(c) r = r.WithContext(c)
// Setup a replacer for the request that keeps track of placeholder
// values across plugins.
replacer := NewReplacer(r, nil, "")
c = context.WithValue(r.Context(), ReplacerCtxKey, replacer)
r = r.WithContext(c)
w.Header().Set("Server", caddy.AppName) w.Header().Set("Server", caddy.AppName)
status, _ := s.serveHTTP(w, r) status, _ := s.serveHTTP(w, r)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment