aboutsummaryrefslogtreecommitdiff
path: root/rediscache
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2021-10-24 12:52:38 -0400
committerMitchell Riedstra <mitch@riedstra.dev>2021-10-24 12:52:38 -0400
commit268fcf7e6b671d4959a12111d5abf553bf0a201b (patch)
tree6f5c46c9a765478eaae78d66f4b9aefc953bbd1a /rediscache
parentd36b6a55c8fa4400cd39de717443e110e990a8a3 (diff)
downloadgo-website-268fcf7e6b671d4959a12111d5abf553bf0a201b.tar.gz
go-website-268fcf7e6b671d4959a12111d5abf553bf0a201b.tar.xz
Redis caching. Linter config and cleanup.
Diffstat (limited to 'rediscache')
-rw-r--r--rediscache/main.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/rediscache/main.go b/rediscache/main.go
new file mode 100644
index 0000000..e7564c8
--- /dev/null
+++ b/rediscache/main.go
@@ -0,0 +1,118 @@
+package rediscache
+
+import (
+ "bytes"
+ "log"
+ "net/http"
+ "os"
+
+ "github.com/gomodule/redigo/redis"
+ "github.com/vmihailenco/msgpack"
+)
+
+// Logger is the default logger used for this package, feel free to
+// override.
+var Logger = log.New(os.Stderr, "REDIS: ", log.LstdFlags)
+
+// redisHTTPResponseWriter is essentially a fake http.ResponseWriter that
+// is going to let us suck out information and chuck it into redis
+// implements the interface as defined in net/http.
+type redisHTTPResponseWriter struct {
+ Headers http.Header
+ StatusCode int
+ Data []byte
+
+ buf *bytes.Buffer
+}
+
+// Simply for satisfying the http.ResponseWriter interface.
+func (rw *redisHTTPResponseWriter) Header() http.Header {
+ if rw.Headers == nil {
+ rw.Headers = http.Header{}
+ }
+
+ return rw.Headers
+}
+
+// Writes to the internal buffer.
+func (rw *redisHTTPResponseWriter) Write(msg []byte) (int, error) {
+ if rw.buf == nil {
+ rw.buf = &bytes.Buffer{}
+ }
+
+ return rw.buf.Write(msg)
+}
+
+// Simply for satisfying the http.ResponseWriter interface.
+func (rw *redisHTTPResponseWriter) 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 *redisHTTPResponseWriter) WriteData() {
+ rw.Data = rw.buf.Bytes()
+}
+
+// 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 Handle(pool *redis.Pool, next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ client := pool.Get()
+ defer client.Close()
+
+ content:
+ data, err := client.Do("GET", r.URL.Path)
+ if err != nil {
+ // Assume something bad has happened with redis, we're
+ // just going to log this and then pass through the
+ // request as normal.
+ Logger.Println("ERROR: ", err)
+ next.ServeHTTP(w, r)
+
+ return
+ } else if data == nil {
+ rw := &redisHTTPResponseWriter{}
+ next.ServeHTTP(rw, r)
+
+ rw.WriteData()
+ b, err := msgpack.Marshal(rw)
+ if err != nil {
+ Logger.Println("ERROR: marshaling: ", err)
+
+ return
+ }
+ _, err = client.Do("SET", r.URL.Path, b)
+ if err != nil {
+ Logger.Println("ERROR: during set: ", err)
+
+ return
+ }
+
+ // We got the content, let's go back around again and dump
+ // it out from redis
+ goto content
+ }
+
+ rw := &redisHTTPResponseWriter{}
+
+ err = msgpack.Unmarshal(data.([]byte), rw)
+ if err != nil {
+ Logger.Println("ERROR: unmarshaling: ", err)
+
+ return
+ }
+
+ 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)
+ })
+}