github.com/vmware/transport-go@v1.3.4/plank/pkg/middleware/cache_control.go (about) 1 // Copyright 2019-2021 VMware, Inc. 2 // SPDX-License-Identifier: BSD-2-Clause 3 4 package middleware 5 6 import ( 7 "fmt" 8 "github.com/gobwas/glob" 9 "github.com/gorilla/mux" 10 "github.com/vmware/transport-go/plank/utils" 11 "net/http" 12 "strings" 13 "time" 14 ) 15 16 // CacheControlRulePair is a container that lumps together the glob pattern, cache control rule that should be applied to 17 // for the matching pattern and the compiled glob pattern for use in runtime. see https://github.com/gobwas/glob for 18 // detailed examples of glob patterns. 19 type CacheControlRulePair struct { 20 GlobPattern string 21 CacheControlRule string 22 CompiledGlobPattern glob.Glob 23 } 24 25 // NewCacheControlRulePair returns a new CacheControlRulePair with the provided text glob pattern and its compiled 26 // counterpart and the cache control rule. 27 func NewCacheControlRulePair(globPattern string, cacheControlRule string) (CacheControlRulePair, error) { 28 var err error 29 pair := CacheControlRulePair{ 30 GlobPattern: globPattern, 31 CacheControlRule: cacheControlRule, 32 } 33 34 pair.CompiledGlobPattern, err = glob.Compile(globPattern) 35 return pair, err 36 } 37 38 // CacheControlDirective is a data structure that represents the entry for Cache-Control rules 39 type CacheControlDirective struct { 40 directives []string 41 } 42 43 // NewCacheControlDirective returns creates a new instance of CacheControlDirective and returns its pointer 44 func NewCacheControlDirective() *CacheControlDirective { 45 return &CacheControlDirective{} 46 } 47 48 func (c *CacheControlDirective) Public() *CacheControlDirective { 49 c.directives = append(c.directives, "public") 50 return c 51 } 52 53 func (c *CacheControlDirective) Private() *CacheControlDirective { 54 c.directives = append(c.directives, "private") 55 return c 56 } 57 58 func (c *CacheControlDirective) NoCache() *CacheControlDirective { 59 c.directives = append(c.directives, "no-cache") 60 return c 61 } 62 63 func (c *CacheControlDirective) NoStore() *CacheControlDirective { 64 c.directives = append(c.directives, "no-store") 65 return c 66 } 67 68 func (c *CacheControlDirective) MaxAge(t time.Duration) *CacheControlDirective { 69 c.directives = append(c.directives, fmt.Sprintf("max-age=%d", int64(t.Seconds()))) 70 return c 71 } 72 73 func (c *CacheControlDirective) SharedMaxAge(t time.Duration) *CacheControlDirective { 74 c.directives = append(c.directives, fmt.Sprintf("s-maxage=%d", int64(t.Seconds()))) 75 return c 76 } 77 78 func (c *CacheControlDirective) MaxStale(t time.Duration) *CacheControlDirective { 79 c.directives = append(c.directives, fmt.Sprintf("max-stale=%d", int64(t.Seconds()))) 80 return c 81 } 82 83 func (c *CacheControlDirective) MinFresh(t time.Duration) *CacheControlDirective { 84 c.directives = append(c.directives, fmt.Sprintf("min-fresh=%d", int64(t.Seconds()))) 85 return c 86 } 87 88 func (c *CacheControlDirective) MustRevalidate() *CacheControlDirective { 89 c.directives = append(c.directives, "must-revalidate") 90 return c 91 } 92 93 func (c *CacheControlDirective) ProxyRevalidate() *CacheControlDirective { 94 c.directives = append(c.directives, "proxy-revalidate") 95 return c 96 } 97 98 func (c *CacheControlDirective) Immutable() *CacheControlDirective { 99 c.directives = append(c.directives, "immutable") 100 return c 101 } 102 103 func (c *CacheControlDirective) NoTransform() *CacheControlDirective { 104 c.directives = append(c.directives, "no-transform") 105 return c 106 } 107 108 func (c *CacheControlDirective) OnlyIfCached() *CacheControlDirective { 109 c.directives = append(c.directives, "only-if-cached") 110 return c 111 } 112 113 func (c *CacheControlDirective) String() string { 114 return strings.Join(c.directives, ", ") 115 } 116 117 // CacheControlMiddleware is the middleware function to be provided as a parameter to mux.Handler() 118 func CacheControlMiddleware(globPatterns []string, directive *CacheControlDirective) mux.MiddlewareFunc { 119 parsed := parseGlobPatterns(globPatterns) 120 return func(handler http.Handler) http.Handler { 121 return cacheControlWrapper(handler, parsed, directive) 122 } 123 } 124 125 // parseGlobPatterns takes an array of glob patterns and returns an array of glob.Glob instances 126 func parseGlobPatterns(globPatterns []string) []glob.Glob { 127 results := make([]glob.Glob, 0) 128 for _, exp := range globPatterns { 129 globP, err := glob.Compile(exp) 130 if err != nil { 131 utils.Log.Errorln("Ignoring invalid glob pattern provided as cache control matcher rule", err) 132 continue 133 } 134 results = append(results, globP) 135 } 136 return results 137 } 138 139 // cacheControlWrapper is the internal function that actually performs the adding of cache control rules based on 140 // glob pattern matching and the rules provided. 141 func cacheControlWrapper(h http.Handler, globs []glob.Glob, directive *CacheControlDirective) http.Handler { 142 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 143 if len(globs) == 0 { 144 h.ServeHTTP(w, r) 145 return 146 } 147 148 uriMatches := false 149 for _, glob := range globs { 150 if uriMatches = glob.Match(r.RequestURI); uriMatches { 151 break 152 } 153 } 154 155 if !uriMatches { 156 h.ServeHTTP(w, r) 157 } 158 159 w.Header().Set("Cache-Control", directive.String()) 160 h.ServeHTTP(w, r) 161 }) 162 }