aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/http/fs.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/http/fs.go')
-rw-r--r--libgo/go/http/fs.go99
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")