From c42be33ba94c47a7959bd1bb2ac5f5ed3b604836 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Tue, 16 Sep 2014 18:52:50 +0200 Subject: [PATCH] Generate ETags and encode PNG directly to response stream. --- cmd/mtwebmapper/TODO | 1 - cmd/mtwebmapper/main.go | 90 +++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 32 deletions(-) delete mode 100644 cmd/mtwebmapper/TODO diff --git a/cmd/mtwebmapper/TODO b/cmd/mtwebmapper/TODO deleted file mode 100644 index da890db..0000000 --- a/cmd/mtwebmapper/TODO +++ /dev/null @@ -1 +0,0 @@ -* Generate and use ETags to improve caching behavior. diff --git a/cmd/mtwebmapper/main.go b/cmd/mtwebmapper/main.go index 5b3b9b3..c1178d8 100644 --- a/cmd/mtwebmapper/main.go +++ b/cmd/mtwebmapper/main.go @@ -12,11 +12,10 @@ import ( "image/png" "log" "net/http" + "os" "path/filepath" "strconv" - "bytes" - "bitbucket.org/s_l_teichmann/mtredisalize/common" "github.com/gorilla/mux" @@ -64,6 +63,15 @@ func toUint(s string) uint { return uint(x) } +func createETag(path string) (etag string, err error) { + var fi os.FileInfo + if fi, err = os.Stat(path); err != nil { + return + } + etag = fmt.Sprintf("%x-%x", fi.ModTime().Unix(), fi.Size()) + return +} + func main() { var ( port int @@ -84,41 +92,38 @@ func main() { r := mux.NewRouter() r.HandleFunc("/map/{z:[0-9]+}/{x:[0-9]+}/{y:[0-9]+}.png", func(rw http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) xs := vars["x"] ys := vars["y"] zs := vars["z"] + x, y, z := toUint(xs), toUint(ys), toUint(zs) if z < 9 { filename := fmt.Sprintf("%d/%d/%d.png", z, x, y) http.ServeFile(rw, r, filepath.Join(mapDir, filename)) return } + if z > 16 { z = 16 } - //fmt.Printf("x %d\n", x) - //fmt.Printf("y %d\n", y) - //fmt.Printf("z %d\n", z) tx := x >> (z - 8) ty := y >> (z - 8) - rx := x & ^(^uint(0) << (z - 8)) - ry := y & ^(^uint(0) << (z - 8)) - - parts := uint(1) << (z - 8) - //fmt.Printf("z %d\n", z) - //fmt.Printf("parts %d\n", parts) - - w := uint(256) / parts - xo := w * rx - yo := w * (parts - 1 - ry) - //fmt.Printf("w %d\n", w) - //fmt.Printf("xo %d\n", xo) - //fmt.Printf("yo %d\n", yo) - //fmt.Printf("rx %d\n", rx) - //fmt.Printf("ry %d\n", ry) baseTile := filepath.Join(mapDir, fmt.Sprintf("8/%d/%d.png", tx, ty)) + + var etag string + var err error + + if ifNoneMatch := r.Header.Get("If-None-Match"); ifNoneMatch != "" { + if etag, err = createETag(baseTile); err == nil { + if ifNoneMatch == etag { + http.Error(rw, http.StatusText(http.StatusNotModified), http.StatusNotModified) + return + } + } + } img := common.LoadPNG(baseTile) type subImage interface { @@ -129,24 +134,47 @@ func main() { var ok bool if si, ok = img.(subImage); !ok { // Should not happen. - http.ServeFile(rw, r, filepath.Join(mapDir, baseTile)) + http.Error(rw, + http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } + rx := x & ^(^uint(0) << (z - 8)) + ry := y & ^(^uint(0) << (z - 8)) + + parts := uint(1) << (z - 8) + + w := uint(256) / parts + xo := w * rx + yo := w * (parts - 1 - ry) + //fmt.Printf("x %d\n", x) + //fmt.Printf("y %d\n", y) + //fmt.Printf("z %d\n", z) + //fmt.Printf("z %d\n", z) + //fmt.Printf("parts %d\n", parts) + //fmt.Printf("w %d\n", w) + //fmt.Printf("xo %d\n", xo) + //fmt.Printf("yo %d\n", yo) + //fmt.Printf("rx %d\n", rx) + //fmt.Printf("ry %d\n", ry) + img = si.SubImage(image.Rect(int(xo), int(yo), int(xo+w), int(yo+w))) img = blowUp(img) - var buf bytes.Buffer - if err := png.Encode(&buf, img); err != nil { - http.ServeFile(rw, r, filepath.Join(mapDir, baseTile)) - return - } - b := buf.Bytes() rw.Header().Set("Content-Type", "image/png") - // Don't set the content length to use chunked mode. - // rw.Header().Set("Content-Length", strconv.Itoa(len(b))) - if _, err := rw.Write(b); err != nil { - log.Printf("WARM: sending image failed: %s", err) + if etag == "" { + if etag, err = createETag(baseTile); err != nil { + log.Printf("Cannot create ETag: %s", baseTile) + } else { + rw.Header().Set("ETag", etag) + } + } else { + rw.Header().Set("ETag", etag) + } + + if err = png.Encode(rw, img); err != nil { + log.Printf("WARN: encoding image failed: %s", err) + return } })