From 9a22cda15dcea8822d0ac5686eba6e084cd19272 Mon Sep 17 00:00:00 2001 From: Aaron Taylor Date: Tue, 7 Nov 2017 12:10:03 -0500 Subject: [PATCH] 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. --- caddyhttp/httpserver/middleware.go | 3 +++ caddyhttp/httpserver/replacer.go | 34 +++++++++++++++++++----------- caddyhttp/httpserver/server.go | 6 ++++++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/caddyhttp/httpserver/middleware.go b/caddyhttp/httpserver/middleware.go index a754e77ce..d470811a9 100644 --- a/caddyhttp/httpserver/middleware.go +++ b/caddyhttp/httpserver/middleware.go @@ -214,6 +214,9 @@ func SameNext(next1, next2 Handler) bool { // Context key constants. 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 caddy.CtxKey = "remote_user" diff --git a/caddyhttp/httpserver/replacer.go b/caddyhttp/httpserver/replacer.go index 629067b69..371526bf6 100644 --- a/caddyhttp/httpserver/replacer.go +++ b/caddyhttp/httpserver/replacer.go @@ -102,20 +102,30 @@ func (lw *limitWriter) String() string { // emptyValue should be the string that is used in place // of empty string (can still be empty string). func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Replacer { - rb := newLimitWriter(MaxLogBodySize) - if r.Body != nil { - r.Body = struct { - io.Reader - io.Closer - }{io.TeeReader(r.Body, rb), io.Closer(r.Body)} + repl := &replacer{ + request: r, + responseRecorder: rr, + emptyValue: emptyValue, } - return &replacer{ - request: r, - requestBody: rb, - responseRecorder: rr, - customReplacements: make(map[string]string), - 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) + 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 { diff --git a/caddyhttp/httpserver/server.go b/caddyhttp/httpserver/server.go index b0f85a056..92f2b6fd7 100644 --- a/caddyhttp/httpserver/server.go +++ b/caddyhttp/httpserver/server.go @@ -356,6 +356,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { c := context.WithValue(r.Context(), OriginalURLCtxKey, urlCopy) 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) status, _ := s.serveHTTP(w, r)