diff --git a/caddyhttp/httpserver/replacer.go b/caddyhttp/httpserver/replacer.go index e0299b8bf..cae5870f5 100644 --- a/caddyhttp/httpserver/replacer.go +++ b/caddyhttp/httpserver/replacer.go @@ -3,6 +3,7 @@ package httpserver import ( "net" "net/http" + "net/http/httputil" "net/url" "os" "path" @@ -11,6 +12,14 @@ import ( "time" ) +// requestReplacer is a strings.Replacer which is used to +// encode literal \r and \n characters and keep everything +// on one line +var requestReplacer = strings.NewReplacer( + "\r", "\\r", + "\n", "\\n", +) + // Replacer is a type which can replace placeholder // substrings in a string with actual values from a // http.Request and ResponseRecorder. Always use @@ -95,6 +104,14 @@ func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Repla dir, _ := path.Split(r.URL.Path) return dir }(), + "{request}": func() string { + dump, err := httputil.DumpRequest(r, false) + if err != nil { + return "" + } + + return requestReplacer.Replace(string(dump)) + }(), }, emptyValue: emptyValue, } diff --git a/caddyhttp/httpserver/replacer_test.go b/caddyhttp/httpserver/replacer_test.go index 466e06239..b6f2f458a 100644 --- a/caddyhttp/httpserver/replacer_test.go +++ b/caddyhttp/httpserver/replacer_test.go @@ -74,6 +74,9 @@ func TestReplace(t *testing.T) { if expected, actual := "The Custom header is foobarbaz.", repl.Replace("The Custom header is {>Custom}."); expected != actual { t.Errorf("{>Custom} replacement: expected '%s', got '%s'", expected, actual) } + if expected, actual := "The request is POST / HTTP/1.1\\r\\nHost: localhost\\r\\nCustom: foobarbaz\\r\\nShorterval: 1\\r\\n\\r\\n.", repl.Replace("The request is {request}."); expected != actual { + t.Errorf("{request} replacement: expected '%s', got '%s'", expected, actual) + } // Test header case-insensitivity if expected, actual := "The cUsToM header is foobarbaz...", repl.Replace("The cUsToM header is {>cUsToM}..."); expected != actual {