Skip to content

Commit

Permalink
refactor: use separate handler for 'Etag'
Browse files Browse the repository at this point in the history
should've done so from the start, but I wanted to get something out
there first

got the idea and most of the implementation from:
https://sidney.kochman.org/2018/etag-middleware-go/
  • Loading branch information
usrme committed Feb 1, 2024
1 parent 88af8cc commit fb11e8e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 16 deletions.
15 changes: 0 additions & 15 deletions chart.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package gobarchar

import (
"crypto/sha1"
"fmt"
"io"
"math"
"math/rand"
"net/http"
Expand Down Expand Up @@ -73,19 +71,6 @@ func PresentBarChart(w http.ResponseWriter, r *http.Request) {

chart := createBarChart(r)

h := sha1.New()
// TODO: See if 'r.URL.RawQuery' would be faster to hash
io.WriteString(h, chart)
etag := fmt.Sprintf("%x", h.Sum(nil))

if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, etag) {
w.WriteHeader(http.StatusNotModified)
return
}
}
w.Header().Set("Etag", etag)

// Skip all templating if user is requesting through 'curl' or 'wget'
agent := r.UserAgent()
if strings.HasPrefix(agent, "curl") || strings.HasPrefix(agent, "Wget") {
Expand Down
43 changes: 42 additions & 1 deletion cmd/gobarchar/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package main

import (
"bytes"
"crypto/sha1"
"fmt"
"hash"
"io"
"log"
"net/http"
"os"
Expand All @@ -11,13 +16,24 @@ import (

var defaultPort = "8080"

type etagResponseWriter struct {
http.ResponseWriter
buf bytes.Buffer
hash hash.Hash
w io.Writer
}

func (e *etagResponseWriter) Write(p []byte) (int, error) {
return e.w.Write(p)
}

func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}

http.Handle("/", timer(http.HandlerFunc(gobarchar.PresentBarChart)))
http.Handle("/", timer(etag(http.HandlerFunc(gobarchar.PresentBarChart))))

log.Println("listening on:", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
Expand All @@ -31,3 +47,28 @@ func timer(h http.Handler) http.Handler {
log.Println("completed in:", duration)
})
}

func etag(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ew := &etagResponseWriter{
ResponseWriter: w,
buf: bytes.Buffer{},
hash: sha1.New(),
}
ew.w = io.MultiWriter(&ew.buf, ew.hash)

h.ServeHTTP(ew, r)

etag := fmt.Sprintf("%x", ew.hash.Sum(nil))
w.Header().Set("Etag", etag)

if r.Header.Get("If-None-Match") == etag {
w.WriteHeader(http.StatusNotModified)
} else {
_, err := ew.buf.WriteTo(w)
if err != nil {
log.Println("unable to write HTTP response", err)
}
}
})
}

0 comments on commit fb11e8e

Please sign in to comment.