forked from hauke96/tiny-http-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache.go
120 lines (95 loc) · 2.62 KB
/
cache.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
package main
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"hash"
"io/ioutil"
"os"
"sync"
"github.com/hauke96/sigolo"
)
type Cache struct {
folder string
hash hash.Hash
knownValues map[string][]byte
mutex *sync.Mutex
}
func CreateCache(path string) (*Cache, error) {
fileInfos, err := ioutil.ReadDir(path)
if err != nil {
sigolo.Error("Cannot open cache folder '%s': %s", path, err)
sigolo.Info("Create cache folder '%s'", path)
os.Mkdir(path, os.ModePerm)
}
values := make(map[string][]byte, 0)
// Go through every file an save its name in the map. The content of the file
// is loaded when needed. This makes sure that we don't have to read
// the directory content each time the user wants data that's not yet loaded.
for _, info := range fileInfos {
if !info.IsDir() {
values[info.Name()] = nil
}
}
hash := sha256.New()
mutex := &sync.Mutex{}
cache := &Cache{
folder: path,
hash: hash,
knownValues: values,
mutex: mutex,
}
return cache, nil
}
func (c *Cache) has(key string) bool {
hashValue := calcHash(key)
c.mutex.Lock()
_, ok := c.knownValues[hashValue]
c.mutex.Unlock()
return ok
}
func (c *Cache) get(key string) ([]byte, error) {
hashValue := calcHash(key)
// Try to get content. Error if not found.
c.mutex.Lock()
content, ok := c.knownValues[hashValue]
c.mutex.Unlock()
if !ok {
sigolo.Debug("Cache doen't know key '%s'", hashValue)
return nil, errors.New(fmt.Sprintf("Key '%s' is not known to cache", hashValue))
}
sigolo.Debug("Cache has key '%s'", hashValue)
// Key is known, but not loaded into RAM
if content == nil {
sigolo.Debug("Cache has content for '%s' already loaded", hashValue)
content, err := ioutil.ReadFile(c.folder + hashValue)
if err != nil {
sigolo.Error("Error reading cached file '%s': %s", hashValue, err)
return nil, err
}
c.mutex.Lock()
c.knownValues[hashValue] = content
c.mutex.Unlock()
}
return content, nil
}
func (c *Cache) put(key string, content []byte) error {
hashValue := calcHash(key)
err := ioutil.WriteFile(c.folder+hashValue, content, 0644)
// Make sure, that the RAM-cache only holds values we were able to write.
// This is a decision to prevent a false impression of the cache: If the
// write fails, the cache isn't working correctly, which should be fixed by
// the user of this cache.
if err == nil {
sigolo.Debug("Cache wrote content into '%s'", hashValue)
c.mutex.Lock()
c.knownValues[hashValue] = content
c.mutex.Unlock()
}
return err
}
func calcHash(data string) string {
sha := sha256.Sum256([]byte(data))
return hex.EncodeToString(sha[:])
}