80 lines
1.7 KiB
Go
80 lines
1.7 KiB
Go
package pages
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"crypto/md5"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type StaticContent struct {
|
|
Type string
|
|
Content []byte
|
|
ETag string
|
|
GZIPContent []byte
|
|
GZIPETag string
|
|
}
|
|
|
|
func (sc *StaticContent) Init() {
|
|
// Populate ETag
|
|
sc.ETag = fmt.Sprintf("%x", md5.Sum(sc.Content))
|
|
|
|
// Set a default Content-Type, if needed
|
|
if sc.Type == "" {
|
|
sc.Type = "application/octet-stream"
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
gz, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
|
|
defer gz.Close()
|
|
|
|
if _, err := gz.Write(sc.Content); err != nil {
|
|
return
|
|
}
|
|
|
|
if err := gz.Flush(); err != nil {
|
|
return
|
|
}
|
|
|
|
// Using gzip encoding adds a minimum of 24 characters to the HTTP
|
|
// header, so only accept gzip encoding if we save that much or more
|
|
if (buf.Len()+24) < len(sc.Content) {
|
|
sc.GZIPContent = buf.Bytes()
|
|
sc.GZIPETag = fmt.Sprintf("%x", md5.Sum(sc.GZIPContent))
|
|
}
|
|
}
|
|
|
|
func (sc *StaticContent) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
gzok := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
|
|
gzlen := len(sc.GZIPContent)
|
|
|
|
// Use the correct etag
|
|
var localETag string
|
|
if gzok && gzlen > 0 {
|
|
localETag = sc.GZIPETag
|
|
} else {
|
|
localETag = sc.ETag
|
|
}
|
|
|
|
// Check the etag, maybe we don't need to send content
|
|
remoteETag := r.Header.Get("If-None-Match")
|
|
if localETag == remoteETag {
|
|
w.WriteHeader(http.StatusNotModified)
|
|
return
|
|
}
|
|
w.Header().Set("ETag", localETag)
|
|
w.Header().Set("Content-Type", sc.Type)
|
|
|
|
// Finally, write our content
|
|
if gzok && gzlen > 0 {
|
|
w.Header().Set("Content-Encoding", "gzip")
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", gzlen))
|
|
w.Write(sc.GZIPContent)
|
|
} else {
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(sc.Content)))
|
|
w.Write(sc.Content)
|
|
}
|
|
}
|