aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2020-03-15 12:18:10 +0000
committerFelix Hanley <felix@userspace.com.au>2020-03-15 12:18:10 +0000
commitfe79a749511caf8ceac4be68457b5f2000a5f4bd (patch)
treecbde6c563ae869bb68806182c1b8d0bf681f9867
parent54ef0ff980d5e953b54a2396523d9dcfc2fde695 (diff)
downloadsws-fe79a749511caf8ceac4be68457b5f2000a5f4bd.tar.gz
sws-fe79a749511caf8ceac4be68457b5f2000a5f4bd.tar.bz2
Fix cache headers
-rw-r--r--.gitignore2
-rw-r--r--cmd/server/handlers.go9
-rw-r--r--cmd/server/hits.go9
-rw-r--r--cmd/server/routes.go15
-rw-r--r--time_buckets.go175
-rw-r--r--time_buckets_test.go161
-rw-r--r--tmpl/example.tmpl17
7 files changed, 12 insertions, 376 deletions
diff --git a/.gitignore b/.gitignore
index 5af7d37..10e3d7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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 }}