From dd4de698cf993407797f535cffc266cad39483ea Mon Sep 17 00:00:00 2001 From: Tobias Weingartner Date: Sun, 17 Apr 2016 10:58:51 -0700 Subject: [PATCH] Better search for config. Handle LastModifiedHeader better. Handle HEAD/GET. --- middleware/markdown/markdown.go | 147 ++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 55 deletions(-) diff --git a/middleware/markdown/markdown.go b/middleware/markdown/markdown.go index 5d6da36f..00e0f72d 100644 --- a/middleware/markdown/markdown.go +++ b/middleware/markdown/markdown.go @@ -8,6 +8,7 @@ import ( "os" "path" "text/template" + "time" "github.com/mholt/caddy/middleware" "github.com/russross/blackfriday" @@ -65,75 +66,111 @@ type Config struct { // ServeHTTP implements the http.Handler interface. func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - for _, cfg := range md.Configs { - if !middleware.Path(r.URL.Path).Matches(cfg.PathScope) { - continue + var cfg *Config + for _, c := range md.Configs { + if middleware.Path(r.URL.Path).Matches(c.PathScope) { // not negated + cfg = c + break // or goto + } + } + if cfg == nil { + return md.Next.ServeHTTP(w, r) // exit early + } + + // We only deal with HEAD/GET + switch r.Method { + case http.MethodGet, http.MethodHead: + default: + return http.StatusMethodNotAllowed, nil + } + + var dirents []os.FileInfo + var lastModTime time.Time + fpath := r.URL.Path + if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { + // We're serving a directory index file, which may be a markdown + // file with a template. Let's grab a list of files this directory + // URL points to, and pass that in to any possible template invocations, + // so that templates can customize the look and feel of a directory. + fdp, err := md.FileSys.Open(fpath) + if err != nil { + if os.IsPermission(err) { + return http.StatusForbidden, err + } + return http.StatusInternalServerError, err } - var dirents []os.FileInfo - fpath := r.URL.Path - if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { - // We're serving a directory index file, which may be a markdown - // file with a template. Let's grab a list of files this directory - // URL points to, and pass that in to any possible template invocations, - // so that templates can customize the look and feel of a directory. - - fdp, err := md.FileSys.Open(fpath) - if err != nil { - return http.StatusInternalServerError, err - } - dirents, err = fdp.Readdir(-1) - if err != nil { - return http.StatusInternalServerError, err - } - - // Set path to found index file - fpath = idx + // Grab a possible set of directory entries. Note, we do not check + // for errors here (unreadable directory, for example). It may + // still be useful to have a directory template file, without the + // directory contents being present. + dirents, _ = fdp.Readdir(-1) + for _, d := range dirents { + lastModTime = latest(lastModTime, d.ModTime()) } - // If supported extension, process it - if _, ok := cfg.Extensions[path.Ext(fpath)]; ok { - f, err := md.FileSys.Open(fpath) - if err != nil { - if os.IsPermission(err) { - return http.StatusForbidden, err - } - return http.StatusNotFound, nil - } + // Set path to found index file + fpath = idx + } - fs, err := f.Stat() - if err != nil { - return http.StatusNotFound, nil + // If supported extension, process it + if _, ok := cfg.Extensions[path.Ext(fpath)]; ok { + f, err := md.FileSys.Open(fpath) + if err != nil { + if os.IsPermission(err) { + return http.StatusForbidden, err } + return http.StatusNotFound, nil + } - body, err := ioutil.ReadAll(f) - if err != nil { - return http.StatusInternalServerError, err - } + fs, err := f.Stat() + if err != nil { + return http.StatusNotFound, nil + } + lastModTime = latest(lastModTime, fs.ModTime()) - ctx := middleware.Context{ - Root: md.FileSys, - Req: r, - URL: r.URL, - } - html, err := cfg.Markdown(fpath, body, dirents, ctx) - if err != nil { - return http.StatusInternalServerError, err - } + body, err := ioutil.ReadAll(f) + if err != nil { + return http.StatusInternalServerError, err + } - // TODO(weingart): move template execution here, something like: - // - // html, err = md.execTemplate(cfg, html, ctx) - // if err != nil { - // return http.StatusInternalServerError, err - // } + ctx := middleware.Context{ + Root: md.FileSys, + Req: r, + URL: r.URL, + } + html, err := cfg.Markdown(fpath, body, dirents, ctx) + if err != nil { + return http.StatusInternalServerError, err + } - middleware.SetLastModifiedHeader(w, fs.ModTime()) + // TODO(weingart): move template execution here, something like: + // + // html, err = md.execTemplate(cfg, html, ctx) + // if err != nil { + // return http.StatusInternalServerError, err + // } + + middleware.SetLastModifiedHeader(w, lastModTime) + if r.Method == "GET" { w.Write(html) - return http.StatusOK, nil } + return http.StatusOK, nil } // Didn't qualify to serve as markdown; pass-thru return md.Next.ServeHTTP(w, r) } + +// latest returns the latest time.Time +func latest(t ...time.Time) time.Time { + var last time.Time + + for _, tt := range t { + if tt.After(last) { + last = tt + } + } + + return last +}