func devdHandler(log termlog.Logger, route Route, templates *template.Template, logHeaders bool, ignoreHeaders []*regexp.Regexp, livereload bool, latency int) http.Handler { ci := inject.CopyInject{} if livereload { ci = injectLivereload } next := route.Endpoint.Handler(templates, ci) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var sublog termlog.Logger if matchStringAny(ignoreHeaders, fmt.Sprintf("%s%s", route.Host, r.RequestURI)) { sublog = termlog.DummyLogger{} } else { sublog = log.Group() } timr := timer.Timer{} defer func() { timing := termlog.DefaultPalette.Timestamp.SprintFunc()("timing: ") sublog.SayAs( "timer", timing+timr.String(), ) sublog.Done() }() timr.RequestHeaders() time.Sleep(time.Millisecond * time.Duration(latency)) sublog.Say("%s %s", r.Method, r.URL) if logHeaders { LogHeader(sublog, r.Header) } ctx := timr.NewContext(context.Background()) ctx = termlog.NewContext(ctx, sublog) next.ServeHTTPContext( ctx, &ResponseLogWriter{ log: sublog, rw: w, timr: &timr, logHeaders: logHeaders, }, r, ) }) }
// LogHeader logs a header func LogHeader(log termlog.Logger, h http.Header) { max := 0 for k := range h { if len(k) > max { max = len(k) } } for k, vals := range h { for _, v := range vals { pad := fmt.Sprintf(fmt.Sprintf("%%%ds", max-len(k)+1), " ") log.SayAs( "headers", "\t%s%s%s", color.BlueString(k)+":", pad, v, ) } } }
// name is '/'-separated, not filepath.Separator. func (fserver *FileServer) serveFile( logger termlog.Logger, w http.ResponseWriter, r *http.Request, name string, redirect bool, ) { const indexPage = "/index.html" // redirect .../index.html to .../ // can't use Redirect() because that would make the path absolute, // which would be a problem running under StripPrefix if strings.HasSuffix(r.URL.Path, indexPage) { logger.SayAs( "debug", "debug fileserver: redirecting %s -> ./", indexPage, ) localRedirect(w, r, "./") return } f, err := fserver.Root.Open(name) if err != nil { logger.WarnAs("debug", "debug fileserver: %s", err) if err := notFound(fserver.Inject, fserver.Templates, w); err != nil { logger.Shout("Internal error: %s", err) } return } defer f.Close() d, err1 := f.Stat() if err1 != nil { logger.WarnAs("debug", "debug fileserver: %s", err) if err := notFound(fserver.Inject, fserver.Templates, w); err != nil { logger.Shout("Internal error: %s", err) } return } if redirect { // redirect to canonical path: / at end of directory url // r.URL.Path always begins with / url := r.URL.Path if d.IsDir() { if url[len(url)-1] != '/' { localRedirect(w, r, path.Base(url)+"/") return } } else { if url[len(url)-1] == '/' { localRedirect(w, r, "../"+path.Base(url)) return } } } // use contents of index.html for directory, if present if d.IsDir() { index := name + indexPage ff, err := fserver.Root.Open(index) if err == nil { defer ff.Close() dd, err := ff.Stat() if err == nil { name = index d = dd f = ff } } } // Still a directory? (we didn't find an index.html file) if d.IsDir() { if checkLastModified(w, r, d.ModTime()) { return } dirList(fserver.Inject, logger, w, name, f, fserver.Templates) return } // serverContent will check modification time sizeFunc := func() (int64, error) { return d.Size(), nil } err = serveContent(fserver.Inject, w, r, d.Name(), d.ModTime(), sizeFunc, f) if err != nil { logger.Warn("Error serving file: %s", err) } }