]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: make FileServer sort directory entries
authorDan Peterson <dpiddy@gmail.com>
Tue, 1 Sep 2015 17:44:26 +0000 (14:44 -0300)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 1 Sep 2015 20:11:22 +0000 (20:11 +0000)
Fixes #11879

Change-Id: If021f86b2764e01c69674e6a423699b822596f15
Reviewed-on: https://go-review.googlesource.com/14161
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/http/fs.go
src/net/http/fs_test.go

index 75720234c25d8083b40b3a37ec85f815fb6b0da2..394c87d29a7853fce4050d2da1205072901da37e 100644 (file)
@@ -17,6 +17,7 @@ import (
        "os"
        "path"
        "path/filepath"
+       "sort"
        "strconv"
        "strings"
        "time"
@@ -68,24 +69,28 @@ type File interface {
 }
 
 func dirList(w ResponseWriter, f File) {
+       dirs, err := f.Readdir(-1)
+       if err != nil {
+               // TODO: log err.Error() to the Server.ErrorLog, once it's possible
+               // for a handler to get at its Server via the ResponseWriter. See
+               // Issue 12438.
+               Error(w, "Error reading directory", StatusInternalServerError)
+               return
+       }
+       sort.Sort(byName(dirs))
+
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        fmt.Fprintf(w, "<pre>\n")
-       for {
-               dirs, err := f.Readdir(100)
-               if err != nil || len(dirs) == 0 {
-                       break
-               }
-               for _, d := range dirs {
-                       name := d.Name()
-                       if d.IsDir() {
-                               name += "/"
-                       }
-                       // name may contain '?' or '#', which must be escaped to remain
-                       // part of the URL path, and not indicate the start of a query
-                       // string or fragment.
-                       url := url.URL{Path: name}
-                       fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
+       for _, d := range dirs {
+               name := d.Name()
+               if d.IsDir() {
+                       name += "/"
                }
+               // name may contain '?' or '#', which must be escaped to remain
+               // part of the URL path, and not indicate the start of a query
+               // string or fragment.
+               url := url.URL{Path: name}
+               fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
        }
        fmt.Fprintf(w, "</pre>\n")
 }
@@ -585,3 +590,9 @@ func sumRangesSize(ranges []httpRange) (size int64) {
        }
        return
 }
+
+type byName []os.FileInfo
+
+func (s byName) Len() int           { return len(s) }
+func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
+func (s byName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
index 794dabc40a62f9c6fc6a70ef6b25fc9dc53ebbee..9b235d278a5bdc3c5ad588abb710abc4fd8e368f 100644 (file)
@@ -283,6 +283,49 @@ func TestFileServerEscapesNames(t *testing.T) {
        }
 }
 
+func TestFileServerSortsNames(t *testing.T) {
+       defer afterTest(t)
+       const contents = "I am a fake file"
+       dirMod := time.Unix(123, 0).UTC()
+       fileMod := time.Unix(1000000000, 0).UTC()
+       fs := fakeFS{
+               "/": &fakeFileInfo{
+                       dir:     true,
+                       modtime: dirMod,
+                       ents: []*fakeFileInfo{
+                               {
+                                       basename: "b",
+                                       modtime:  fileMod,
+                                       contents: contents,
+                               },
+                               {
+                                       basename: "a",
+                                       modtime:  fileMod,
+                                       contents: contents,
+                               },
+                       },
+               },
+       }
+
+       ts := httptest.NewServer(FileServer(&fs))
+       defer ts.Close()
+
+       res, err := Get(ts.URL)
+       if err != nil {
+               t.Fatalf("Get: %v", err)
+       }
+       defer res.Body.Close()
+
+       b, err := ioutil.ReadAll(res.Body)
+       if err != nil {
+               t.Fatalf("read Body: %v", err)
+       }
+       s := string(b)
+       if !strings.Contains(s, "<a href=\"a\">a</a>\n<a href=\"b\">b</a>") {
+               t.Errorf("output appears to be unsorted:\n%s", s)
+       }
+}
+
 func mustRemoveAll(dir string) {
        err := os.RemoveAll(dir)
        if err != nil {