// 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 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) } } // if static site is generated, attempt to use it if filepath, ok := cfg.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().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) }