/
directory_handler.go
143 lines (107 loc) · 3.83 KB
/
directory_handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package lazycache
import "net/http"
import "fmt"
import "encoding/json"
import "strings"
import "time"
import "sync"
type DirListing struct {
Path string
Files []string
Directories []string
expires time.Time
}
type DirMapStore struct {
Cache map[string](*DirListing)
Mutex sync.Mutex
}
var DirCache DirMapStore
// a Duration, measured in nanoseconds
const CachedExpiration = 5 * time.Minute
func init() {
DirCache = DirMapStore{
Cache: make(map[string](*DirListing)),
}
}
func (cache *DirMapStore) getDirectory(node *Node) (*DirListing, error) {
// Initialize or update as necessary
cache.Mutex.Lock()
defer cache.Mutex.Unlock()
cacheKey := node.Fs.OriginalPath(node.Path)
//Logger.Log("msg", fmt.Sprintf("Checking cache key: %s", cacheKey))
listing, has := cache.Cache[cacheKey]
if has {
if time.Now().After(listing.expires) {
has = false
}
}
if !has {
//Logger.Log("msg", fmt.Sprintf("Need to update dir cache for %s", node.Path))
var err error
startTime := time.Now()
listing, err = node.Fs.ReadDir(node.Path)
Logger.Log("debug", fmt.Sprintf("Retrieving directory listing took %f", time.Since(startTime).Seconds()))
//Logger.Log("msg", fmt.Sprintf("Listing has %d files and %d directories", len(listing.Files), len(listing.Directories)))
if err == nil {
listing.expires = time.Now().Add(CachedExpiration)
//Logger.Log( "msg", fmt.Sprintf("Created cache entry for %s, current time is %s, expires at %s", cacheKey, time.Now(), listing.expires.String() ))
cache.Cache[cacheKey] = listing
} else {
Logger.Log("msg", fmt.Sprintf("Error querying remote directory: %s", node.Path))
}
}
// This needs to be inside the mutex because it changes listing
// How else can I tell if the node tree needs to be updated?
if len(node.Children) != len(listing.Directories)+len(listing.Files) {
Logger.Log("msg", fmt.Sprintf("Bootstrapping directory %s (%d != %d+%d)", node.Path,
len(node.Children), len(listing.Directories), len(listing.Files)))
node.BootstrapDirectory(*listing)
}
return listing, nil
}
func HandleDirectory(node *Node, path []string, w http.ResponseWriter, req *http.Request) *Node {
//fmt.Printf("HandleDirectory %s with path (%d): (%s)\n", node.Path, len(path), strings.Join(path, ":"))
listing, err := DirCache.getDirectory(node)
if err != nil {
http.Error(w, fmt.Sprintf("Error retrieving directory: %s", err.Error()), 500)
return nil
}
//Logger.Log("msg", fmt.Sprintf("Listing has %d files and %d directories\"", len(listing.Files), len(listing.Directories)))
// If there's residual path, they must be children (not a verb)
if len(path) > 0 {
//fmt.Printf("%d elements of residual path left, recursing to %s\n", len(path), path[0])
if child, ok := node.Children[path[0]]; ok && child != nil {
return child
} else {
http.Error(w, fmt.Sprintf("Can't find child %s within %s", path[0], node.trimPath), 404)
}
} else {
// You're the leaf node
// TODO: Should really dump cached values, not reread from the source
//listing, err := node.Fs.ReadHttpDir(node.Path)
b, err := json.MarshalIndent(listing, "", " ")
if err != nil {
Logger.Log("level", "error", "msg", fmt.Sprintf("JSON error: %s", err))
}
addCacheDefeatHeaders(w)
w.Write(b)
}
return nil
}
func (node *Node) BootstrapDirectory(listing DirListing) {
//fmt.Printf("Bootstrapping directory %s\n", node.Path)
// Clear any existing children
node.Children = make(map[string]*Node)
for _, d := range listing.Directories {
// Trim off trailing slash
dirName := strings.TrimRight(d, "/")
newNode := node.MakeNode(dirName + "/")
newNode.leafFunc = HandleDirectory // Assign leafFunc because we know it's a directory
node.Children[dirName] = newNode
}
for _, f := range listing.Files {
newNode := node.MakeNode(f)
node.Children[f] = newNode
newNode.autodetectLeafFunc()
}
}