aboutsummaryrefslogtreecommitdiff
path: root/mapcache
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2023-01-07 13:31:23 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2023-01-07 13:31:23 -0500
commitca33a035c779ae14fb6330c8801c75f49dd1bb79 (patch)
treedeaabaf15d6d91079a68f247e46070399e4343ee /mapcache
parent97dd660925434be537cd9a49a1d0c893b223e357 (diff)
downloadgo-website-ca33a035c779ae14fb6330c8801c75f49dd1bb79.tar.gz
go-website-ca33a035c779ae14fb6330c8801c75f49dd1bb79.tar.xz
Add an internal caching option. It performs quite well.v0.0.22
Also refactor and clean up most linter warnings.
Diffstat (limited to 'mapcache')
-rw-r--r--mapcache/main.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/mapcache/main.go b/mapcache/main.go
new file mode 100644
index 0000000..0b6b697
--- /dev/null
+++ b/mapcache/main.go
@@ -0,0 +1,119 @@
+// Package mapcache is very similar to rediscache, except there are no
+// external dependencies and the page output is saved in a map
+package mapcache
+
+import (
+ "bytes"
+ "net/http"
+ "sync"
+)
+
+type responseWriter struct {
+ Headers http.Header
+ StatusCode int
+ Data []byte
+ buf *bytes.Buffer
+}
+
+func (rw *responseWriter) Header() http.Header {
+ if rw.Headers == nil {
+ rw.Headers = http.Header{}
+ }
+
+ return rw.Headers
+}
+
+func (rw *responseWriter) Write(msg []byte) (int, error) {
+ if rw.buf == nil {
+ rw.buf = &bytes.Buffer{}
+ }
+
+ return rw.buf.Write(msg) //nolint:wrapcheck
+}
+
+// Simply for satisfying the http.ResponseWriter interface.
+func (rw *responseWriter) WriteHeader(code int) {
+ rw.StatusCode = code
+}
+
+// WriteData takes the internal buffer and writes out the entire
+// contents to the 'Data' field for storage in Redis.
+func (rw *responseWriter) WriteData() {
+ rw.Data = rw.buf.Bytes()
+}
+
+func New() *Cache {
+ return &Cache{
+ m: &sync.RWMutex{},
+ cached: map[string]*responseWriter{},
+ }
+}
+
+type Cache struct {
+ m *sync.RWMutex
+ cached map[string]*responseWriter
+}
+
+func (c *Cache) Remove(path string) {
+ c.m.Lock()
+ delete(c.cached, path)
+ c.m.Unlock()
+}
+
+func (c *Cache) Clear() {
+ c.m.Lock()
+ c.cached = map[string]*responseWriter{}
+ c.m.Unlock()
+}
+
+// HandleWithParams is the same as Handle but caches for the GET params
+// rather than discarding them.
+func (c *Cache) HandleWithParams(next http.Handler) http.Handler {
+ return c.handle(true, next)
+}
+
+// Handle is a Simple function that will cache the response for given handler in
+// redis and instead of responding with the result from the handler it will
+// simply dump the contents of the redis key if it exists.
+func (c *Cache) Handle(next http.Handler) http.Handler {
+ return c.handle(false, next)
+}
+
+func (c *Cache) handle(params bool, next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ subkey := r.URL.Path
+ if params {
+ subkey = r.URL.Path + "?" + r.URL.RawQuery
+ }
+
+ content:
+ c.m.RLock()
+ rw, ok := c.cached[subkey]
+ c.m.RUnlock()
+ if !ok {
+ rw := &responseWriter{}
+ next.ServeHTTP(rw, r)
+
+ rw.WriteData()
+
+ c.m.Lock()
+ c.cached[subkey] = rw
+ c.m.Unlock()
+
+ // We got the content, let's go back around again and dump
+ // it out from the cache
+ goto content
+ }
+
+ if rw.Headers != nil {
+ for k, v := range rw.Headers {
+ w.Header()[k] = v
+ }
+ }
+
+ if rw.StatusCode != 0 {
+ w.WriteHeader(rw.StatusCode)
+ }
+ _, _ = w.Write(rw.Data)
+ })
+}