github.com/aacfactory/fns@v1.2.85/transports/middlewares/cachecontrol/make.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package cachecontrol 19 20 import ( 21 "bytes" 22 "github.com/aacfactory/fns/commons/bytex" 23 "github.com/aacfactory/fns/context" 24 "github.com/aacfactory/fns/services" 25 "github.com/aacfactory/fns/transports" 26 "github.com/valyala/bytebufferpool" 27 "strconv" 28 ) 29 30 var ( 31 pragma = []byte("Pragma") 32 noCache = []byte("no-cache") 33 noStore = []byte("no-store") 34 zeroMaxAge = []byte("max-age=0") 35 noTransform = []byte("no-transform") 36 public = []byte("public") 37 private = []byte("private") 38 maxAge = []byte("max-age") 39 mustRevalidate = []byte("must-revalidate") 40 proxyRevalidate = []byte("proxy-revalidate") 41 comma = []byte(", ") 42 equal = []byte("=") 43 ) 44 45 type MakeOptions struct { 46 mustRevalidate bool 47 proxyRevalidate bool 48 public bool 49 maxAge int 50 } 51 52 type MakeOption func(option *MakeOptions) 53 54 func MustRevalidate() MakeOption { 55 return func(option *MakeOptions) { 56 option.mustRevalidate = true 57 } 58 } 59 60 func ProxyRevalidate() MakeOption { 61 return func(option *MakeOptions) { 62 option.proxyRevalidate = true 63 } 64 } 65 66 func Private() MakeOption { 67 return func(option *MakeOptions) { 68 option.public = false 69 } 70 } 71 72 func Public() MakeOption { 73 return func(option *MakeOptions) { 74 option.public = true 75 } 76 } 77 78 func MaxAge(age int) MakeOption { 79 return func(option *MakeOptions) { 80 if age < 0 { 81 age = 0 82 } 83 option.maxAge = age 84 } 85 } 86 87 func Make(ctx context.Context, options ...MakeOption) { 88 // check enable 89 enableCV := ctx.LocalValue(contextEnableKey) 90 if enableCV == nil { 91 return 92 } 93 if enable, _ := enableCV.(bool); !enable { 94 return 95 } 96 // check internal 97 sr, hasSR := services.TryLoadRequest(ctx) 98 if !hasSR { 99 return 100 } 101 if sr.Header().Internal() { 102 return 103 } 104 // check transport request path 105 tr, hasTR := transports.TryLoadRequest(ctx) 106 if !hasTR { 107 return 108 } 109 path := tr.Path() 110 s, f := sr.Fn() 111 sf := append([]byte{'/'}, s...) 112 sf = append(sf, '/') 113 sf = append(sf, f...) 114 if !bytes.Equal(path, sf) { 115 return 116 } 117 118 header := tr.Header() 119 // pragma 120 ph := header.Get(pragma) 121 if len(ph) > 0 && bytes.Equal(ph, noCache) { 122 return 123 } 124 // 125 noTransformEnabled := false 126 // cache control 127 cch := header.Get(transports.CacheControlHeaderName) 128 if len(cch) > 0 { 129 // no-cache, no-store, max-age=0 130 if bytes.Contains(cch, noCache) || bytes.Contains(cch, noStore) || bytes.Contains(cch, zeroMaxAge) { 131 return 132 } 133 if bytes.Contains(cch, noTransform) { 134 noTransformEnabled = true 135 } 136 } 137 // write response header 138 responseHeader, hasResponseHeader := transports.TryLoadResponseHeader(ctx) 139 if !hasResponseHeader { 140 return 141 } 142 opt := MakeOptions{} 143 for _, option := range options { 144 option(&opt) 145 } 146 ccr := bytebufferpool.Get() 147 if opt.public { 148 _, _ = ccr.Write(comma) 149 _, _ = ccr.Write(public) 150 if opt.proxyRevalidate { 151 _, _ = ccr.Write(comma) 152 _, _ = ccr.Write(proxyRevalidate) 153 } 154 if opt.mustRevalidate { 155 _, _ = ccr.Write(comma) 156 _, _ = ccr.Write(mustRevalidate) 157 } 158 if noTransformEnabled { 159 _, _ = ccr.Write(comma) 160 _, _ = ccr.Write(noTransform) 161 } 162 } else { 163 _, _ = ccr.Write(comma) 164 _, _ = ccr.Write(private) 165 if opt.mustRevalidate { 166 _, _ = ccr.Write(comma) 167 _, _ = ccr.Write(mustRevalidate) 168 } 169 } 170 if opt.maxAge > 0 { 171 _, _ = ccr.Write(comma) 172 _, _ = ccr.Write(maxAge) 173 _, _ = ccr.Write(equal) 174 _, _ = ccr.Write(bytex.FromString(strconv.Itoa(opt.maxAge))) 175 } 176 h := ccr.Bytes() 177 if len(h) > 0 { 178 h = h[2:] 179 } 180 responseHeader.Set(transports.CacheControlHeaderName, h) 181 bytebufferpool.Put(ccr) 182 return 183 }