diff options
author | Felix Hanley <felix@userspace.com.au> | 2020-03-15 12:18:10 +0000 |
---|---|---|
committer | Felix Hanley <felix@userspace.com.au> | 2020-03-15 12:18:10 +0000 |
commit | fe79a749511caf8ceac4be68457b5f2000a5f4bd (patch) | |
tree | cbde6c563ae869bb68806182c1b8d0bf681f9867 | |
parent | 54ef0ff980d5e953b54a2396523d9dcfc2fde695 (diff) | |
download | sws-fe79a749511caf8ceac4be68457b5f2000a5f4bd.tar.gz sws-fe79a749511caf8ceac4be68457b5f2000a5f4bd.tar.bz2 |
Fix cache headers
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | cmd/server/handlers.go | 9 | ||||
-rw-r--r-- | cmd/server/hits.go | 9 | ||||
-rw-r--r-- | cmd/server/routes.go | 15 | ||||
-rw-r--r-- | time_buckets.go | 175 | ||||
-rw-r--r-- | time_buckets_test.go | 161 | ||||
-rw-r--r-- | tmpl/example.tmpl | 17 |
7 files changed, 12 insertions, 376 deletions
@@ -1,3 +1,4 @@ +coverage* dist/ node_modules/ @@ -6,5 +7,4 @@ counter/sws.min.js cmd/server/migrations.go cmd/server/counter.go cmd/server/templates.go -cmd/server/static.go *.db diff --git a/cmd/server/handlers.go b/cmd/server/handlers.go index f3c177d..ffe1b7b 100644 --- a/cmd/server/handlers.go +++ b/cmd/server/handlers.go @@ -60,12 +60,3 @@ func handleIndex(rndr Renderer) http.HandlerFunc { } } } - -func handleExample(rndr Renderer) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if err := rndr.Render(w, "example", nil); err != nil { - log(err) - http.Error(w, http.StatusText(500), 500) - } - } -} diff --git a/cmd/server/hits.go b/cmd/server/hits.go index 979dbb8..9420665 100644 --- a/cmd/server/hits.go +++ b/cmd/server/hits.go @@ -5,7 +5,6 @@ import ( "crypto/sha1" "encoding/base64" "fmt" - "io" "net" "net/http" "strings" @@ -70,7 +69,7 @@ func handleHitCounter(db sws.CounterStore, mmdbPath string) http.HandlerFunc { } // TODO restrict to site sites w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Cache-Control", "no-store") w.Header().Set("Content-Type", "image/gif") w.Write(gifBytes) return @@ -87,7 +86,8 @@ func handleCounter(addr string) http.HandlerFunc { if err := tmpl.Execute(&buf, newTemplateData(nil)); err != nil { panic(err) } - etag := fmt.Sprintf(`"%x"`, sha1.Sum(buf.Bytes())) + b := buf.Bytes() + etag := fmt.Sprintf(`"%x"`, sha1.Sum(b)) return func(w http.ResponseWriter, r *http.Request) { if match := r.Header.Get("If-None-Match"); match != "" { @@ -99,9 +99,10 @@ func handleCounter(addr string) http.HandlerFunc { // TODO restrict to site sites w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Etag", etag) + w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Content-Type", "application/javascript") - if _, err := io.Copy(w, &buf); err != nil { + if _, err := w.Write(b); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } diff --git a/cmd/server/routes.go b/cmd/server/routes.go index 3877a21..2b9aaec 100644 --- a/cmd/server/routes.go +++ b/cmd/server/routes.go @@ -35,13 +35,12 @@ func createRouter(db sws.Store, mmdbPath string) (chi.Router, error) { } tmpls, err := loadHTMLTemplateMap(map[string][]string{ - "sites": append([]string{"sites.tmpl"}, tmplsAuthed...), - "site": append([]string{"site.tmpl", "worldMap.tmpl"}, tmplsAuthed...), - "home": append([]string{"home.tmpl"}, tmplsPublic...), - "login": append([]string{"login.tmpl"}, tmplsPublic...), - "user": append([]string{"user.tmpl"}, tmplsAuthed...), - "404": append([]string{"404.tmpl"}, tmplsPublic...), - "example": []string{"example.tmpl"}, + "sites": append([]string{"sites.tmpl"}, tmplsAuthed...), + "site": append([]string{"site.tmpl", "worldMap.tmpl"}, tmplsAuthed...), + "home": append([]string{"home.tmpl"}, tmplsPublic...), + "login": append([]string{"login.tmpl"}, tmplsPublic...), + "user": append([]string{"user.tmpl"}, tmplsAuthed...), + "404": append([]string{"404.tmpl"}, tmplsPublic...), }, funcMap) if err != nil { return nil, err @@ -148,8 +147,6 @@ func createRouter(db sws.Store, mmdbPath string) (chi.Router, error) { } })) - // Example - r.Get("/test.html", handleExample(rndr)) return r, nil } diff --git a/time_buckets.go b/time_buckets.go deleted file mode 100644 index f9748d2..0000000 --- a/time_buckets.go +++ /dev/null @@ -1,175 +0,0 @@ -// +build ignore - -package sws - -import ( - "sort" - "time" -) - -type TimeBuckets struct { - duration time.Duration - TimeMin, TimeMax time.Time - CountMin, CountMax int - buckets []Bucket -} - -type Bucket struct { - t time.Time - hits []*Hit -} - -func (tb TimeBuckets) Hits() []*Hit { - out := make([]*Hit, 0) - for _, b := range tb.buckets { - for _, h := range b.hits { - out = append(out, h) - } - } - SortHits(out) - return out -} - -// Implement sort.Interface -func (tb TimeBuckets) Len() int { return len(tb.buckets) } -func (tb TimeBuckets) Less(i, j int) bool { return tb.buckets[i].t.Before(tb.buckets[i].t) } -func (tb TimeBuckets) Swap(i, j int) { tb.buckets[i], tb.buckets[j] = tb.buckets[j], tb.buckets[i] } - -// XYValues splits the buckets into two data series, one with the times -// and the other with the values. -func (tb TimeBuckets) XYValues() ([]time.Time, []float64) { - x := make([]time.Time, len(tb.buckets)) - y := make([]float64, len(tb.buckets)) - for i, b := range tb.buckets { - x[i] = b.t - y[i] = float64(len(b.hits)) - } - return x, y -} - -// HitsToTimeBuckets converts a slice of hits to time buckets, group by durtation. -func HitsToTimeBuckets(hits []*Hit, d time.Duration) TimeBuckets { - out := TimeBuckets{ - duration: d, - buckets: make([]Bucket, 0), - } - SortHits(hits) - for j, h := range hits { - k := h.CreatedAt.Truncate(d) - if j == 0 || k.Before(out.TimeMin) { - out.TimeMin = k - } - if j == 0 || k.After(out.TimeMax) { - out.TimeMax = k - } - var found bool - for i, tb := range out.buckets { - if tb.t.Equal(k) { - out.buckets[i].hits = append(out.buckets[i].hits, h) - found = true - } - } - if !found { - out.buckets = append(out.buckets, Bucket{t: k, hits: []*Hit{h}}) - } - } - out.updateMinMax() - sort.Sort(out) - return out -} - -// Fill adds extra buckets so each duration segment has a bucket. -// If no begin or end times are provided it uses the existing min and max times. -func (tb *TimeBuckets) Fill(b, e *time.Time) { - begin := tb.TimeMin - if b != nil { - begin = *b - } - end := tb.TimeMax - if e != nil { - end = *e - } - - total := diffDurations(begin, end, tb.duration) - - newBuckets := make([]Bucket, total) - - var existing int - var idx int - for n := begin; idx < total && !n.After(end); n = n.Add(tb.duration) { - switch { - case existing >= len(tb.buckets): - newBuckets[idx] = Bucket{t: n, hits: []*Hit{}} - - case n.Before(tb.buckets[existing].t): - newBuckets[idx] = Bucket{t: n, hits: []*Hit{}} - - default: - newBuckets[idx] = tb.buckets[existing] - existing++ - } - idx++ - } - tb.updateMinMax() - tb.buckets = newBuckets -} - -func (tb TimeBuckets) YMax() int { - return tb.CountMax -} -func (tb TimeBuckets) XSeries() []Bucket { - return tb.buckets -} - -func (b Bucket) Label() string { - return b.t.Format("15:04 Jan 2") -} - -func (b Bucket) Time() string { - return b.t.Format("15:04 Jan 2") -} - -func (b Bucket) YValue() int { - return len(b.hits) -} - -func (tb *TimeBuckets) updateMinMax() { - if len(tb.buckets) < 1 { - return - } - minC := len(tb.buckets[0].hits) - maxC := len(tb.buckets[0].hits) - minT := tb.buckets[0].t - maxT := tb.buckets[0].t - for _, b := range tb.buckets { - c := len(b.hits) - if c < minC { - minC = c - } - if c > maxC { - maxC = c - } - if b.t.Before(minT) { - minT = b.t - } - if b.t.After(maxT) { - maxT = b.t - } - } - tb.TimeMin = minT - tb.TimeMax = maxT - tb.CountMin = minC - tb.CountMax = maxC -} - -func diffDurations(t1, t2 time.Time, d time.Duration) int { - t1n := t1.Unix() - t2n := t2.Unix() - var diff int64 - if t1n > t2n { - diff = t1n - t2n - } else { - diff = t2n - t1n - } - return int(float64(diff) / d.Seconds()) -} diff --git a/time_buckets_test.go b/time_buckets_test.go deleted file mode 100644 index 813845a..0000000 --- a/time_buckets_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// +build ignore - -package sws - -import ( - "testing" - "time" -) - -func TestHitsToTimeBuckets(t *testing.T) { - now := time.Now() - - tests := []struct { - in []*Hit - expected TimeBuckets - }{ - { - []*Hit{ - {ID: ptrInt(1), CreatedAt: ptrTime(now.Add(-5 * time.Second))}, - {ID: ptrInt(2), CreatedAt: ptrTime(now.Add(-4 * time.Second))}, - {ID: ptrInt(3), CreatedAt: ptrTime(now.Add(-4 * time.Second))}, - {ID: ptrInt(4), CreatedAt: ptrTime(now.Add(-4 * time.Second))}, - {ID: ptrInt(5), CreatedAt: ptrTime(now.Add(-3 * time.Second))}, - {ID: ptrInt(6), CreatedAt: ptrTime(now.Add(-2 * time.Second))}, - }, - TimeBuckets{ - Duration: time.Second, - TimeMin: now.Add(-5 * time.Second).Round(time.Second), - TimeMax: now.Add(-2 * time.Second).Round(time.Second), - Data: []Bucket{ - {Time: now.Add(-5 * time.Second).Round(time.Second), Count: 1}, - {Time: now.Add(-4 * time.Second).Round(time.Second), Count: 3}, - {Time: now.Add(-3 * time.Second).Round(time.Second), Count: 1}, - {Time: now.Add(-2 * time.Second).Round(time.Second), Count: 1}, - }, - }, - }, - } - - for i, tt := range tests { - actual := HitsToTimeBuckets(tt.in, time.Second) - if !actual.TimeMin.Equal(tt.expected.TimeMin) { - t.Errorf("%d => expected %s, got %s", i, tt.expected.TimeMin, actual.TimeMin) - } - if !actual.TimeMax.Equal(tt.expected.TimeMax) { - t.Errorf("%d => expected %s, got %s", i, tt.expected.TimeMax, actual.TimeMax) - } - } -} - -func TestXYValues(t *testing.T) { - now := time.Now() - - tests := []struct { - in TimeBuckets - outX []time.Time - outY []float64 - }{ - { - TimeBuckets{ - Duration: time.Minute, - Data: []Bucket{ - {Time: now.Add(-3 * time.Minute), Count: 1}, - {Time: now.Add(-2 * time.Minute), Count: 2}, - {Time: now.Add(-1 * time.Minute), Count: 1}, - }, - }, - []time.Time{ - now.Add(-3 * time.Minute), - now.Add(-2 * time.Minute), - now.Add(-1 * time.Minute), - }, - []float64{1, 2, 1}, - }, - } - - for i, tt := range tests { - aX, aY := tt.in.XYValues() - for j, x := range aX { - if x != tt.outX[j] { - t.Errorf("%d => expected [%d] = %s, got %s", i, j, tt.outX[j], x) - } - } - for j, y := range aY { - if y != tt.outY[j] { - t.Errorf("%d => expected [%d] = %f, got %f", i, j, tt.outY[j], y) - } - } - } -} - -func TestTimeBucketsFill(t *testing.T) { - now := time.Now().Round(time.Second) - - tests := []struct { - in TimeBuckets - begin, end *time.Time - expected []Bucket - }{ - { - // End provided - TimeBuckets{ - Duration: time.Second, - TimeMin: now.Add(-5 * time.Second), - TimeMax: now.Add(-5 * time.Second), - Data: []Bucket{{Time: now.Add(-5 * time.Second), Count: 1}}, - }, - nil, - ptrTime(now), - []Bucket{ - {Time: now.Add(-5 * time.Second), Count: 1}, - {Time: now.Add(-4 * time.Second), Count: 0}, - {Time: now.Add(-3 * time.Second), Count: 0}, - {Time: now.Add(-2 * time.Second), Count: 0}, - {Time: now.Add(-1 * time.Second), Count: 0}, - {Time: now, Count: 0}, - }, - }, - { - // Begin provided - TimeBuckets{ - Duration: time.Second, - TimeMin: now.Add(-5 * time.Second), - TimeMax: now.Add(-5 * time.Second), - Data: []Bucket{{Time: now.Add(-5 * time.Second), Count: 1}}, - }, - ptrTime(now.Add(-6 * time.Second)), - nil, - []Bucket{ - {Time: now.Add(-6 * time.Second), Count: 0}, - {Time: now.Add(-5 * time.Second), Count: 1}, - }, - }, - { - // No begin or end - TimeBuckets{ - Duration: time.Second, - TimeMin: now.Add(-5 * time.Second), - TimeMax: now.Add(-5 * time.Second), - Data: []Bucket{{Time: now.Add(-5 * time.Second), Count: 1}}, - }, - nil, - nil, - []Bucket{ - {Time: now.Add(-5 * time.Second), Count: 1}, - }, - }, - } - - for i, tt := range tests { - tt.in.Fill(tt.begin, tt.end) - for j, b := range tt.in.Data { - if b.Time != tt.expected[j].Time { - t.Errorf("%d => [%d] expected %s, got %s", i, j, tt.expected[j].Time, b.Time) - } - if b.Count != tt.expected[j].Count { - t.Errorf("%d => [%d] expected %d, got %d", i, j, tt.expected[j].Count, b.Count) - } - } - } -} diff --git a/tmpl/example.tmpl b/tmpl/example.tmpl deleted file mode 100644 index 8524cf3..0000000 --- a/tmpl/example.tmpl +++ /dev/null @@ -1,17 +0,0 @@ -{{ define "example" }} -<!doctype html> -<html> - <head> - <meta charset="utf-8"> - <title>This is the title</title> - <script>_sws = { local: true }</script> - <script async src="http://localhost:5000/sws.js" data-id="1"></script> - <noscript> - <img src="http://localhost:5000/sws.gif" /> - </noscript> - </head> - <body> - <a href="?referred">test</a> - </body> -</html> -{{ end }} |