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  }