Commit 4c034f6a authored by Matt Holt's avatar Matt Holt Committed by GitHub

Merge pull request #1613 from tw4452852/addlink

Context: add Push action
parents 503c6b39 761a32a0
...@@ -29,6 +29,20 @@ type Context struct { ...@@ -29,6 +29,20 @@ type Context struct {
Req *http.Request Req *http.Request
URL *url.URL URL *url.URL
Args []interface{} // defined by arguments to .Include Args []interface{} // defined by arguments to .Include
// just used for adding preload links for server push
responseHeader http.Header
}
// NewContextWithHeader creates a context with given response header.
//
// To plugin developer:
// The returned context's exported fileds remain empty,
// you should then initialize them if you want.
func NewContextWithHeader(rh http.Header) Context {
return Context{
responseHeader: rh,
}
} }
// Include returns the contents of filename relative to the site root. // Include returns the contents of filename relative to the site root.
...@@ -410,6 +424,15 @@ func (c Context) RandomString(minLen, maxLen int) string { ...@@ -410,6 +424,15 @@ func (c Context) RandomString(minLen, maxLen int) string {
return string(result) return string(result)
} }
// Push adds a preload link in response header for server push
func (c Context) Push(link string) string {
if c.responseHeader == nil {
return ""
}
c.responseHeader.Add("Link", "<"+link+">; rel=preload")
return ""
}
// buffer pool for .Include context actions // buffer pool for .Include context actions
var includeBufs = sync.Pool{ var includeBufs = sync.Pool{
New: func() interface{} { New: func() interface{} {
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"net" "net"
"net/http" "net/http"
"net/http/httptest"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
...@@ -731,8 +732,9 @@ func initTestContext() (Context, error) { ...@@ -731,8 +732,9 @@ func initTestContext() (Context, error) {
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
res := httptest.NewRecorder()
return Context{Root: http.Dir(os.TempDir()), Req: request}, nil return Context{Root: http.Dir(os.TempDir()), responseHeader: res.Header(), Req: request}, nil
} }
func getContextOrFail(t *testing.T) Context { func getContextOrFail(t *testing.T) Context {
...@@ -874,3 +876,35 @@ func TestFiles(t *testing.T) { ...@@ -874,3 +876,35 @@ func TestFiles(t *testing.T) {
} }
} }
} }
func TestPush(t *testing.T) {
for name, c := range map[string]struct {
input string
expectLinks []string
}{
"oneLink": {
input: `{{.Push "/test.css"}}`,
expectLinks: []string{"</test.css>; rel=preload"},
},
"multipleLinks": {
input: `{{.Push "/test1.css"}} {{.Push "/test2.css"}}`,
expectLinks: []string{"</test1.css>; rel=preload", "</test2.css>; rel=preload"},
},
} {
c := c
t.Run(name, func(t *testing.T) {
ctx := getContextOrFail(t)
tmpl, err := template.New("").Parse(c.input)
if err != nil {
t.Fatal(err)
}
err = tmpl.Execute(ioutil.Discard, ctx)
if err != nil {
t.Fatal(err)
}
if got := ctx.responseHeader["Link"]; !reflect.DeepEqual(got, c.expectLinks) {
t.Errorf("Result not match: expect %v, but got %v", c.expectLinks, got)
}
})
}
}
...@@ -133,11 +133,10 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -133,11 +133,10 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
} }
lastModTime = latest(lastModTime, fs.ModTime()) lastModTime = latest(lastModTime, fs.ModTime())
ctx := httpserver.Context{ ctx := httpserver.NewContextWithHeader(w.Header())
Root: md.FileSys, ctx.Root = md.FileSys
Req: r, ctx.Req = r
URL: r.URL, ctx.URL = r.URL
}
html, err := cfg.Markdown(title(fpath), f, dirents, ctx) html, err := cfg.Markdown(title(fpath), f, dirents, ctx)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
......
...@@ -34,7 +34,10 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error ...@@ -34,7 +34,10 @@ func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
for _, ext := range rule.Extensions { for _, ext := range rule.Extensions {
if reqExt == ext { if reqExt == ext {
// Create execution context // Create execution context
ctx := httpserver.Context{Root: t.FileSys, Req: r, URL: r.URL} ctx := httpserver.NewContextWithHeader(w.Header())
ctx.Root = t.FileSys
ctx.Req = r
ctx.URL = r.URL
// New template // New template
templateName := filepath.Base(fpath) templateName := filepath.Base(fpath)
......
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