mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
drive: actually cache results on statcache
Updates #11967 Signed-off-by: Percy Wegmann <percy@tailscale.com>
This commit is contained in:
parent
406293682c
commit
2cf764e998
@ -24,33 +24,26 @@ func (h *Handler) handlePROPFIND(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Delegate to a Child.
|
// Delegate to a Child.
|
||||||
depth := getDepth(r)
|
depth := getDepth(r)
|
||||||
|
|
||||||
cached := h.StatCache.get(r.URL.Path, depth)
|
status, result := h.StatCache.getOr(r.URL.Path, depth, func() (int, []byte) {
|
||||||
if cached != nil {
|
// Use a buffering ResponseWriter so that we can manipulate the result.
|
||||||
w.Header().Del("Content-Length")
|
// The only thing we use from the original ResponseWriter is Header().
|
||||||
w.WriteHeader(http.StatusMultiStatus)
|
bw := &bufferingResponseWriter{ResponseWriter: w}
|
||||||
w.Write(cached)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a buffering ResponseWriter so that we can manipulate the result.
|
mpl := h.maxPathLength(r)
|
||||||
// The only thing we use from the original ResponseWriter is Header().
|
h.delegate(mpl, pathComponents[mpl-1:], bw, r)
|
||||||
bw := &bufferingResponseWriter{ResponseWriter: w}
|
|
||||||
|
|
||||||
mpl := h.maxPathLength(r)
|
// Fixup paths to add the requested path as a prefix.
|
||||||
h.delegate(mpl, pathComponents[mpl-1:], bw, r)
|
pathPrefix := shared.Join(pathComponents[0:mpl]...)
|
||||||
|
b := hrefRegex.ReplaceAll(bw.buf.Bytes(), []byte(fmt.Sprintf("<D:href>%s/$1</D:href>", pathPrefix)))
|
||||||
|
|
||||||
// Fixup paths to add the requested path as a prefix.
|
return bw.status, b
|
||||||
pathPrefix := shared.Join(pathComponents[0:mpl]...)
|
})
|
||||||
b := hrefRegex.ReplaceAll(bw.buf.Bytes(), []byte(fmt.Sprintf("<D:href>%s/$1</D:href>", pathPrefix)))
|
|
||||||
|
|
||||||
if h.StatCache != nil && bw.status == http.StatusMultiStatus && b != nil {
|
|
||||||
h.StatCache.set(r.URL.Path, depth, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Del("Content-Length")
|
w.Header().Del("Content-Length")
|
||||||
w.WriteHeader(bw.status)
|
w.WriteHeader(status)
|
||||||
w.Write(b)
|
if result != nil {
|
||||||
|
w.Write(result)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package compositedav
|
package compositedav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -25,6 +26,23 @@ type StatCache struct {
|
|||||||
cachesByDepthAndPath map[int]*ttlcache.Cache[string, []byte]
|
cachesByDepthAndPath map[int]*ttlcache.Cache[string, []byte]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOr checks the cache for the named value at the given depth. If a cached
|
||||||
|
// value was found, it returns http.StatusMultiStatus along with the cached
|
||||||
|
// value. Otherwise, it executes the given function and returns the resulting
|
||||||
|
// status and value. If the function returned http.StatusMultiStatus, getOr
|
||||||
|
// caches the resulting value at the given name and depth before returning.
|
||||||
|
func (c *StatCache) getOr(name string, depth int, or func() (int, []byte)) (int, []byte) {
|
||||||
|
cached := c.get(name, depth)
|
||||||
|
if cached != nil {
|
||||||
|
return http.StatusMultiStatus, cached
|
||||||
|
}
|
||||||
|
status, next := or()
|
||||||
|
if c != nil && status == http.StatusMultiStatus && next != nil {
|
||||||
|
c.set(name, depth, next)
|
||||||
|
}
|
||||||
|
return status, next
|
||||||
|
}
|
||||||
|
|
||||||
func (c *StatCache) get(name string, depth int) []byte {
|
func (c *StatCache) get(name string, depth int) []byte {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -184,7 +184,7 @@ func newSystem(t *testing.T) *system {
|
|||||||
// Make sure we don't leak goroutines
|
// Make sure we don't leak goroutines
|
||||||
tstest.ResourceCheck(t)
|
tstest.ResourceCheck(t)
|
||||||
|
|
||||||
fs := NewFileSystemForLocal(log.Printf)
|
fs := newFileSystemForLocal(log.Printf, nil)
|
||||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to Listen: %s", err)
|
t.Fatalf("failed to Listen: %s", err)
|
||||||
|
@ -27,6 +27,10 @@
|
|||||||
// NewFileSystemForLocal starts serving a filesystem for local clients.
|
// NewFileSystemForLocal starts serving a filesystem for local clients.
|
||||||
// Inbound connections must be handed to HandleConn.
|
// Inbound connections must be handed to HandleConn.
|
||||||
func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
|
func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
|
||||||
|
return newFileSystemForLocal(logf, &compositedav.StatCache{TTL: statCacheTTL})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFileSystemForLocal(logf logger.Logf, statCache *compositedav.StatCache) *FileSystemForLocal {
|
||||||
if logf == nil {
|
if logf == nil {
|
||||||
logf = log.Printf
|
logf = log.Printf
|
||||||
}
|
}
|
||||||
@ -34,7 +38,7 @@ func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
|
|||||||
logf: logf,
|
logf: logf,
|
||||||
h: &compositedav.Handler{
|
h: &compositedav.Handler{
|
||||||
Logf: logf,
|
Logf: logf,
|
||||||
StatCache: &compositedav.StatCache{TTL: statCacheTTL},
|
StatCache: statCache,
|
||||||
},
|
},
|
||||||
listener: newConnListener(),
|
listener: newConnListener(),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user