forked from pomerium/autocache
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
93 lines (82 loc) · 2.11 KB
/
main.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
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/golang/groupcache"
"github.com/pomerium/autocache"
"golang.org/x/crypto/bcrypt"
)
const (
defaultAddr = ":http"
)
func main() {
addr := os.Getenv("ADDR")
if addr == "" {
addr = defaultAddr
}
existing := []string{}
if nodes := os.Getenv("NODES"); nodes != "" {
existing = strings.Split(nodes, ",")
}
ac, err := autocache.New(&autocache.Options{})
if err != nil {
log.Fatal(err)
}
if _, err := ac.Join(existing); err != nil {
log.Fatal(err)
}
var exampleCache cache
exampleCache.group = groupcache.NewGroup("bcrypt", 1<<20, exampleCache)
mux := http.NewServeMux()
mux.Handle("/get/", exampleCache)
mux.Handle("/_groupcache/", ac)
log.Fatal(http.ListenAndServe(addr, mux))
}
type cache struct {
group *groupcache.Group
}
// Get is am arbitrary getter function. Bcrypt is nice here because, it:
// 1) takes a long time
// 2) uses a random seed so non-cache results for the same key are obvious
func (ac cache) Get(ctx context.Context, key string, dst groupcache.Sink) error {
now := time.Now()
defer func() {
log.Printf("bcryptKey/key:%q\ttime:%v", key, time.Since(now))
}()
out, err := bcrypt.GenerateFromPassword([]byte(key), 14)
if err != nil {
return err
}
if err := dst.SetBytes(out); err != nil {
return err
}
return nil
}
func (ac cache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
key := r.FormValue("key")
if key == "" {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
now := time.Now()
defer func() {
log.Printf("cacheHandler: group[%s]\tkey[%q]\ttime[%v]", ac.group.Name(), key, time.Since(now))
}()
var respBody []byte
if err := ac.group.Get(r.Context(), key, groupcache.AllocatingByteSliceSink(&respBody)); err != nil {
log.Printf("Get/cache.Get error: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, string(respBody))
}