// 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 } // Check for index files fpath := r.URL.Path if idx, ok := middleware.IndexFile(t.FileSys, fpath, rule.IndexFiles); ok { fpath = idx } // Check the extension reqExt := path.Ext(fpath) for _, ext := range rule.Extensions { if reqExt == ext { // Create execution context ctx := context{root: t.FileSys, req: r, URL: r.URL} // Build the template tpl, err := template.ParseFiles(filepath.Join(t.Root, fpath)) if err != nil { if os.IsNotExist(err) { return http.StatusNotFound, nil } else if os.IsPermission(err) { return http.StatusForbidden, nil } return http.StatusInternalServerError, err } // Execute it var buf bytes.Buffer err = tpl.Execute(&buf, ctx) if err != nil { return http.StatusInternalServerError, err } buf.WriteTo(w) return http.StatusOK, nil } } } return t.Next.ServeHTTP(w, r) }
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { if !middleware.Path(r.URL.Path).Matches(rule.BasePath) { continue } // Check for index file fpath := r.URL.Path if idx, ok := middleware.IndexFile(h.FileSys, fpath, browse.IndexPages); ok { fpath = idx } // TODO: Check extension. If .lua, assume whole file is Lua script. file, err := h.FileSys.Open(filepath.Join(h.Root, fpath)) if err != nil { if os.IsNotExist(err) { return http.StatusNotFound, nil } else if os.IsPermission(err) { return http.StatusForbidden, nil } return http.StatusInternalServerError, err } defer file.Close() contents, err := ioutil.ReadAll(file) if err != nil { return http.StatusInternalServerError, err } var out bytes.Buffer if err := Interpret(&out, contents); err != nil { return http.StatusInternalServerError, err } // Write the combined text to the http.ResponseWriter w.Write(out.Bytes()) return http.StatusOK, nil } return h.Next.ServeHTTP(w, r) }
// 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 } fpath := r.URL.Path if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { fpath = idx } for _, ext := range cfg.Extensions { if strings.HasSuffix(fpath, ext) { f, err := md.FileSys.Open(fpath) if err != nil { if os.IsPermission(err) { return http.StatusForbidden, err } return http.StatusNotFound, nil } fs, err := f.Stat() if err != nil { return http.StatusNotFound, nil } // if development is set, scan directory for file changes for links. if cfg.Development { if err := GenerateStatic(md, cfg); err != nil { log.Println("On-demand generation error (markdown):", err) } } cfg.RLock() filepath, ok := cfg.StaticFiles[fpath] cfg.RUnlock() // if static site is generated, attempt to use it if ok { if fs1, err := os.Stat(filepath); err == nil { // if markdown has not been modified since static page // generation, serve the static page if fs.ModTime().Before(fs1.ModTime()) { if html, err := ioutil.ReadFile(filepath); err == nil { w.Write(html) return http.StatusOK, 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 } ctx := middleware.Context{ Root: md.FileSys, Req: r, URL: r.URL, } html, err := md.Process(cfg, fpath, body, ctx) if err != nil { return http.StatusInternalServerError, err } w.Write(html) return http.StatusOK, nil } } } // Didn't qualify to serve as markdown; pass-thru return md.Next.ServeHTTP(w, r) }
// ServeHTTP satisfies the middleware.Handler interface. func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { // First requirement: Base path must match if !middleware.Path(r.URL.Path).Matches(rule.Path) { continue } // In addition to matching the path, a request must meet some // other criteria before being proxied as FastCGI. For example, // we probably want to exclude static assets (CSS, JS, images...) // but we also want to be flexible for the script we proxy to. fpath := r.URL.Path if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok { fpath = idx } // These criteria work well in this order for PHP sites if fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) || !h.exists(fpath) { // Create environment for CGI script env, err := h.buildEnv(r, rule, fpath) if err != nil { return http.StatusInternalServerError, err } // Connect to FastCGI gateway var fcgi *FCGIClient // check if unix socket or tcp if strings.HasPrefix(rule.Address, "/") || strings.HasPrefix(rule.Address, "unix:") { if strings.HasPrefix(rule.Address, "unix:") { rule.Address = rule.Address[len("unix:"):] } fcgi, err = Dial("unix", rule.Address) } else { fcgi, err = Dial("tcp", rule.Address) } if err != nil { return http.StatusBadGateway, err } var resp *http.Response contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length")) switch r.Method { case "HEAD": resp, err = fcgi.Head(env) case "GET": resp, err = fcgi.Get(env) case "OPTIONS": resp, err = fcgi.Options(env) case "POST": resp, err = fcgi.Post(env, r.Header.Get("Content-Type"), r.Body, contentLength) case "PUT": resp, err = fcgi.Put(env, r.Header.Get("Content-Type"), r.Body, contentLength) case "PATCH": resp, err = fcgi.Patch(env, r.Header.Get("Content-Type"), r.Body, contentLength) case "DELETE": resp, err = fcgi.Delete(env, r.Header.Get("Content-Type"), r.Body, contentLength) default: return http.StatusMethodNotAllowed, nil } if resp.Body != nil { defer resp.Body.Close() } if err != nil && err != io.EOF { return http.StatusBadGateway, err } // Write the response header for key, vals := range resp.Header { for _, val := range vals { w.Header().Add(key, val) } } w.WriteHeader(resp.StatusCode) // Write the response body // TODO: If this has an error, the response will already be // partly written. We should copy out of resp.Body into a buffer // first, then write it to the response... _, err = io.Copy(w, resp.Body) if err != nil { return http.StatusBadGateway, err } return 0, nil } } return h.Next.ServeHTTP(w, r) }
// ServeHTTP satisfies the middleware.Handler interface. func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { // First requirement: Base path must match and the path must be allowed. if !middleware.Path(r.URL.Path).Matches(rule.Path) || !rule.AllowedPath(r.URL.Path) { continue } // In addition to matching the path, a request must meet some // other criteria before being proxied as FastCGI. For example, // we probably want to exclude static assets (CSS, JS, images...) // but we also want to be flexible for the script we proxy to. fpath := r.URL.Path if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok { fpath = idx // Index file present. // If request path cannot be split, return error. if !rule.canSplit(fpath) { return http.StatusInternalServerError, ErrIndexMissingSplit } } else { // No index file present. // If request path cannot be split, ignore request. if !rule.canSplit(fpath) { continue } } // These criteria work well in this order for PHP sites if !h.exists(fpath) || fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) { // Create environment for CGI script env, err := h.buildEnv(r, rule, fpath) if err != nil { return http.StatusInternalServerError, err } // Connect to FastCGI gateway network, address := rule.parseAddress() fcgiBackend, err := Dial(network, address) if err != nil { return http.StatusBadGateway, err } var resp *http.Response contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length")) switch r.Method { case "HEAD": resp, err = fcgiBackend.Head(env) case "GET": resp, err = fcgiBackend.Get(env) case "OPTIONS": resp, err = fcgiBackend.Options(env) default: resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength) } if resp.Body != nil { defer resp.Body.Close() } if err != nil && err != io.EOF { return http.StatusBadGateway, err } // Write response header writeHeader(w, resp) // Write the response body _, err = io.Copy(w, resp.Body) if err != nil { return http.StatusBadGateway, err } // Log any stderr output from upstream if fcgiBackend.stderr.Len() != 0 { // Remove trailing newline, error logger already does this. err = LogError(strings.TrimSuffix(fcgiBackend.stderr.String(), "\n")) } // Normally we would return the status code if it is an error status (>= 400), // however, upstream FastCGI apps don't know about our contract and have // probably already written an error page. So we just return 0, indicating // that the response body is already written. However, we do return any // error value so it can be logged. // Note that the proxy middleware works the same way, returning status=0. return 0, err } } return h.Next.ServeHTTP(w, r) }
// ServeHTTP implements the http.Handler interface. func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, m := range md.Configs { if !middleware.Path(r.URL.Path).Matches(m.PathScope) { continue } fpath := r.URL.Path if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok { fpath = idx } for _, ext := range m.Extensions { if strings.HasSuffix(fpath, ext) { f, err := md.FileSys.Open(fpath) if err != nil { if os.IsPermission(err) { return http.StatusForbidden, err } return http.StatusNotFound, nil } fs, err := f.Stat() if err != nil { return http.StatusNotFound, nil } // if static site is generated, attempt to use it if filepath, ok := m.StaticFiles[fpath]; ok { if fs1, err := os.Stat(filepath); err == nil { // if markdown has not been modified // since static page generation, // serve the static page if fs.ModTime().UnixNano() < fs1.ModTime().UnixNano() { if html, err := ioutil.ReadFile(filepath); err == nil { w.Write(html) return http.StatusOK, 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 } html, err := md.Process(m, fpath, body) if err != nil { return http.StatusInternalServerError, err } w.Write(html) return http.StatusOK, nil } } } // Didn't qualify to serve as markdown; pass-thru return md.Next.ServeHTTP(w, r) }
// 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 } // Check for index files fpath := r.URL.Path if idx, ok := middleware.IndexFile(t.FileSys, fpath, rule.IndexFiles); ok { fpath = idx } // Check the extension reqExt := path.Ext(fpath) for _, ext := range rule.Extensions { if reqExt == ext { // Create execution context ctx := middleware.Context{Root: t.FileSys, Req: r, URL: r.URL} // New template templateName := filepath.Base(fpath) tpl := template.New(templateName) // Set delims if rule.Delims != [2]string{} { tpl.Delims(rule.Delims[0], rule.Delims[1]) } // Build the template templatePath := filepath.Join(t.Root, fpath) tpl, err := tpl.ParseFiles(templatePath) if err != nil { if os.IsNotExist(err) { return http.StatusNotFound, nil } else if os.IsPermission(err) { return http.StatusForbidden, nil } return http.StatusInternalServerError, err } // Execute it var buf bytes.Buffer err = tpl.Execute(&buf, ctx) if err != nil { return http.StatusInternalServerError, err } templateInfo, err := os.Stat(templatePath) if err == nil { // add the Last-Modified header if we were able to read the stamp middleware.SetLastModifiedHeader(w, templateInfo.ModTime()) } buf.WriteTo(w) return http.StatusOK, nil } } } return t.Next.ServeHTTP(w, r) }
// ServeHTTP satisfies the middleware.Handler interface. func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { // First requirement: Base path must match if !middleware.Path(r.URL.Path).Matches(rule.Path) { continue } // In addition to matching the path, a request must meet some // other criteria before being proxied as FastCGI. For example, // we probably want to exclude static assets (CSS, JS, images...) // but we also want to be flexible for the script we proxy to. fpath := r.URL.Path if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok { fpath = idx // Index file present. // If request path cannot be split, return error. if !h.canSplit(fpath, rule) { return http.StatusInternalServerError, ErrIndexMissingSplit } } else { // No index file present. // If request path cannot be split, ignore request. if !h.canSplit(fpath, rule) { continue } } // These criteria work well in this order for PHP sites if !h.exists(fpath) || fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) { // Create environment for CGI script env, err := h.buildEnv(r, rule, fpath) if err != nil { return http.StatusInternalServerError, err } // Connect to FastCGI gateway fcgi, err := getClient(&rule) if err != nil { return http.StatusBadGateway, err } var resp *http.Response contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length")) switch r.Method { case "HEAD": resp, err = fcgi.Head(env) case "GET": resp, err = fcgi.Get(env) case "OPTIONS": resp, err = fcgi.Options(env) case "POST": resp, err = fcgi.Post(env, r.Header.Get("Content-Type"), r.Body, contentLength) case "PUT": resp, err = fcgi.Put(env, r.Header.Get("Content-Type"), r.Body, contentLength) case "PATCH": resp, err = fcgi.Patch(env, r.Header.Get("Content-Type"), r.Body, contentLength) case "DELETE": resp, err = fcgi.Delete(env, r.Header.Get("Content-Type"), r.Body, contentLength) default: return http.StatusMethodNotAllowed, nil } if resp.Body != nil { defer resp.Body.Close() } if err != nil && err != io.EOF { return http.StatusBadGateway, err } writeHeader(w, resp) // Write the response body // TODO: If this has an error, the response will already be // partly written. We should copy out of resp.Body into a buffer // first, then write it to the response... _, err = io.Copy(w, resp.Body) if err != nil { return http.StatusBadGateway, err } return 0, nil } } return h.Next.ServeHTTP(w, r) }
// ServeHTTP implements the http.Handler interface. func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { 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) switch { case err == nil: // nop case os.IsPermission(err): return http.StatusForbidden, err case os.IsExist(err): return http.StatusNotFound, nil default: // did we run out of FD? return http.StatusInternalServerError, err } defer fdp.Close() // 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. Note, the directory's last // modification is also present here (entry "."). dirents, _ = fdp.Readdir(-1) for _, d := range dirents { lastModTime = latest(lastModTime, d.ModTime()) } // Set path to found index file fpath = idx } // If not supported extension, pass on it if _, ok := cfg.Extensions[path.Ext(fpath)]; !ok { return md.Next.ServeHTTP(w, r) } // At this point we have a supported extension/markdown f, err := md.FileSys.Open(fpath) switch { case err == nil: // nop case os.IsPermission(err): return http.StatusForbidden, err case os.IsExist(err): return http.StatusNotFound, nil default: // did we run out of FD? return http.StatusInternalServerError, err } defer f.Close() if fs, err := f.Stat(); err != nil { return http.StatusGone, nil } else { lastModTime = latest(lastModTime, fs.ModTime()) } ctx := middleware.Context{ Root: md.FileSys, Req: r, URL: r.URL, } html, err := cfg.Markdown(title(fpath), f, dirents, ctx) if err != nil { return http.StatusInternalServerError, err } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Length", strconv.FormatInt(int64(len(html)), 10)) middleware.SetLastModifiedHeader(w, lastModTime) if r.Method == http.MethodGet { w.Write(html) } return http.StatusOK, nil }
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { for _, rule := range h.Rules { if !middleware.Path(r.URL.Path).Matches(rule.BasePath) { continue } // Check for index file fpath := r.URL.Path if idx, ok := middleware.IndexFile(h.FileSys, fpath, middleware.IndexPages); ok { fpath = idx } // TODO: Check extension. If .lua, assume whole file is Lua script. fileName := filepath.Join(h.Root, fpath) file, err := h.FileSys.Open(fileName) if err != nil { if os.IsNotExist(err) { return http.StatusNotFound, nil } else if os.IsPermission(err) { return http.StatusForbidden, nil } return http.StatusInternalServerError, err } defer file.Close() input, err := ioutil.ReadAll(file) if err != nil { return http.StatusInternalServerError, err } L := lua.NewState() defer L.Close() ctx := interpreter.NewContext(L, w) if err := interpreter.Interpret(L, input, &ctx.Out); err != nil { var errReport error ierr := err.(interpreter.InterpretationError) if lerr, ok := ierr.Err.(*lua.ApiError); ok { switch cause := lerr.Cause.(type) { case *parse.Error: errReport = fmt.Errorf("%s:%d (col %d): Syntax error near '%s'", fileName, cause.Pos.Line+ierr.LineOffset, cause.Pos.Column, cause.Token) case *lua.CompileError: errReport = fmt.Errorf("%s:%d: %s", fileName, cause.Line+ierr.LineOffset, cause.Message) default: errReport = fmt.Errorf("%s: %s", fileName, cause.Error()) } } return http.StatusInternalServerError, errReport } for _, f := range ctx.Callbacks { err := f() if err != nil { // TODO fmt.Println(err) } } // Write the combined text to the http.ResponseWriter w.Write(ctx.Out.Bytes()) return http.StatusOK, nil } return h.Next.ServeHTTP(w, r) }