diff options
Diffstat (limited to 'libgo/go/http/fs.go')
-rw-r--r-- | libgo/go/http/fs.go | 99 |
1 files changed, 76 insertions, 23 deletions
diff --git a/libgo/go/http/fs.go b/libgo/go/http/fs.go index 17d5297..2c7c636 100644 --- a/libgo/go/http/fs.go +++ b/libgo/go/http/fs.go @@ -11,6 +11,7 @@ import ( "io" "mime" "os" + "path" "path/filepath" "strconv" "strings" @@ -18,6 +19,38 @@ import ( "utf8" ) +// A Dir implements http.FileSystem using the native file +// system restricted to a specific directory tree. +type Dir string + +func (d Dir) Open(name string) (File, os.Error) { + if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 { + return nil, os.NewError("http: invalid character in file path") + } + f, err := os.Open(filepath.Join(string(d), filepath.FromSlash(path.Clean("/"+name)))) + if err != nil { + return nil, err + } + return f, nil +} + +// A FileSystem implements access to a collection of named files. +// The elements in a file path are separated by slash ('/', U+002F) +// characters, regardless of host operating system convention. +type FileSystem interface { + Open(name string) (File, os.Error) +} + +// A File is returned by a FileSystem's Open method and can be +// served by the FileServer implementation. +type File interface { + Close() os.Error + Stat() (*os.FileInfo, os.Error) + Readdir(count int) ([]os.FileInfo, os.Error) + Read([]byte) (int, os.Error) + Seek(offset int64, whence int) (int64, os.Error) +} + // Heuristic: b is text if it is valid UTF-8 and doesn't // contain any unprintable ASCII or Unicode characters. func isText(b []byte) bool { @@ -44,7 +77,8 @@ func isText(b []byte) bool { return true } -func dirList(w ResponseWriter, f *os.File) { +func dirList(w ResponseWriter, f File) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprintf(w, "<pre>\n") for { dirs, err := f.Readdir(100) @@ -63,16 +97,19 @@ func dirList(w ResponseWriter, f *os.File) { fmt.Fprintf(w, "</pre>\n") } -func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { +// name is '/'-separated, not filepath.Separator. +func serveFile(w ResponseWriter, r *Request, fs FileSystem, 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) { - Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently) + localRedirect(w, r, "./") return } - f, err := os.Open(name) + f, err := fs.Open(name) if err != nil { // TODO expose actual error? NotFound(w, r) @@ -93,12 +130,12 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { url := r.URL.Path if d.IsDirectory() { if url[len(url)-1] != '/' { - Redirect(w, r, url+"/", StatusMovedPermanently) + localRedirect(w, r, path.Base(url)+"/") return } } else { if url[len(url)-1] == '/' { - Redirect(w, r, url[0:len(url)-1], StatusMovedPermanently) + localRedirect(w, r, "../"+path.Base(url)) return } } @@ -112,8 +149,8 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { // use contents of index.html for directory, if present if d.IsDirectory() { - index := name + filepath.FromSlash(indexPage) - ff, err := os.Open(index) + index := name + indexPage + ff, err := fs.Open(index) if err == nil { defer ff.Close() dd, err := ff.Stat() @@ -157,7 +194,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { // TODO(adg): handle multiple ranges ranges, err := parseRange(r.Header.Get("Range"), size) if err == nil && len(ranges) > 1 { - err = os.ErrorString("multiple ranges not supported") + err = os.NewError("multiple ranges not supported") } if err != nil { Error(w, err.String(), StatusRequestedRangeNotSatisfiable) @@ -175,7 +212,9 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { } w.Header().Set("Accept-Ranges", "bytes") - w.Header().Set("Content-Length", strconv.Itoa64(size)) + if w.Header().Get("Content-Encoding") == "" { + w.Header().Set("Content-Length", strconv.Itoa64(size)) + } w.WriteHeader(code) @@ -184,30 +223,44 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { } } +// localRedirect gives a Moved Permanently response. +// It does not convert relative paths to absolute paths like Redirect does. +func localRedirect(w ResponseWriter, r *Request, newPath string) { + if q := r.URL.RawQuery; q != "" { + newPath += "?" + q + } + w.Header().Set("Location", newPath) + w.WriteHeader(StatusMovedPermanently) +} + // ServeFile replies to the request with the contents of the named file or directory. func ServeFile(w ResponseWriter, r *Request, name string) { - serveFile(w, r, name, false) + dir, file := filepath.Split(name) + serveFile(w, r, Dir(dir), file, false) } type fileHandler struct { - root string - prefix string + root FileSystem } // FileServer returns a handler that serves HTTP requests // with the contents of the file system rooted at root. -// It strips prefix from the incoming requests before -// looking up the file name in the file system. -func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} } +// +// To use the operating system's file system implementation, +// use http.Dir: +// +// http.Handle("/", http.FileServer(http.Dir("/tmp"))) +func FileServer(root FileSystem) Handler { + return &fileHandler{root} +} func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { - path := r.URL.Path - if !strings.HasPrefix(path, f.prefix) { - NotFound(w, r) - return + upath := r.URL.Path + if !strings.HasPrefix(upath, "/") { + upath = "/" + upath + r.URL.Path = upath } - path = path[len(f.prefix):] - serveFile(w, r, filepath.Join(f.root, filepath.FromSlash(path)), true) + serveFile(w, r, f.root, path.Clean(upath), true) } // httpRange specifies the byte range to be sent to the client. @@ -225,7 +278,7 @@ func parseRange(s string, size int64) ([]httpRange, os.Error) { return nil, os.NewError("invalid range") } var ranges []httpRange - for _, ra := range strings.Split(s[len(b):], ",", -1) { + for _, ra := range strings.Split(s[len(b):], ",") { i := strings.Index(ra, "-") if i < 0 { return nil, os.NewError("invalid range") |