From 3ec870cb56c5eea1f8409249a4d96a31db4ad699 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Sat, 18 Apr 2015 09:57:51 -0600 Subject: [PATCH] Templates middleware with "include" functionality --- config/middleware.go | 2 + middleware/templates/context.go | 24 +++++++ middleware/templates/templates.go | 106 ++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 middleware/templates/context.go create mode 100644 middleware/templates/templates.go diff --git a/config/middleware.go b/config/middleware.go index b6f57bd44..4f2fe547d 100644 --- a/config/middleware.go +++ b/config/middleware.go @@ -13,6 +13,7 @@ import ( "github.com/mholt/caddy/middleware/proxy" "github.com/mholt/caddy/middleware/redirect" "github.com/mholt/caddy/middleware/rewrite" + "github.com/mholt/caddy/middleware/templates" "github.com/mholt/caddy/middleware/websockets" ) @@ -48,6 +49,7 @@ func init() { register("fastcgi", fastcgi.New) register("websocket", websockets.New) register("markdown", markdown.New) + register("templates", templates.New) register("browse", browse.New) } diff --git a/middleware/templates/context.go b/middleware/templates/context.go new file mode 100644 index 000000000..10e06f637 --- /dev/null +++ b/middleware/templates/context.go @@ -0,0 +1,24 @@ +package templates + +import ( + "io/ioutil" + "net/http" +) + +// This file contains the context and functions available for +// use in the templates. + +// context is the context with which templates are executed. +type context struct { + root http.FileSystem +} + +// Include returns the contents of filename relative to the site root +func (c context) Include(filename string) (string, error) { + file, err := c.root.Open(filename) + if err != nil { + return "", err + } + body, err := ioutil.ReadAll(file) + return string(body), err +} diff --git a/middleware/templates/templates.go b/middleware/templates/templates.go new file mode 100644 index 000000000..25e142164 --- /dev/null +++ b/middleware/templates/templates.go @@ -0,0 +1,106 @@ +package templates + +import ( + "html/template" + "net/http" + "path" + + "github.com/mholt/caddy/middleware" +) + +// New constructs a new Templates middleware instance. +func New(c middleware.Controller) (middleware.Middleware, error) { + rules, err := parse(c) + if err != nil { + return nil, err + } + + tmpls := Templates{ + Root: c.Root(), + Rules: rules, + } + + return func(next middleware.Handler) middleware.Handler { + tmpls.Next = next + return tmpls + }, nil +} + +// ServeHTTP implements the middleware.Handler interface. +func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { + for _, rule := range t.Rules { + if !middleware.Path(r.URL.Path).Matches(rule.Path) { + continue + } + + reqExt := path.Ext(r.URL.Path) + + for _, ext := range rule.Extensions { + if reqExt == ext { + // Create execution context + ctx := context{root: http.Dir(t.Root)} + + // Build the template + tpl, err := template.ParseFiles(t.Root + r.URL.Path) + if err != nil { + return http.StatusInternalServerError, err + } + + // Execute it + err = tpl.Execute(w, ctx) + if err != nil { + return http.StatusInternalServerError, err + } + + return http.StatusOK, nil + } + } + } + + return t.Next.ServeHTTP(w, r) +} + +func parse(c middleware.Controller) ([]Rule, error) { + var rules []Rule + + for c.Next() { + var rule Rule + + if c.NextArg() { + // First argument would be the path + rule.Path = c.Val() + + // Any remaining arguments are extensions + rule.Extensions = c.RemainingArgs() + if len(rule.Extensions) == 0 { + rule.Extensions = defaultExtensions + } + } else { + rule.Path = defaultPath + rule.Extensions = defaultExtensions + } + + rules = append(rules, rule) + } + + return rules, nil +} + +// Templates is middleware to render templated files as the HTTP response. +type Templates struct { + Next middleware.Handler + Root string + Rules []Rule +} + +// Rule represents a template rule. A template will only execute +// with this rule if the request path matches the Path specified +// and requests a resource with one of the extensions specified. +type Rule struct { + Path string + Extensions []string +} + +const defaultPath = "/" + +var defaultExtensions = []string{".html", ".htm", ".txt"}