Ejemplo n.º 1
0
// ServeFile responds to w with the contents of path within fs.
func ServeFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, path string) {

	// Open file handle.
	f, err := fs.Open(path)
	if err != nil {
		http.Error(w, "404 Not Found", 404)
		return
	}
	defer f.Close()

	// Make sure path exists.
	fileinfo, err1 := f.Stat()
	if err1 != nil {
		http.Error(w, "404 Not Found", 404)
		return
	}

	// Reject directory requests.
	if fileinfo.IsDir() {
		http.Error(w, "403 Forbidden", 403)
		return
	}

	http.ServeContent(w, r, fileinfo.Name(), fileinfo.ModTime(), f)

}
Ejemplo n.º 2
0
func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) {
	f, err := fs.Open(name)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	defer util.Close(f)

	d, err1 := f.Stat()
	if err1 != nil {
		http.NotFound(w, r)
		return
	}

	url := r.URL.Path
	if d.IsDir() {
		if url[len(url)-1] != '/' {
			w.Header().Set("Location", path.Base(url)+"/")
			w.WriteHeader(http.StatusMovedPermanently)
			return
		}
		glog.Infof("Dir List: %s", name)
		dirList(w, f)
		return
	}

	http.ServeContent(w, r, d.Name(), d.ModTime(), f)
}
Ejemplo n.º 3
0
// StaticMiddlewareFromDir returns a middleware that serves static files from the specified http.FileSystem.
// This middleware is great for development because each file is read from disk each time and no
// special caching or cache headers are sent.
//
// If a path is requested which maps to a folder with an index.html folder on your filesystem,
// then that index.html file will be served.
func StaticMiddlewareFromDir(dir http.FileSystem, options ...StaticOption) func(ResponseWriter, *Request, NextMiddlewareFunc) {
	var option StaticOption
	if len(options) > 0 {
		option = options[0]
	}
	return func(w ResponseWriter, req *Request, next NextMiddlewareFunc) {
		if req.Method != "GET" && req.Method != "HEAD" {
			next(w, req)
			return
		}

		file := req.URL.Path
		if option.Prefix != "" {
			if !strings.HasPrefix(file, option.Prefix) {
				next(w, req)
				return
			}
			file = file[len(option.Prefix):]
		}

		f, err := dir.Open(file)
		if err != nil {
			next(w, req)
			return
		}
		defer f.Close()

		fi, err := f.Stat()
		if err != nil {
			next(w, req)
			return
		}

		// If the file is a directory, try to serve an index file.
		// If no index is available, DO NOT serve the directory to avoid
		// Content-Length issues. Simply skip to the next middleware, and return
		// a 404 if no path with the same name is handled.
		if fi.IsDir() {
			if option.IndexFile != "" {
				file = filepath.Join(file, option.IndexFile)
				f, err = dir.Open(file)
				if err != nil {
					next(w, req)
					return
				}
				defer f.Close()

				fi, err = f.Stat()
				if err != nil || fi.IsDir() {
					next(w, req)
					return
				}
			} else {
				next(w, req)
				return
			}
		}
		http.ServeContent(w, req.Request, file, fi.ModTime(), f)
	}
}
Ejemplo n.º 4
0
// name is '/'-separated, not filepath.Separator.
func (f *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string, redirect bool) {
	looper += 1
	if looper > 10 {
		panic("TILT")
	}
	const indexPage = "/index.html"

	debug("opening %s from fs", name)
	file, err := fs.Open(name)
	if err != nil {
		if os.IsNotExist(err) {
			if !testIfFile.MatchString(name) {
				f.serveFile(w, r, fs, ".", redirect)
				return
			}
		}
		msg, code := toHTTPError(err)
		http.Error(w, msg, code)
		return
	}
	defer file.Close()

	debug("getting stat")
	d, err1 := file.Stat()
	if err1 != nil {
		if os.IsNotExist(err1) {
			if !testIfFile.MatchString(name) {
				f.serveFile(w, r, fs, f.rootDir, redirect)
				return
			}
		}
		msg, code := toHTTPError(err1)
		http.Error(w, msg, code)
		return
	}

	// use contents of index.html for directory, if present
	if d.IsDir() {
		index := strings.TrimSuffix(name, "/") + indexPage
		ff, err := fs.Open(index)
		if err == nil {
			defer ff.Close()
			dd, err := ff.Stat()
			if err == nil {
				name = index
				d = dd
				file = ff
			}
		}
	}

	// Still a directory? (we didn't find an index.html file)
	if d.IsDir() {
		f.serveFile(w, r, fs, ".", redirect)
	}

	// serveContent will check modification time
	sizeFunc := func() (int64, error) { return d.Size(), nil }
	f.serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, file)
}
Ejemplo n.º 5
0
func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) {
	f, err := fs.Open(name)
	if err != nil {
		http.NotFound(w, r)
		return
	}
	defer f.Close()

	d, err1 := f.Stat()
	if err1 != nil {
		http.NotFound(w, r)
		return
	}

	if d.IsDir() {
		// Directory, list the contents
		dirList(w, f)
	} else {
		// Actually playback a recording
		offset, err := time.ParseDuration(r.FormValue("offset"))
		if err != nil {
			log.Error("parse offset: %v: %v", err, r.FormValue("offset"))
			offset = 0
		}

		streamRecording(w, f, offset)
	}
}
Ejemplo n.º 6
0
func (ms middlewareStatic) serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) (err error) {
	f, err := fs.Open(name)
	if err != nil {
		return
	}
	defer f.Close()

	d, err := f.Stat()
	if err != nil {
		return ErrFileNotFound
	}

	if d.IsDir() {
		f.Close()
		f, err = fs.Open(name + "/index.html")
		if err != nil {
			return
		}

		d, err = f.Stat()
		if err != nil {
			return ErrFileNotFound
		}

		if d.IsDir() {
			return ErrFileNotFound
		}
	}

	http.ServeContent(w, r, d.Name(), d.ModTime(), f)
	return nil
}
Ejemplo n.º 7
0
func ExampleReadTwoOpenedUncompressedFiles() {
	var fs http.FileSystem = assets

	f0, err := fs.Open("/not-worth-compressing-file.txt")
	if err != nil {
		panic(err)
	}
	defer f0.Close()
	_ = f0.(notWorthGzipCompressing)
	f1, err := fs.Open("/not-worth-compressing-file.txt")
	if err != nil {
		panic(err)
	}
	defer f1.Close()
	_ = f1.(notWorthGzipCompressing)

	_, err = io.CopyN(os.Stdout, f0, 9)
	if err != nil {
		panic(err)
	}
	_, err = io.CopyN(os.Stdout, f1, 9)
	if err != nil {
		panic(err)
	}

	// Output:
	// Its normaIts norma
}
Ejemplo n.º 8
0
// Looks for a file.gz file and serves that if present
// `original` is a file server
func MaybeGzip(root http.FileSystem, original http.Handler) http.Handler {
	return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
		if strings.Contains(request.Header.Get("Accept-Encoding"), "gzip") {
			requestPath := request.URL.Path
			if !strings.HasPrefix(requestPath, "/") {
				requestPath = "/" + requestPath
			}
			requestPath = path.Clean(requestPath)
			gzPath := requestPath + ".gz"
			if requestPath == "/" {
				gzPath = "/index.html.gz"
			}
			// log.Print(gzPath)
			file, err := root.Open(gzPath)
			if err == nil {
				file.Close()
				contentType := mime.TypeByExtension(filepath.Ext(requestPath))
				if contentType == "" {
					contentType = "text/html; charset=utf-8"
				}
				response.Header().Set("Content-Type", contentType)
				response.Header().Set("Content-Encoding", "gzip")
				request.URL.Path = gzPath
			}
		}
		original.ServeHTTP(response, request)
	})
}
Ejemplo n.º 9
0
// ContextInclude opens filename using fs and executes a template with the context ctx.
// This does the same thing that Context.Include() does, but with the ability to provide
// your own context so that the included files can have access to additional fields your
// type may provide. You can embed Context in your type, then override its Include method
// to call this function with ctx being the instance of your type, and fs being Context.Root.
func ContextInclude(filename string, ctx interface{}, fs http.FileSystem) (string, error) {
	file, err := fs.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()

	body, err := ioutil.ReadAll(file)
	if err != nil {
		return "", err
	}

	tpl, err := template.New(filename).Parse(string(body))
	if err != nil {
		return "", err
	}

	var buf bytes.Buffer
	err = tpl.Execute(&buf, ctx)
	if err != nil {
		return "", err
	}

	return buf.String(), nil
}
Ejemplo n.º 10
0
func ExampleReadTwoOpenedCompressedFiles() {
	var fs http.FileSystem = assets

	f0, err := fs.Open("/sample-file.txt")
	if err != nil {
		panic(err)
	}
	defer f0.Close()
	_ = f0.(gzipByter)
	f1, err := fs.Open("/sample-file.txt")
	if err != nil {
		panic(err)
	}
	defer f1.Close()
	_ = f1.(gzipByter)

	_, err = io.CopyN(os.Stdout, f0, 9)
	if err != nil {
		panic(err)
	}
	_, err = io.CopyN(os.Stdout, f1, 9)
	if err != nil {
		panic(err)
	}

	// Output:
	// This fileThis file
}
Ejemplo n.º 11
0
// @ modified by henrylee2cn 2016.1.22
func (e *Echo) serveFile(fs http.FileSystem, file string, c *Context) (err error) {
	f, err := fs.Open(file)
	if err != nil {
		return NewHTTPError(http.StatusNotFound)
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		/* NOTE:
		Not checking the Last-Modified header as it caches the response `304` when
		changing differnt directories for the same path.
		*/
		d := f

		// Index file
		file = filepath.Join(file, indexPage)
		f, err = fs.Open(file)
		if err != nil {
			if e.autoIndex {
				// Auto index
				return listDir(d, c)
			}
			return NewHTTPError(http.StatusForbidden)
		}
		fi, _ = f.Stat() // Index file stat
	}

	http.ServeContent(c.response, c.request, fi.Name(), fi.ModTime(), f)
	return
}
Ejemplo n.º 12
0
func ExampleCompressed() {
	// Compressed file system.
	var fs http.FileSystem = assets

	walkFn := func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf("can't stat file %s: %v\n", path, err)
			return nil
		}

		fmt.Println(path)
		if fi.IsDir() {
			return nil
		}

		f, err := fs.Open(path)
		if err != nil {
			fmt.Printf("fs.Open(%q): %v\n", path, err)
			return nil
		}
		defer f.Close()

		b, err := ioutil.ReadAll(f)
		fmt.Printf("%q %v\n", string(b), err)

		if gzipFile, ok := f.(gzipByter); ok {
			b := gzipFile.GzipBytes()
			fmt.Printf("%q\n", string(b))
		} else {
			fmt.Println("<not compressed>")
		}
		return nil
	}

	err := vfsutil.Walk(fs, "/", walkFn)
	if err != nil {
		panic(err)
	}

	// Output:
	// /
	// /folderA
	// /folderA/file1.txt
	// "Stuff in /folderA/file1.txt." <nil>
	// <not compressed>
	// /folderA/file2.txt
	// "Stuff in /folderA/file2.txt." <nil>
	// <not compressed>
	// /folderB
	// /folderB/folderC
	// /folderB/folderC/file3.txt
	// "Stuff in /folderB/folderC/file3.txt." <nil>
	// <not compressed>
	// /not-worth-compressing-file.txt
	// "Its normal contents are here." <nil>
	// <not compressed>
	// /sample-file.txt
	// "This file compresses well. Blaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah!" <nil>
	// "\x1f\x8b\b\x00\x00\tn\x88\x00\xff\n\xc9\xc8,VH\xcb\xccIUH\xce\xcf-(J-.N-V(O\xcd\xc9\xd1Sp\xcaI\x1c\xd4 C\x11\x10\x00\x00\xff\xff\xe7G\x81:\xbd\x00\x00\x00"
}
Ejemplo n.º 13
0
// Stat returns the FileInfo structure describing file.
func Stat(fs http.FileSystem, name string) (os.FileInfo, error) {
	f, err := fs.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return f.Stat()
}
Ejemplo n.º 14
0
// ReadDir reads the contents of the directory associated with file and
// returns a slice of FileInfo values in directory order.
func ReadDir(fs http.FileSystem, name string) ([]os.FileInfo, error) {
	f, err := fs.Open(name)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	return f.Readdir(0)
}
Ejemplo n.º 15
0
// ReadFile reads the file named by path from fs and returns the contents.
func ReadFile(fs http.FileSystem, path string) ([]byte, error) {
	rc, err := fs.Open(path)
	if err != nil {
		return nil, err
	}
	defer rc.Close()
	return ioutil.ReadAll(rc)
}
Ejemplo n.º 16
0
func readFile(fs http.FileSystem, path string) ([]byte, error) {
	file, err := fs.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	return ioutil.ReadAll(file)
}
Ejemplo n.º 17
0
func serveFile(reply *Reply, r *http.Request, fs http.FileSystem, name string, redirect bool) {
	const indexPage = "/index.html"
	if strings.HasSuffix(r.URL.Path, indexPage) {
		localRedirect(reply, r, "./")
		return
	}
	f, err := fs.Open(name)
	if err != nil {
		reply.SetCode(http.StatusNotFound)
		return
	}
	d, err1 := f.Stat()
	if err1 != nil {
		reply.SetCode(http.StatusNotFound)
		return
	}
	if redirect {
		url := r.URL.Path
		if d.IsDir() {
			if url[len(url)-1] != '/' {
				localRedirect(reply, r, path.Base(url)+"/")
				return
			}
		} else {
			if url[len(url)-1] == '/' {
				localRedirect(reply, r, "../"+path.Base(url))
				return
			}
		}
	}
	// use contents of index.html for directory, if present
	if d.IsDir() {
		index := strings.TrimSuffix(name, "/") + indexPage
		ff, err := fs.Open(index)
		if err == nil {
			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(reply, r, d.ModTime()) {
			return
		}
		dirList(reply, f)
		return
	}
	// serveContent will check modification time
	sizeFunc := func() (int64, error) { return d.Size(), nil }
	serveContent(reply, r, d.Name(), d.ModTime(), sizeFunc, f)
}
Ejemplo n.º 18
0
func ExampleNotExist() {
	var fs http.FileSystem = assets

	_, err := fs.Open("/does-not-exist")
	fmt.Println("os.IsNotExist:", os.IsNotExist(err))
	fmt.Println(err)

	// Output:
	// os.IsNotExist: true
	// open /does-not-exist: file does not exist
}
Ejemplo n.º 19
0
func StaticFilesHandler(h http.Handler, prefix string, fs http.FileSystem) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		filename := strings.TrimPrefix(r.URL.Path, prefix)
		_, err := fs.Open(filename)
		if err != nil {
			h.ServeHTTP(w, r)
			return
		}
		fileserver := http.StripPrefix(prefix, http.FileServer(fs))
		fileserver.ServeHTTP(w, r)
	})
}
Ejemplo n.º 20
0
// openStat performs Open and Stat and returns results, or first error encountered.
// The caller is responsible for closing the returned file when done.
func openStat(fs http.FileSystem, name string) (http.File, os.FileInfo, error) {
	f, err := fs.Open(name)
	if err != nil {
		return nil, nil, err
	}
	fi, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, nil, err
	}
	return f, fi, nil
}
Ejemplo n.º 21
0
func open(fs http.FileSystem, name string) (http.File, os.FileInfo) {
	f, err := fs.Open(name)
	if err != nil {
		return nil, nil
	}
	s, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, nil
	}
	return f, s
}
Ejemplo n.º 22
0
// StaticMiddlewareFromDir returns a middleware that serves static files from the specified http.FileSystem.
// This middleware is great for development because each file is read from disk each time and no
// special caching or cache headers are sent.
//
// If a path is requested which maps to a folder with an index.html folder on your filesystem,
// then that index.html file will be served.
func StaticMiddlewareFromDir(dir http.FileSystem, options ...StaticOption) func(ResponseWriter, *Request, NextMiddlewareFunc) {
	var option StaticOption
	if len(options) > 0 {
		option = options[0]
	}
	return func(w ResponseWriter, req *Request, next NextMiddlewareFunc) {
		if req.Method != "GET" && req.Method != "HEAD" {
			next(w, req)
			return
		}

		file := req.URL.Path
		if option.Prefix != "" {
			if !strings.HasPrefix(file, option.Prefix) {
				next(w, req)
				return
			}
			file = file[len(option.Prefix):]
		}

		f, err := dir.Open(file)
		if err != nil {
			next(w, req)
			return
		}
		defer f.Close()

		fi, err := f.Stat()
		if err != nil {
			next(w, req)
			return
		}

		// Try to serve index
		if option.IndexFile != "" && fi.IsDir() {
			file = filepath.Join(file, option.IndexFile)
			f, err = dir.Open(file)
			if err != nil {
				next(w, req)
				return
			}
			defer f.Close()

			fi, err = f.Stat()
			if err != nil || fi.IsDir() {
				next(w, req)
				return
			}
		}
		http.ServeContent(w, req.Request, file, fi.ModTime(), f)
	}
}
Ejemplo n.º 23
0
func serveStaticFile(rw http.ResponseWriter, req *http.Request, root http.FileSystem, file string) {
	f, err := root.Open("/" + file)
	if err != nil {
		http.NotFound(rw, req)
		log.Printf("Failed to open file %q from uistatic.Files: %v", file, err)
		return
	}
	defer f.Close()
	var modTime time.Time
	if fi, err := f.Stat(); err == nil {
		modTime = fi.ModTime()
	}
	http.ServeContent(rw, req, file, modTime, f)
}
Ejemplo n.º 24
0
func ExampleSeekDir1() {
	var fs http.FileSystem = assets

	f, err := fs.Open("/")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	fis, err := f.Readdir(0)
	fmt.Println(fisStringer(fis), err)

	// Output:
	// [ folderA folderB not-worth-compressing-file.txt sample-file.txt ] <nil>
}
Ejemplo n.º 25
0
// openFile attempts to open the file within the specified Filesystem. If
// successful, the http.File is returned and must be closed by the caller.
// Otherwise, the path was not a regular file that could be opened and an
// error is returned.
func openFile(fs http.FileSystem, path string) (http.File, error) {
	file, err := fs.Open(path)
	if err != nil {
		return nil, err
	}
	info, err := file.Stat()
	if err != nil {
		file.Close()
		return nil, err
	}
	if info.Mode().IsRegular() {
		return file, nil
	}
	file.Close()
	return nil, fmt.Errorf("%s is not a file on the given filesystem", path)
}
Ejemplo n.º 26
0
// justFiles bypasses the http.FileServer if a directory is requested.
func justFiles(dir http.FileSystem, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		f, err := dir.Open(r.URL.Path)
		if err != nil {
			http.NotFound(w, r)
			return
		}

		info, err := f.Stat()
		if err == nil && info.IsDir() && strings.HasSuffix(r.URL.Path, "/") {
			http.NotFound(w, r)
			return
		}

		next.ServeHTTP(w, r)
	})
}
Ejemplo n.º 27
0
// IndexFile looks for a file in /root/fpath/indexFile for each string
// in indexFiles. If an index file is found, it returns the root-relative
// path to the file and true. If no index file is found, empty string
// and false is returned. fpath must end in a forward slash '/'
// otherwise no index files will be tried (directory paths must end
// in a forward slash according to HTTP).
//
// All paths passed into and returned from this function use '/' as the
// path separator, just like URLs.  IndexFle handles path manipulation
// internally for systems that use different path separators.
func IndexFile(root http.FileSystem, fpath string, indexFiles []string) (string, bool) {
	if fpath[len(fpath)-1] != '/' || root == nil {
		return "", false
	}
	for _, indexFile := range indexFiles {
		// func (http.FileSystem).Open wants all paths separated by "/",
		// regardless of operating system convention, so use
		// path.Join instead of filepath.Join
		fp := path.Join(fpath, indexFile)
		f, err := root.Open(fp)
		if err == nil {
			f.Close()
			return fp, true
		}
	}
	return "", false
}
Ejemplo n.º 28
0
func ExampleSeek() {
	var fs http.FileSystem = assets

	f, err := fs.Open("/sample-file.txt")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	_, err = io.CopyN(os.Stdout, f, 5)
	if err != nil {
		panic(err)
	}
	_, err = f.Seek(22, os.SEEK_CUR)
	if err != nil {
		panic(err)
	}
	_, err = io.CopyN(os.Stdout, f, 10)
	if err != nil {
		panic(err)
	}
	fmt.Print("...")
	_, err = f.Seek(-4, os.SEEK_END)
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(os.Stdout, f)
	if err != nil {
		panic(err)
	}
	_, err = f.Seek(3, os.SEEK_SET)
	if err != nil {
		panic(err)
	}
	_, err = f.Seek(1, os.SEEK_CUR)
	if err != nil {
		panic(err)
	}
	_, err = io.CopyN(os.Stdout, f, 22)
	if err != nil {
		panic(err)
	}

	// Output:
	// This Blaaaaaaaa...aah! file compresses well.
}
Ejemplo n.º 29
0
func walkFileSystem(fs http.FileSystem, path string, t *tar.Writer) error {
	fh, e := fs.Open(path)
	if e != nil {
		return e
	}
	infos, e := fh.Readdir(0)
	if e != nil {
		return e
	}
	for i := range infos {
		name := infos[i].Name()
		if path != "/" {
			name = fmt.Sprintf("%s/%s", path, name)
		}
		header := &tar.Header{Name: name, ModTime: infos[i].ModTime().UTC()}
		switch {
		case infos[i].IsDir():
			header.Typeflag = tar.TypeDir
			header.Mode = 0755
			e = t.WriteHeader(header)
			if e = walkFileSystem(fs, name, t); e != nil {
				return e
			}
		default:
			file, e := fs.Open(name)
			if e != nil {
				return e
			}

			header.Mode = 0644
			header.Size = infos[i].Size()
			e = t.WriteHeader(header)
			if e != nil {
				return e
			}

			_, e = io.Copy(t, file)
			if e != nil {
				return e
			}
		}
	}
	return nil
}
Ejemplo n.º 30
0
Archivo: main.go Proyecto: TommyEdi/go
// name is '/'-separated, not filepath.Separator.
func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) {
	f, err := fs.Open(name)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer f.Close()

	d, err := f.Stat()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// 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
		}
	}

	// A directory?
	if d.IsDir() {
		// TODO: Consider using checkLastModified?
		/*if checkLastModified(w, r, d.ModTime()) {
			return
		}*/
		dirList(w, f, name)
		return
	}

	if _, plain := r.URL.Query()["plain"]; plain {
		w.Header().Set("Content-Type", "text/plain")
	}
	http.ServeContent(w, r, d.Name(), d.ModTime(), f)
}