From 0a798aafacddc061eb0901ee45fdbd11d65fd1db Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 21 Apr 2017 19:54:25 -0600 Subject: [PATCH] mitm, templates, context: Pool buffers to reduce allocations Also disable some tests on context.Hostname because they're not portable --- caddyhttp/httpserver/context.go | 21 ++++++++++++++++----- caddyhttp/httpserver/context_test.go | 16 +++++++++------- caddyhttp/httpserver/mitm.go | 13 +++++++++++-- caddyhttp/templates/setup.go | 7 +++++++ caddyhttp/templates/templates.go | 8 ++++++-- caddyhttp/templates/templates_test.go | 4 ++++ 6 files changed, 53 insertions(+), 16 deletions(-) diff --git a/caddyhttp/httpserver/context.go b/caddyhttp/httpserver/context.go index 33fa602c4..46a73a69f 100644 --- a/caddyhttp/httpserver/context.go +++ b/caddyhttp/httpserver/context.go @@ -11,6 +11,7 @@ import ( "net/url" "path" "strings" + "sync" "text/template" "time" @@ -256,9 +257,6 @@ func (c Context) Markdown(filename string) (string, error) { return string(markdown), nil } -// TemplateFuncs contains user defined functions -var TemplateFuncs = template.FuncMap{} - // ContextInclude opens filename using fs and executes a template with the context ctx. // This does the same thing that Context.Include() does, but with the ability to provide // your own context so that the included files can have access to additional fields your @@ -281,8 +279,10 @@ func ContextInclude(filename string, ctx interface{}, fs http.FileSystem) (strin return "", err } - var buf bytes.Buffer - err = tpl.Execute(&buf, ctx) + buf := includeBufs.Get().(*bytes.Buffer) + buf.Reset() + defer includeBufs.Put(buf) + err = tpl.Execute(buf, ctx) if err != nil { return "", err } @@ -409,3 +409,14 @@ func (c Context) RandomString(minLen, maxLen int) string { return string(result) } + +// buffer pool for .Include context actions +var includeBufs = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +// TemplateFuncs contains user-defined functions +// for execution in templates. +var TemplateFuncs = template.FuncMap{} diff --git a/caddyhttp/httpserver/context_test.go b/caddyhttp/httpserver/context_test.go index dfd5cb226..91d6cfd79 100644 --- a/caddyhttp/httpserver/context_test.go +++ b/caddyhttp/httpserver/context_test.go @@ -251,14 +251,16 @@ func TestHostname(t *testing.T) { inputRemoteAddr string expectedHostname string }{ + // TODO(mholt): Fix these tests, they're not portable. i.e. my resolver + // returns "fwdr-8.fwdr-8.fwdr-8.fwdr-8." instead of these google ones. // Test 0 - ipv4 with port - {"8.8.8.8:1111", "google-public-dns-a.google.com."}, - // Test 1 - ipv4 without port - {"8.8.8.8", "google-public-dns-a.google.com."}, - // Test 2 - ipv6 with port - {"[2001:4860:4860::8888]:11", "google-public-dns-a.google.com."}, - // Test 3 - ipv6 without port and brackets - {"2001:4860:4860::8888", "google-public-dns-a.google.com."}, + // {"8.8.8.8:1111", "google-public-dns-a.google.com."}, + // // Test 1 - ipv4 without port + // {"8.8.8.8", "google-public-dns-a.google.com."}, + // // Test 2 - ipv6 with port + // {"[2001:4860:4860::8888]:11", "google-public-dns-a.google.com."}, + // // Test 3 - ipv6 without port and brackets + // {"2001:4860:4860::8888", "google-public-dns-a.google.com."}, // Test 4 - no hostname available {"1.1.1.1", "1.1.1.1"}, } diff --git a/caddyhttp/httpserver/mitm.go b/caddyhttp/httpserver/mitm.go index 6b18fd3e8..e5403a902 100644 --- a/caddyhttp/httpserver/mitm.go +++ b/caddyhttp/httpserver/mitm.go @@ -133,7 +133,7 @@ func (c *clientHelloConn) Read(b []byte) (n int, err error) { if err != nil { return } - c.buf = nil // buffer no longer needed + bufpool.Put(c.buf) // buffer no longer needed // parse the ClientHello and store it in the map rawParsed := parseRawClientHello(hello) @@ -285,7 +285,9 @@ func (l *tlsHelloListener) Accept() (net.Conn, error) { if err != nil { return nil, err } - helloConn := &clientHelloConn{Conn: conn, listener: l, buf: new(bytes.Buffer)} + buf := bufpool.Get().(*bytes.Buffer) + buf.Reset() + helloConn := &clientHelloConn{Conn: conn, listener: l, buf: buf} return tls.Server(helloConn, l.config), nil } @@ -570,6 +572,13 @@ func hasGreaseCiphers(cipherSuites []uint16) bool { return false } +// pool buffers so we can reuse allocations over time +var bufpool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + var greaseCiphers = map[uint16]struct{}{ 0x0A0A: {}, 0x1A1A: {}, diff --git a/caddyhttp/templates/setup.go b/caddyhttp/templates/setup.go index b8d1ca748..4622afe95 100644 --- a/caddyhttp/templates/setup.go +++ b/caddyhttp/templates/setup.go @@ -1,7 +1,9 @@ package templates import ( + "bytes" "net/http" + "sync" "github.com/mholt/caddy" "github.com/mholt/caddy/caddyhttp/httpserver" @@ -27,6 +29,11 @@ func setup(c *caddy.Controller) error { Rules: rules, Root: cfg.Root, FileSys: http.Dir(cfg.Root), + BufPool: &sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + }, } cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { diff --git a/caddyhttp/templates/templates.go b/caddyhttp/templates/templates.go index 01abb8216..36388fba8 100644 --- a/caddyhttp/templates/templates.go +++ b/caddyhttp/templates/templates.go @@ -9,6 +9,7 @@ import ( "os" "path" "path/filepath" + "sync" "text/template" "github.com/mholt/caddy/caddyhttp/httpserver" @@ -60,8 +61,10 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error } // Execute it - var buf bytes.Buffer - err = tpl.Execute(&buf, ctx) + buf := t.BufPool.Get().(*bytes.Buffer) + buf.Reset() + defer t.BufPool.Put(buf) + err = tpl.Execute(buf, ctx) if err != nil { return http.StatusInternalServerError, err } @@ -97,6 +100,7 @@ type Templates struct { Rules []Rule Root string FileSys http.FileSystem + BufPool *sync.Pool // docs: "A Pool must not be copied after first use." } // Rule represents a template rule. A template will only execute diff --git a/caddyhttp/templates/templates_test.go b/caddyhttp/templates/templates_test.go index f190599c3..c1d53a2de 100644 --- a/caddyhttp/templates/templates_test.go +++ b/caddyhttp/templates/templates_test.go @@ -1,8 +1,10 @@ package templates import ( + "bytes" "net/http" "net/http/httptest" + "sync" "testing" "github.com/mholt/caddy/caddyhttp/httpserver" @@ -28,6 +30,7 @@ func TestTemplates(t *testing.T) { }, Root: "./testdata", FileSys: http.Dir("./testdata"), + BufPool: &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}, } tmplroot := Templates{ @@ -43,6 +46,7 @@ func TestTemplates(t *testing.T) { }, Root: "./testdata", FileSys: http.Dir("./testdata"), + BufPool: &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}, } // Test tmpl on /photos/test.html