Commit 0a798aaf authored by Matthew Holt's avatar Matthew Holt

mitm, templates, context: Pool buffers to reduce allocations

Also disable some tests on context.Hostname because they're not portable
parent f8614b87
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"net/url" "net/url"
"path" "path"
"strings" "strings"
"sync"
"text/template" "text/template"
"time" "time"
...@@ -256,9 +257,6 @@ func (c Context) Markdown(filename string) (string, error) { ...@@ -256,9 +257,6 @@ func (c Context) Markdown(filename string) (string, error) {
return string(markdown), nil 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. // 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 // 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 // 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 ...@@ -281,8 +279,10 @@ func ContextInclude(filename string, ctx interface{}, fs http.FileSystem) (strin
return "", err return "", err
} }
var buf bytes.Buffer buf := includeBufs.Get().(*bytes.Buffer)
err = tpl.Execute(&buf, ctx) buf.Reset()
defer includeBufs.Put(buf)
err = tpl.Execute(buf, ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
...@@ -409,3 +409,14 @@ func (c Context) RandomString(minLen, maxLen int) string { ...@@ -409,3 +409,14 @@ func (c Context) RandomString(minLen, maxLen int) string {
return string(result) 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{}
...@@ -251,14 +251,16 @@ func TestHostname(t *testing.T) { ...@@ -251,14 +251,16 @@ func TestHostname(t *testing.T) {
inputRemoteAddr string inputRemoteAddr string
expectedHostname 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 // Test 0 - ipv4 with port
{"8.8.8.8:1111", "google-public-dns-a.google.com."}, // {"8.8.8.8:1111", "google-public-dns-a.google.com."},
// Test 1 - ipv4 without port // // Test 1 - ipv4 without port
{"8.8.8.8", "google-public-dns-a.google.com."}, // {"8.8.8.8", "google-public-dns-a.google.com."},
// Test 2 - ipv6 with port // // Test 2 - ipv6 with port
{"[2001:4860:4860::8888]:11", "google-public-dns-a.google.com."}, // {"[2001:4860:4860::8888]:11", "google-public-dns-a.google.com."},
// Test 3 - ipv6 without port and brackets // // Test 3 - ipv6 without port and brackets
{"2001:4860:4860::8888", "google-public-dns-a.google.com."}, // {"2001:4860:4860::8888", "google-public-dns-a.google.com."},
// Test 4 - no hostname available // Test 4 - no hostname available
{"1.1.1.1", "1.1.1.1"}, {"1.1.1.1", "1.1.1.1"},
} }
......
...@@ -133,7 +133,7 @@ func (c *clientHelloConn) Read(b []byte) (n int, err error) { ...@@ -133,7 +133,7 @@ func (c *clientHelloConn) Read(b []byte) (n int, err error) {
if err != nil { if err != nil {
return 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 // parse the ClientHello and store it in the map
rawParsed := parseRawClientHello(hello) rawParsed := parseRawClientHello(hello)
...@@ -285,7 +285,9 @@ func (l *tlsHelloListener) Accept() (net.Conn, error) { ...@@ -285,7 +285,9 @@ func (l *tlsHelloListener) Accept() (net.Conn, error) {
if err != nil { if err != nil {
return nil, err 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 return tls.Server(helloConn, l.config), nil
} }
...@@ -570,6 +572,13 @@ func hasGreaseCiphers(cipherSuites []uint16) bool { ...@@ -570,6 +572,13 @@ func hasGreaseCiphers(cipherSuites []uint16) bool {
return false 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{}{ var greaseCiphers = map[uint16]struct{}{
0x0A0A: {}, 0x0A0A: {},
0x1A1A: {}, 0x1A1A: {},
......
package templates package templates
import ( import (
"bytes"
"net/http" "net/http"
"sync"
"github.com/mholt/caddy" "github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver" "github.com/mholt/caddy/caddyhttp/httpserver"
...@@ -27,6 +29,11 @@ func setup(c *caddy.Controller) error { ...@@ -27,6 +29,11 @@ func setup(c *caddy.Controller) error {
Rules: rules, Rules: rules,
Root: cfg.Root, Root: cfg.Root,
FileSys: http.Dir(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 { cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"sync"
"text/template" "text/template"
"github.com/mholt/caddy/caddyhttp/httpserver" "github.com/mholt/caddy/caddyhttp/httpserver"
...@@ -60,8 +61,10 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -60,8 +61,10 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
} }
// Execute it // Execute it
var buf bytes.Buffer buf := t.BufPool.Get().(*bytes.Buffer)
err = tpl.Execute(&buf, ctx) buf.Reset()
defer t.BufPool.Put(buf)
err = tpl.Execute(buf, ctx)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
...@@ -97,6 +100,7 @@ type Templates struct { ...@@ -97,6 +100,7 @@ type Templates struct {
Rules []Rule Rules []Rule
Root string Root string
FileSys http.FileSystem 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 // Rule represents a template rule. A template will only execute
......
package templates package templates
import ( import (
"bytes"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"sync"
"testing" "testing"
"github.com/mholt/caddy/caddyhttp/httpserver" "github.com/mholt/caddy/caddyhttp/httpserver"
...@@ -28,6 +30,7 @@ func TestTemplates(t *testing.T) { ...@@ -28,6 +30,7 @@ func TestTemplates(t *testing.T) {
}, },
Root: "./testdata", Root: "./testdata",
FileSys: http.Dir("./testdata"), FileSys: http.Dir("./testdata"),
BufPool: &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }},
} }
tmplroot := Templates{ tmplroot := Templates{
...@@ -43,6 +46,7 @@ func TestTemplates(t *testing.T) { ...@@ -43,6 +46,7 @@ func TestTemplates(t *testing.T) {
}, },
Root: "./testdata", Root: "./testdata",
FileSys: http.Dir("./testdata"), FileSys: http.Dir("./testdata"),
BufPool: &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }},
} }
// Test tmpl on /photos/test.html // Test tmpl on /photos/test.html
......
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