123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- package resource
- import (
- "crypto/md5"
- "encoding/hex"
- "fmt"
- "net/http"
- "time"
- )
- type OneResource struct {
- Path string
- Ctype string
- Bytes []byte
- MTime int64
- }
- type Resource struct {
- list map[string]OneResource
- }
- func New() *Resource {
- r := Resource{}
- r.list = map[string]OneResource{}
- return &r
- }
- func etag(str string) string {
- hasher := md5.New()
- hasher.Write([]byte(str))
- return hex.EncodeToString(hasher.Sum(nil))
- }
- func modified(p string, s int, v int64, w http.ResponseWriter, r *http.Request) bool {
- w.Header().Set("Cache-Control", "no-cache")
- // Set: ETag
- ehash := etag(fmt.Sprintf("%s-%d-%d", p, s, v))
- w.Header().Set("ETag", fmt.Sprintf("%s", ehash))
- // Set: Last-Modified
- w.Header().Set(
- "Last-Modified",
- time.Unix(v, 0).In(time.FixedZone("GMT", 0)).Format("Wed, 01 Oct 2006 15:04:05 GMT"),
- )
- // Check: ETag
- if cc := r.Header.Get("Cache-Control"); cc != "no-cache" {
- if inm := r.Header.Get("If-None-Match"); inm == ehash {
- w.WriteHeader(http.StatusNotModified)
- return false
- }
- }
- // Check: Last-Modified
- if cc := r.Header.Get("Cache-Control"); cc != "no-cache" {
- if ims := r.Header.Get("If-Modified-Since"); ims != "" {
- if t, err := time.Parse("Wed, 01 Oct 2006 15:04:05 GMT", ims); err == nil {
- if time.Unix(v, 0).In(time.FixedZone("GMT", 0)).Unix() <= t.In(time.FixedZone("GMT", 0)).Unix() {
- w.WriteHeader(http.StatusNotModified)
- return false
- }
- }
- }
- }
- return true
- }
- func (this *Resource) Add(path string, ctype string, bytes []byte, mtime int64) {
- // Do not add if already in resources list
- if _, ok := this.list[path]; ok == true {
- return
- }
- // Add to resources list
- this.list[path] = OneResource{
- Path: path,
- Ctype: ctype,
- Bytes: bytes,
- MTime: mtime,
- }
- }
- func (this *Resource) Response(w http.ResponseWriter, r *http.Request, before func(w http.ResponseWriter, r *http.Request, i *OneResource), after func(w http.ResponseWriter, r *http.Request, i *OneResource)) bool {
- // Do not process if this is not necessary
- if len(r.URL.Path) <= 1 {
- return false
- }
- // Check for resource
- res, ok := this.list[r.URL.Path[1:]]
- if ok == false {
- return false
- }
- // Cache headers
- w.Header().Set("Content-Type", res.Ctype)
- if !modified(r.URL.Path, len(res.Bytes), res.MTime, w, r) {
- return true
- }
- // Call `before` callback
- if before != nil {
- before(w, r, &res)
- }
- // Send resource
- w.Write(res.Bytes)
- // Call `after` callback
- if after != nil {
- after(w, r, &res)
- }
- return true
- }
|