diff options
| -rw-r--r-- | lib/http/server.go | 2 | ||||
| -rw-r--r-- | lib/http/server_test.go | 77 | ||||
| -rw-r--r-- | lib/http/testdata/Server_HandleFS_test.txt | 33 | ||||
| -rw-r--r-- | lib/memfs/node.go | 26 |
4 files changed, 122 insertions, 16 deletions
diff --git a/lib/http/server.go b/lib/http/server.go index a9761582..5801832f 100644 --- a/lib/http/server.go +++ b/lib/http/server.go @@ -499,7 +499,7 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) { responseETag = strconv.FormatInt(nodeModtime, 10) requestETag = req.Header.Get(HeaderIfNoneMatch) ) - if requestETag == responseETag { + if !node.IsDir() && requestETag == responseETag { res.WriteHeader(http.StatusNotModified) return } diff --git a/lib/http/server_test.go b/lib/http/server_test.go index 8961bace..a3a82d9e 100644 --- a/lib/http/server_test.go +++ b/lib/http/server_test.go @@ -17,6 +17,7 @@ import ( "net/http/httputil" "os" "path/filepath" + "regexp" "strings" "testing" "time" @@ -860,6 +861,82 @@ func TestStatusError(t *testing.T) { } } +func TestServer_HandleFS(t *testing.T) { + var tdata *test.Data + var err error + tdata, err = test.LoadData(`testdata/Server_HandleFS_test.txt`) + if err != nil { + t.Fatal(err) + } + + var root = t.TempDir() + var mfsOpts = memfs.Options{ + Root: root, + MaxFileSize: -1, + TryDirect: true, + } + var mfs *memfs.MemFS + mfs, err = memfs.New(&mfsOpts) + if err != nil { + t.Fatal(err) + } + + var serverOpts = ServerOptions{ + Memfs: mfs, + Address: `127.0.0.1:15213`, + EnableIndexHTML: true, + } + var httpd *Server + httpd, err = NewServer(serverOpts) + if err != nil { + t.Fatal(err) + } + + var redactTime = regexp.MustCompile(`\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d`) + var redactedTime = []byte(`0000-00-00T00:00:00`) + var simreq = libhttptest.SimulateRequest{ + Path: `/`, + } + var simres *libhttptest.SimulateResult + var exp string + var got []byte + + t.Run(`OnEmptyRoot`, func(t *testing.T) { + simres, err = libhttptest.Simulate(httpd.ServeHTTP, &simreq) + if err != nil { + t.Fatal(err) + } + + exp = string(tdata.Output[t.Name()]) + got, err = simres.DumpResponse([]string{HeaderETag}) + if err != nil { + t.Fatal(err) + } + test.Assert(t, `response`, exp, string(got)) + }) + + t.Run(`OnNewDirectory`, func(t *testing.T) { + var newDir = filepath.Join(root, `dirA`) + err = os.MkdirAll(newDir, 0755) + if err != nil { + t.Fatal(err) + } + + simres, err = libhttptest.Simulate(httpd.ServeHTTP, &simreq) + if err != nil { + t.Fatal(err) + } + + exp = string(tdata.Output[t.Name()]) + got, err = simres.DumpResponse([]string{HeaderETag}) + if err != nil { + t.Fatal(err) + } + got = redactTime.ReplaceAll(got, redactedTime) + test.Assert(t, `response`, exp, string(got)) + }) +} + // TestServer_Options_HandleFS test GET on memfs with authorization. func TestServer_Options_HandleFS(t *testing.T) { type testCase struct { diff --git a/lib/http/testdata/Server_HandleFS_test.txt b/lib/http/testdata/Server_HandleFS_test.txt new file mode 100644 index 00000000..23dd32c9 --- /dev/null +++ b/lib/http/testdata/Server_HandleFS_test.txt @@ -0,0 +1,33 @@ + +<<< TestServer_HandleFS/OnEmptyRoot +HTTP/1.1 200 OK +Connection: close +Content-Type: text/html; charset=utf-8 + +<!DOCTYPE html><html> +<head> +<meta name="viewport" content="width=device-width"> +<style> +body{font-family:monospace; white-space:pre;} +</style> +</head> +<body> +<h3>Index of /</h3> +</body></html> + +<<< TestServer_HandleFS/OnNewDirectory +HTTP/1.1 200 OK +Connection: close +Content-Type: text/html; charset=utf-8 + +<!DOCTYPE html><html> +<head> +<meta name="viewport" content="width=device-width"> +<style> +body{font-family:monospace; white-space:pre;} +</style> +</head> +<body> +<h3>Index of /</h3> +<div>drwxr-xr-x <tt> 0</tt> 0000-00-00T00:00:00Z <a href="/dirA">dirA</a></div><br/> +</body></html> diff --git a/lib/memfs/node.go b/lib/memfs/node.go index 6a334bbd..5de22c54 100644 --- a/lib/memfs/node.go +++ b/lib/memfs/node.go @@ -15,6 +15,7 @@ import ( "os" "path" "path/filepath" + "slices" "sort" "time" @@ -30,7 +31,8 @@ const ( body{font-family:monospace; white-space:pre;} </style> </head> -<body>` +<body> +` ) var ( @@ -157,12 +159,6 @@ func (node *Node) GenerateIndexHTML() { if !node.IsDir() { return } - if len(node.Content) != 0 { - // Either the index has been generated or the node is not - // empty. - node.size = int64(len(node.Content)) - return - } var ( buf bytes.Buffer @@ -171,23 +167,23 @@ func (node *Node) GenerateIndexHTML() { buf.WriteString(templateIndexHTMLHeader) - fmt.Fprintf(&buf, `<h3>Index of %s</h3>`, node.name) + fmt.Fprintf(&buf, "<h3>Index of %s</h3>\n", node.name) if node.Parent != nil { - buf.WriteString(`<div><a href="..">..</a></div><br/>`) + buf.WriteString("<div><a href='..'>..</a></div><br/>\n") } for _, child = range node.Childs { - fmt.Fprintf(&buf, `<div>%s <tt>%12d</tt> %s <a href=%q>%s</a></div><br/>`, - child.mode, child.size, child.modTime.Format(time.RFC3339), + fmt.Fprintf(&buf, "<div>%s <tt>%12d</tt> %s <a href=%q>%s</a></div><br/>\n", + child.mode, child.size, child.modTime.UTC().Format(time.RFC3339), child.Path, child.name) } - buf.WriteString(`</body></html>`) + buf.WriteString("</body></html>\n") node.ContentType = `text/html; charset=utf-8` node.size = int64(buf.Len()) - node.Content = libbytes.Copy(buf.Bytes()) + node.Content = slices.Clone(buf.Bytes()) } // IsDir return true if the node is a directory. @@ -522,16 +518,16 @@ func (node *Node) Update(newInfo os.FileInfo, maxFileSize int64) (err error) { } } - if !doUpdate { + if !newInfo.IsDir() && !doUpdate { return nil } node.modTime = newInfo.ModTime() - node.size = newInfo.Size() if newInfo.IsDir() { err = node.updateDir(maxFileSize) } else { + node.size = newInfo.Size() err = node.UpdateContent(maxFileSize) } if err != nil { |
