func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path //shorthand reply := func(c int, msg string) { w.WriteHeader(c) if msg != "" { w.Write([]byte(msg)) } } //requested file p := filepath.Join(s.c.Directory, path) //check file or dir isdir := false missing := false if info, err := os.Stat(p); err != nil { missing = true } else { isdir = info.IsDir() } if s.c.PushState && missing && filepath.Ext(p) == "" { //missing and pushstate and no ext p = s.root //change to request for the root isdir = false missing = false } if s.fallback != nil && (missing || isdir) { //fallback proxy enabled r.Host = s.fallbackHost s.fallback.ServeHTTP(w, r) return } if !s.c.NoArchive && missing { //check if is archivable ok := false ext := archive.Extension(p) dir := "" if ext != "" { var err error if dir, err = filepath.Abs(strings.TrimSuffix(p, ext)); err == nil { if info, err := os.Stat(dir); err == nil && info.IsDir() { ok = true } } } if ok { w.Header().Set("Content-Type", mime.TypeByExtension(ext)) w.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(dir)+ext) w.WriteHeader(200) //write archive a, _ := archive.NewWriter(ext, w) if err := a.AddDir(dir); err != nil { w.Write([]byte("\n\nERROR: " + err.Error())) return } if err := a.Close(); err != nil { w.Write([]byte("\n\nERROR: " + err.Error())) return } return } } if !isdir && missing { //file not found!! reply(404, "Not found") return } //force trailing slash if isdir && !s.c.NoSlash && !strings.HasSuffix(path, "/") { w.Header().Set("Location", path+"/") w.WriteHeader(302) w.Write([]byte("Redirecting (must use slash for directories)")) return } //optionally use index instead of directory list if isdir && !s.c.NoIndex { dirindex := filepath.Join(p, "index.html") if _, err := os.Stat(dirindex); err == nil { p = dirindex isdir = false } } //directory list if isdir { if s.c.NoList { reply(403, "Listing not allowed") return } s.dirlist(w, r, p) return } //check file again info, err := os.Stat(p) if err != nil { reply(404, "Not found") return } //stream file f, err := os.Open(p) if err != nil { reply(500, err.Error()) return } //add all served file's parent dirs to the watcher if s.c.LiveReload { dir, _ := filepath.Split(p) if _, watching := s.watching[dir]; !watching { if err := s.watcher.Add(dir); err == nil { s.watching[dir] = true } } } modtime := info.ModTime() //first time - dont use cache if !s.served[p] { s.served[p] = true modtime = time.Now() } //http.ServeContent handles caching and range requests http.ServeContent(w, r, info.Name(), modtime, f) }
func main() { a, _ := archive.NewWriter("file.tar", os.Stdout) //detects .tar a.AddDir("foo") a.Close() }