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.
This commit is contained in:
Aaron Taylor 2017-11-07 12:10:03 -05:00 committed by Matt Holt
parent 169ab3acda
commit 9a22cda15d
3 changed files with 31 additions and 12 deletions

View File

@ -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"

View File

@ -102,20 +102,30 @@ 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 {
rb := newLimitWriter(MaxLogBodySize) repl := &replacer{
if r.Body != nil { request: r,
r.Body = struct { responseRecorder: rr,
io.Reader emptyValue: emptyValue,
io.Closer
}{io.TeeReader(r.Body, rb), io.Closer(r.Body)}
} }
return &replacer{
request: r, // extract customReplacements from a request replacer when present.
requestBody: rb, if existing, ok := r.Context().Value(ReplacerCtxKey).(*replacer); ok {
responseRecorder: rr, repl.requestBody = existing.requestBody
customReplacements: make(map[string]string), repl.customReplacements = existing.customReplacements
emptyValue: emptyValue, } else {
// if there is no existing replacer, build one from scratch.
rb := newLimitWriter(MaxLogBodySize)
if r.Body != nil {
r.Body = struct {
io.Reader
io.Closer
}{io.TeeReader(r.Body, rb), io.Closer(r.Body)}
}
repl.requestBody = rb
repl.customReplacements = make(map[string]string)
} }
return repl
} }
func canLogRequest(r *http.Request) bool { func canLogRequest(r *http.Request) bool {

View File

@ -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)