|
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- // Copyright 2013 The Go Authors. All rights reserved.
- //
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file or at
- // https://developers.google.com/open-source/licenses/bsd.
-
- package httputil
-
- import (
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "strings"
- "sync"
- )
-
- type busterWriter struct {
- headerMap http.Header
- status int
- io.Writer
- }
-
- func (bw *busterWriter) Header() http.Header {
- return bw.headerMap
- }
-
- func (bw *busterWriter) WriteHeader(status int) {
- bw.status = status
- }
-
- // CacheBusters maintains a cache of cache busting tokens for static resources served by Handler.
- type CacheBusters struct {
- Handler http.Handler
-
- mu sync.Mutex
- tokens map[string]string
- }
-
- func sanitizeTokenRune(r rune) rune {
- if r <= ' ' || r >= 127 {
- return -1
- }
- // Convert percent encoding reserved characters to '-'.
- if strings.ContainsRune("!#$&'()*+,/:;=?@[]", r) {
- return '-'
- }
- return r
- }
-
- // Get returns the cache busting token for path. If the token is not already
- // cached, Get issues a HEAD request on handler and uses the response ETag and
- // Last-Modified headers to compute a token.
- func (cb *CacheBusters) Get(path string) string {
- cb.mu.Lock()
- if cb.tokens == nil {
- cb.tokens = make(map[string]string)
- }
- token, ok := cb.tokens[path]
- cb.mu.Unlock()
- if ok {
- return token
- }
-
- w := busterWriter{
- Writer: ioutil.Discard,
- headerMap: make(http.Header),
- }
- r := &http.Request{URL: &url.URL{Path: path}, Method: "HEAD"}
- cb.Handler.ServeHTTP(&w, r)
-
- if w.status == 200 {
- token = w.headerMap.Get("Etag")
- if token == "" {
- token = w.headerMap.Get("Last-Modified")
- }
- token = strings.Trim(token, `" `)
- token = strings.Map(sanitizeTokenRune, token)
- }
-
- cb.mu.Lock()
- cb.tokens[path] = token
- cb.mu.Unlock()
-
- return token
- }
-
- // AppendQueryParam appends the token as a query parameter to path.
- func (cb *CacheBusters) AppendQueryParam(path string, name string) string {
- token := cb.Get(path)
- if token == "" {
- return path
- }
- return path + "?" + name + "=" + token
- }
|