Skip to content

Commit ad415c4

Browse files
authored
Merge pull request #47 from fastly/joeshaw/to-upstream/cache-and-purge-api
2 parents d5b89b6 + 8d4d09f commit ad415c4

File tree

10 files changed

+2081
-13
lines changed

10 files changed

+2081
-13
lines changed

_examples/corecache/main.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"encoding/hex"
7+
"errors"
8+
"fmt"
9+
"io"
10+
"os"
11+
"time"
12+
13+
"github.com/fastly/compute-sdk-go/cache/core"
14+
"github.com/fastly/compute-sdk-go/fsthttp"
15+
"github.com/fastly/compute-sdk-go/purge"
16+
)
17+
18+
func main() {
19+
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
20+
key := keyForRequest(r)
21+
switch r.Method {
22+
23+
// Fetch content from the cache.
24+
case "GET":
25+
tx, err := core.NewTransaction(key, core.LookupOptions{})
26+
if err != nil {
27+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
28+
return
29+
}
30+
defer tx.Close()
31+
32+
f, err := tx.Found()
33+
if errors.Is(err, core.ErrNotFound) {
34+
fsthttp.Error(w, err.Error(), fsthttp.StatusNotFound)
35+
return
36+
}
37+
if err != nil {
38+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
39+
return
40+
}
41+
defer f.Body.Close()
42+
43+
msg, err := io.ReadAll(f.Body)
44+
if err != nil {
45+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
46+
return
47+
}
48+
49+
w.Header().Set("Content-Type", "text/plain")
50+
fmt.Fprintf(w, "%s's message for %s is: %s\n", getPOP(), r.URL.Path, msg)
51+
52+
// Write data to the cache and stream it back to the client.
53+
case "POST":
54+
if r.Header.Get("Content-Type") != "text/plain" && r.Header.Get("Content-Type") != "text/plain; charset=utf-8" {
55+
w.WriteHeader(fsthttp.StatusUnsupportedMediaType)
56+
return
57+
}
58+
59+
msg, err := io.ReadAll(r.Body)
60+
if err != nil {
61+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
62+
return
63+
}
64+
65+
tx, err := core.NewTransaction(key, core.LookupOptions{})
66+
if err != nil {
67+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
68+
return
69+
}
70+
defer tx.Close()
71+
72+
if !tx.MustInsert() {
73+
w.WriteHeader(fsthttp.StatusConflict)
74+
return
75+
}
76+
77+
// We call InsertAndStreamBack to create both a handle to
78+
// write the content into the cache and a Found object to
79+
// stream the contents back out to the client. As soon as
80+
// data is written to the insert body, it is immediately
81+
// streamed to all clients waiting on this transaction,
82+
// including this one.
83+
//
84+
// This is preferable to using an io.MultiWriter because a
85+
// MultiWriter is constrained by the slowest writer. If
86+
// other transactions are waiting on the content to be
87+
// written to the cache and streamed, we don't want that
88+
// process to be delayed by a slow client for this request.
89+
insertBody, found, err := tx.InsertAndStreamBack(core.WriteOptions{
90+
TTL: 600 * time.Second,
91+
Length: uint64(len(msg)),
92+
SurrogateKeys: []string{hex.EncodeToString(key)},
93+
})
94+
if err != nil {
95+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
96+
return
97+
}
98+
defer found.Body.Close()
99+
100+
insertBody.Write(msg)
101+
102+
if err := insertBody.Close(); err != nil {
103+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
104+
return
105+
}
106+
107+
msg, err = io.ReadAll(found.Body)
108+
if err != nil {
109+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
110+
return
111+
}
112+
113+
w.Header().Set("Content-Type", "text/plain")
114+
w.WriteHeader(fsthttp.StatusCreated)
115+
fmt.Fprintf(w, "%s's message for %s is: %s\n", getPOP(), r.URL.Path, msg)
116+
117+
// Purge the key from the cache.
118+
case "DELETE":
119+
if err := purge.PurgeSurrogateKey(hex.EncodeToString(key), purge.PurgeOptions{}); err != nil {
120+
fsthttp.Error(w, err.Error(), fsthttp.StatusInternalServerError)
121+
return
122+
}
123+
w.WriteHeader(fsthttp.StatusAccepted)
124+
125+
default:
126+
w.WriteHeader(fsthttp.StatusMethodNotAllowed)
127+
}
128+
})
129+
}
130+
131+
func keyForRequest(r *fsthttp.Request) []byte {
132+
h := sha256.New()
133+
h.Write([]byte(r.URL.Path))
134+
h.Write([]byte(getPOP()))
135+
return h.Sum(nil)
136+
}
137+
138+
func getPOP() string {
139+
return os.Getenv("FASTLY_POP")
140+
}

0 commit comments

Comments
 (0)