|
|
|
@ -2,6 +2,7 @@ package main
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"bytes" |
|
|
|
|
"context" |
|
|
|
|
"flag" |
|
|
|
|
"fmt" |
|
|
|
|
"image" |
|
|
|
@ -12,45 +13,55 @@ import (
|
|
|
|
|
"os" |
|
|
|
|
"sync" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"github.com/pkg/errors" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
var port = flag.Int("port", 8000, "port") |
|
|
|
|
var freq = flag.Duration("d", 15*time.Second, "refresh interval") |
|
|
|
|
|
|
|
|
|
type Now struct { |
|
|
|
|
lastUpdated time.Time |
|
|
|
|
cacheWindow time.Duration |
|
|
|
|
buffer *bytes.Buffer |
|
|
|
|
lock sync.RWMutex |
|
|
|
|
sync.RWMutex |
|
|
|
|
|
|
|
|
|
cache *bytes.Buffer |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func NewNow(d time.Duration) *Now { |
|
|
|
|
return &Now{ |
|
|
|
|
cacheWindow: d, |
|
|
|
|
buffer: &bytes.Buffer{}, |
|
|
|
|
func NewNow(ctx context.Context, d time.Duration) *Now { |
|
|
|
|
n := &Now{ |
|
|
|
|
cache: &bytes.Buffer{}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
n.Update() |
|
|
|
|
go func() { |
|
|
|
|
t := time.NewTicker(d) |
|
|
|
|
for { |
|
|
|
|
select { |
|
|
|
|
case <-t.C: |
|
|
|
|
n.Update() |
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
return n |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (n *Now) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
|
|
|
|
log.Printf("%+v", req.RemoteAddr) |
|
|
|
|
if time.Now().After(n.lastUpdated.Add(n.cacheWindow)) { |
|
|
|
|
log.Println("updating image") |
|
|
|
|
n.Update() |
|
|
|
|
n.lastUpdated = time.Now() |
|
|
|
|
} |
|
|
|
|
n.lock.RLock() |
|
|
|
|
w.Write(n.buffer.Bytes()) |
|
|
|
|
n.lock.RUnlock() |
|
|
|
|
n.RLock() |
|
|
|
|
w.Write(n.cache.Bytes()) |
|
|
|
|
n.RUnlock() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (n *Now) Update() { |
|
|
|
|
func (n *Now) Update() error { |
|
|
|
|
resp, err := http.Get("http://imgs.xkcd.com/comics/now.png") |
|
|
|
|
if err != nil { |
|
|
|
|
log.Fatal(err) |
|
|
|
|
return errors.Wrap(err, "get") |
|
|
|
|
} |
|
|
|
|
defer resp.Body.Close() |
|
|
|
|
src, err := png.Decode(resp.Body) |
|
|
|
|
if err != nil { |
|
|
|
|
log.Fatal(err) |
|
|
|
|
return errors.Wrap(err, "png decode") |
|
|
|
|
} |
|
|
|
|
bounds := src.Bounds() |
|
|
|
|
dest := image.NewNRGBA(bounds) |
|
|
|
@ -64,9 +75,12 @@ func (n *Now) Update() {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
n.lock.Lock() |
|
|
|
|
png.Encode(n.buffer, dest) |
|
|
|
|
n.lock.Unlock() |
|
|
|
|
|
|
|
|
|
n.Lock() |
|
|
|
|
png.Encode(n.cache, dest) |
|
|
|
|
n.Unlock() |
|
|
|
|
|
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
@ -79,7 +93,9 @@ func main() {
|
|
|
|
|
log.Printf("serving on: http://%s:%d/", hostname, *port) |
|
|
|
|
addr := fmt.Sprintf(":%d", *port) |
|
|
|
|
|
|
|
|
|
http.Handle("/", NewNow(15*time.Minute)) |
|
|
|
|
n := NewNow(context.Background(), *freq) |
|
|
|
|
|
|
|
|
|
http.Handle("/", n) |
|
|
|
|
|
|
|
|
|
err = http.ListenAndServe(addr, nil) |
|
|
|
|
if err != nil { |
|
|
|
|