public string BuildCacheControlHeader(CacheInfo cacheInfo) { var maxAge = cacheInfo.MaxAge; if (maxAge == null && (cacheInfo.LastModified != null || cacheInfo.ETag != null)) maxAge = DefaultMaxAge; var cacheHeader = new List<string>(); if (maxAge != null) cacheHeader.Add("max-age=" + maxAge.Value.TotalSeconds); if (cacheInfo.CacheControl != CacheControl.None) { var cache = cacheInfo.CacheControl; if (cache.HasFlag(CacheControl.Public)) cacheHeader.Add("public"); else if (cache.HasFlag(CacheControl.Private)) cacheHeader.Add("private"); if (cache.HasFlag(CacheControl.MustRevalidate)) cacheHeader.Add("must-revalidate"); if (cache.HasFlag(CacheControl.NoCache)) cacheHeader.Add("no-cache"); if (cache.HasFlag(CacheControl.NoStore)) cacheHeader.Add("no-store"); if (cache.HasFlag(CacheControl.NoTransform)) cacheHeader.Add("no-transform"); if (cache.HasFlag(CacheControl.ProxyRevalidate)) cacheHeader.Add("proxy-revalidate"); } if (cacheHeader.Count <= 0) return null; var cacheControl = cacheHeader.ToArray().Join(", "); return CacheControlFilter != null ? CacheControlFilter(cacheControl) : cacheControl; }
private bool CacheAndWriteResponse(CacheInfo cacheInfo, IRequest req, IResponse res, object response) { var httpResult = response as IHttpResult; var dto = httpResult != null ? httpResult.Response : response; if (dto == null || dto is IPartialWriter || dto is IStreamWriter) return false; var expiresIn = cacheInfo.ExpiresIn.GetValueOrDefault(DefaultExpiresIn); var cache = cacheInfo.LocalCache ? HostContext.LocalCache : HostContext.Cache; var responseBytes = dto as byte[]; if (responseBytes == null) { var rawStr = dto as string; if (rawStr != null) responseBytes = rawStr.ToUtf8Bytes(); else { var stream = dto as Stream; if (stream != null) responseBytes = stream.ReadFully(); } } var encoding = req.GetCompressionType(); var cacheKeyEncoded = encoding != null ? cacheInfo.CacheKey + "." + encoding : null; if (responseBytes != null || req.ResponseContentType.IsBinary()) { if (responseBytes == null) responseBytes = HostContext.ContentTypes.SerializeToBytes(req, dto); cache.Set(cacheInfo.CacheKey, responseBytes, expiresIn); if (encoding != null) { res.AddHeader(HttpHeaders.ContentEncoding, encoding); responseBytes = responseBytes.CompressBytes(encoding); cache.Set(cacheKeyEncoded, responseBytes, expiresIn); } } else { var serializedDto = req.SerializeToString(dto); if (req.ResponseContentType.MatchesContentType(MimeTypes.Json)) { var jsonp = req.GetJsonpCallback(); if (jsonp != null) serializedDto = jsonp + "(" + serializedDto + ")"; } responseBytes = serializedDto.ToUtf8Bytes(); cache.Set(cacheInfo.CacheKey, responseBytes, expiresIn); if (encoding != null) { responseBytes = responseBytes.CompressBytes(encoding); cache.Set(cacheKeyEncoded, responseBytes, expiresIn); res.AddHeader(HttpHeaders.ContentEncoding, encoding); } } var doHttpCaching = cacheInfo.MaxAge != null || cacheInfo.CacheControl != CacheControl.None; if (doHttpCaching) { var cacheControl = BuildCacheControlHeader(cacheInfo); if (cacheControl != null) { var lastModified = cacheInfo.LastModified.GetValueOrDefault(DateTime.UtcNow); cache.Set("date:" + cacheInfo.CacheKey, lastModified, expiresIn); res.AddHeaderLastModified(lastModified); res.AddHeader(HttpHeaders.CacheControl, cacheControl); if (encoding != null) res.AddHeader(HttpHeaders.Vary, "Accept-Encoding"); if (cacheInfo.VaryByUser) res.AddHeader(HttpHeaders.Vary, "Cookie"); } } if (httpResult != null) { foreach (var header in httpResult.Headers) { res.AddHeader(header.Key, header.Value); } } res.WriteBytesToResponse(responseBytes, req.ResponseContentType); return true; }
public static bool HandleValidCache(this IRequest req, CacheInfo cacheInfo) { if (cacheInfo == null) return false; var res = req.Response; var cache = cacheInfo.LocalCache ? HostContext.LocalCache : HostContext.Cache; DateTime? lastModified = null; var doHttpCaching = cacheInfo.MaxAge != null || cacheInfo.CacheControl != CacheControl.None; if (doHttpCaching) { lastModified = cache.Get<DateTime?>(cacheInfo.LastModifiedKey()); if (req.HasValidCache(lastModified)) { res.EndNotModified(); return true; } } var encoding = !cacheInfo.NoCompression ? req.GetCompressionType() : null; var responseBytes = encoding != null ? cache.Get<byte[]>(cacheInfo.CacheKey + "." + encoding) : cache.Get<byte[]>(cacheInfo.CacheKey); if (responseBytes != null) { if (encoding != null) res.AddHeader(HttpHeaders.ContentEncoding, encoding); if (cacheInfo.VaryByUser) res.AddHeader(HttpHeaders.Vary, "Cookie"); var cacheControl = HostContext.GetPlugin<HttpCacheFeature>().BuildCacheControlHeader(cacheInfo); if (cacheControl != null) res.AddHeader(HttpHeaders.CacheControl, cacheControl); if (!doHttpCaching) lastModified = cache.Get<DateTime?>(cacheInfo.LastModifiedKey()); if (lastModified != null) res.AddHeader(HttpHeaders.LastModified, lastModified.Value.ToUniversalTime().ToString("r")); res.WriteBytesToResponse(responseBytes, req.ResponseContentType); return true; } return false; }
public override void Execute(IRequest req, IResponse res, object requestDto) { if (req.Verb != HttpMethods.Get && req.Verb != HttpMethods.Head) return; if (req.IsInProcessRequest()) return; var feature = HostContext.GetPlugin<HttpCacheFeature>(); if (feature == null) throw new NotSupportedException(ErrorMessages.CacheFeatureMustBeEnabled.Fmt("[CacheResponse]")); var keyBase = "res:" + req.RawUrl; var keySuffix = MimeTypes.GetExtension(req.ResponseContentType); var modifiers = ""; if (req.ResponseContentType == MimeTypes.Json) { string jsonp = req.GetJsonpCallback(); if (jsonp != null) modifiers = "jsonp:" + jsonp.SafeVarName(); } if (VaryByUser) modifiers += (modifiers.Length > 0 ? "+" : "") + "user:"******"+" : "") + "role:" + role; } } } } if (modifiers.Length > 0) keySuffix += "+" + modifiers; var cacheInfo = new CacheInfo { KeyBase = keyBase, KeyModifiers = keySuffix, ExpiresIn = Duration > 0 ? TimeSpan.FromSeconds(Duration) : (TimeSpan?)null, MaxAge = MaxAge >= 0 ? TimeSpan.FromSeconds(MaxAge) : (TimeSpan?)null, CacheControl = CacheControl, VaryByUser = VaryByUser, LocalCache = LocalCache, NoCompression = NoCompression, }; if (req.HandleValidCache(cacheInfo)) return; req.Items[Keywords.CacheInfo] = cacheInfo; }
public static bool HandleValidCache(this IRequest req, CacheInfo cacheInfo) { if (cacheInfo == null) { return(false); } var res = req.Response; var cache = cacheInfo.LocalCache ? HostContext.AppHost.GetMemoryCacheClient(req) : HostContext.AppHost.GetCacheClient(req); DateTime?lastModified = null; var doHttpCaching = cacheInfo.MaxAge != null || cacheInfo.CacheControl != CacheControl.None; if (doHttpCaching) { lastModified = cache.Get <DateTime?>(cacheInfo.LastModifiedKey()); if (req.HasValidCache(lastModified)) { res.EndNotModified(); return(true); } } var encoding = !cacheInfo.NoCompression ? req.GetCompressionType() : null; var responseBytes = encoding != null ? cache.Get <byte[]>(cacheInfo.CacheKey + "." + encoding) : cache.Get <byte[]>(cacheInfo.CacheKey); if (responseBytes != null) { if (encoding != null) { res.AddHeader(HttpHeaders.ContentEncoding, encoding); } if (cacheInfo.VaryByUser) { res.AddHeader(HttpHeaders.Vary, "Cookie"); } var cacheControl = HostContext.GetPlugin <HttpCacheFeature>().BuildCacheControlHeader(cacheInfo); if (cacheControl != null) { res.AddHeader(HttpHeaders.CacheControl, cacheControl); } if (!doHttpCaching) { lastModified = cache.Get <DateTime?>(cacheInfo.LastModifiedKey()); } if (lastModified != null) { res.AddHeader(HttpHeaders.LastModified, lastModified.Value.ToUniversalTime().ToString("r")); } res.WriteBytesToResponse(responseBytes, req.ResponseContentType); return(true); } return(false); }
public override void Execute(IRequest req, IResponse res, object requestDto) { if (req.Verb != HttpMethods.Get && req.Verb != HttpMethods.Head) { return; } if (req.IsInProcessRequest()) { return; } var feature = HostContext.GetPlugin <HttpCacheFeature>(); if (feature == null) { throw new NotSupportedException(ErrorMessages.CacheFeatureMustBeEnabled.Fmt("[CacheResponse]")); } var keyBase = "res:" + req.RawUrl; var keySuffix = MimeTypes.GetExtension(req.ResponseContentType); var modifiers = ""; if (req.ResponseContentType == MimeTypes.Json) { string jsonp = req.GetJsonpCallback(); if (jsonp != null) { modifiers = "jsonp:" + jsonp.SafeVarName(); } } if (VaryByUser) { modifiers += (modifiers.Length > 0 ? "+" : "") + "user:"******"+" : "") + "role:" + role; } } } } } if (modifiers.Length > 0) { keySuffix += "+" + modifiers; } var cacheInfo = new CacheInfo { KeyBase = keyBase, KeyModifiers = keySuffix, ExpiresIn = Duration > 0 ? TimeSpan.FromSeconds(Duration) : (TimeSpan?)null, MaxAge = MaxAge >= 0 ? TimeSpan.FromSeconds(MaxAge) : (TimeSpan?)null, CacheControl = CacheControl, VaryByUser = VaryByUser, LocalCache = LocalCache, NoCompression = NoCompression, }; if (req.HandleValidCache(cacheInfo)) { return; } req.Items[Keywords.CacheInfo] = cacheInfo; }
public static string LastModifiedKey(this CacheInfo cacheInfo) { return("date:" + cacheInfo.CacheKey); }
public string BuildCacheControlHeader(CacheInfo cacheInfo) { var maxAge = cacheInfo.MaxAge; if (maxAge == null && (cacheInfo.LastModified != null || cacheInfo.ETag != null)) { maxAge = DefaultMaxAge; } var cacheHeader = new List <string>(); if (maxAge != null) { cacheHeader.Add("max-age=" + maxAge.Value.TotalSeconds); } if (cacheInfo.CacheControl != CacheControl.None) { var cache = cacheInfo.CacheControl; if (cache.Has(CacheControl.Public)) { cacheHeader.Add("public"); } else if (cache.Has(CacheControl.Private)) { cacheHeader.Add("private"); } if (cache.Has(CacheControl.MustRevalidate)) { cacheHeader.Add("must-revalidate"); } if (cache.Has(CacheControl.NoCache)) { cacheHeader.Add("no-cache"); } if (cache.Has(CacheControl.NoStore)) { cacheHeader.Add("no-store"); } if (cache.Has(CacheControl.NoTransform)) { cacheHeader.Add("no-transform"); } if (cache.Has(CacheControl.ProxyRevalidate)) { cacheHeader.Add("proxy-revalidate"); } } if (cacheHeader.Count <= 0) { return(null); } var cacheControl = cacheHeader.ToArray().Join(", "); return(CacheControlFilter != null ? CacheControlFilter(cacheControl) : cacheControl); }
private bool CacheAndWriteResponse(CacheInfo cacheInfo, IRequest req, IResponse res, object response) { var httpResult = response as IHttpResult; var dto = httpResult != null ? httpResult.Response : response; if (dto == null || dto is IPartialWriter || dto is IStreamWriter) { return(false); } var expiresIn = cacheInfo.ExpiresIn.GetValueOrDefault(DefaultExpiresIn); var cache = cacheInfo.LocalCache ? HostContext.LocalCache : HostContext.Cache; var responseBytes = dto as byte[]; if (responseBytes == null) { var rawStr = dto as string; if (rawStr != null) { responseBytes = rawStr.ToUtf8Bytes(); } else { var stream = dto as Stream; if (stream != null) { responseBytes = stream.ReadFully(); } } } var encoding = !cacheInfo.NoCompression ? req.GetCompressionType() : null; var cacheKeyEncoded = encoding != null ? cacheInfo.CacheKey + "." + encoding : null; if (responseBytes != null || req.ResponseContentType.IsBinary()) { if (responseBytes == null) { responseBytes = HostContext.ContentTypes.SerializeToBytes(req, dto); } cache.Set(cacheInfo.CacheKey, responseBytes, expiresIn); if (encoding != null) { res.AddHeader(HttpHeaders.ContentEncoding, encoding); responseBytes = responseBytes.CompressBytes(encoding); cache.Set(cacheKeyEncoded, responseBytes, expiresIn); } } else { var serializedDto = req.SerializeToString(dto); if (req.ResponseContentType.MatchesContentType(MimeTypes.Json)) { var jsonp = req.GetJsonpCallback(); if (jsonp != null) { serializedDto = jsonp + "(" + serializedDto + ")"; } } responseBytes = serializedDto.ToUtf8Bytes(); cache.Set(cacheInfo.CacheKey, responseBytes, expiresIn); if (encoding != null) { responseBytes = responseBytes.CompressBytes(encoding); cache.Set(cacheKeyEncoded, responseBytes, expiresIn); res.AddHeader(HttpHeaders.ContentEncoding, encoding); } } var doHttpCaching = cacheInfo.MaxAge != null || cacheInfo.CacheControl != CacheControl.None; if (doHttpCaching) { var cacheControl = BuildCacheControlHeader(cacheInfo); if (cacheControl != null) { var lastModified = cacheInfo.LastModified.GetValueOrDefault(DateTime.UtcNow); cache.Set("date:" + cacheInfo.CacheKey, lastModified, expiresIn); res.AddHeaderLastModified(lastModified); res.AddHeader(HttpHeaders.CacheControl, cacheControl); if (encoding != null) { res.AddHeader(HttpHeaders.Vary, "Accept-Encoding"); } if (cacheInfo.VaryByUser) { res.AddHeader(HttpHeaders.Vary, "Cookie"); } } } if (httpResult != null) { foreach (var header in httpResult.Headers) { res.AddHeader(header.Key, header.Value); } } res.WriteBytesToResponse(responseBytes, req.ResponseContentType); return(true); }
public static async Task <bool> HandleValidCache(this IRequest req, CacheInfo cacheInfo, CancellationToken token = default) { if (cacheInfo == null) { return(false); } ICacheClient cache; ICacheClientAsync cacheAsync = null; // only non-null if native ICacheClientAsync exists if (cacheInfo.LocalCache) { cache = HostContext.AppHost.GetMemoryCacheClient(req); } else { HostContext.AppHost.TryGetNativeCacheClient(req, out cache, out cacheAsync); } var cacheControl = HostContext.GetPlugin <HttpCacheFeature>().BuildCacheControlHeader(cacheInfo); var res = req.Response; DateTime?lastModified = null; var doHttpCaching = cacheInfo.MaxAge != null || cacheInfo.CacheControl != CacheControl.None; if (doHttpCaching) { lastModified = cacheAsync != null ? await cacheAsync.GetAsync <DateTime?>(cacheInfo.LastModifiedKey(), token).ConfigAwait() : cache.Get <DateTime?>(cacheInfo.LastModifiedKey()); if (req.HasValidCache(lastModified)) { if (cacheControl != null) { res.AddHeader(HttpHeaders.CacheControl, cacheControl); } res.EndNotModified(); return(true); } } var encoding = !cacheInfo.NoCompression ? req.GetCompressionType() : null; var useCacheKey = encoding != null ? cacheInfo.CacheKey + "." + encoding : cacheInfo.CacheKey; var responseBytes = cacheAsync != null ? await cacheAsync.GetAsync <byte[]>(useCacheKey, token).ConfigAwait() : cache.Get <byte[]>(useCacheKey); if (responseBytes != null) { if (encoding != null) { res.AddHeader(HttpHeaders.ContentEncoding, encoding); } if (cacheInfo.VaryByUser) { res.AddHeader(HttpHeaders.Vary, "Cookie"); } if (cacheControl != null) { res.AddHeader(HttpHeaders.CacheControl, cacheControl); } if (!doHttpCaching) { lastModified = cache.Get <DateTime?>(cacheInfo.LastModifiedKey()); } if (lastModified != null) { res.AddHeader(HttpHeaders.LastModified, lastModified.Value.ToUniversalTime().ToString("r")); } await res.WriteBytesToResponse(responseBytes, req.ResponseContentType, token).ConfigAwait(); return(true); } return(false); }
private async Task <bool> CacheAndWriteResponse(CacheInfo cacheInfo, IRequest req, IResponse res, object response) { var httpResult = response as IHttpResult; var dto = httpResult != null ? httpResult.Response : response; if (dto == null || dto is IPartialWriter || dto is IPartialWriterAsync || dto is IStreamWriter || dto is IStreamWriterAsync) { return(false); } var expiresIn = cacheInfo.ExpiresIn.GetValueOrDefault(DefaultExpiresIn); var cache = cacheInfo.LocalCache ? HostContext.AppHost.GetMemoryCacheClient(req) : HostContext.AppHost.GetCacheClient(req); var responseBytes = dto as byte[]; if (responseBytes == null) { responseBytes = dto switch { string rawStr => rawStr.ToUtf8Bytes(), MemoryStream ms => await ms.ReadFullyAsync().ConfigAwait(), Stream stream => await stream.ReadFullyAsync().ConfigAwait(), _ => responseBytes }; } var encoding = !cacheInfo.NoCompression ? req.GetCompressionType() : null; if (response is HttpResult customResult) { if (customResult.View != null) { req.Items[Keywords.View] = customResult.View; } if (customResult.Template != null) { req.Items[Keywords.Template] = customResult.Template; } } using (httpResult?.ResultScope?.Invoke()) using (HostContext.Config.AllowJsConfig ? JsConfig.CreateScope(req.QueryString[Keywords.JsConfig]) : null) { var cacheKeyEncoded = encoding != null ? cacheInfo.CacheKey + "." + encoding : null; if (responseBytes != null || req.ResponseContentType.IsBinary()) { if (responseBytes == null) { responseBytes = HostContext.ContentTypes.SerializeToBytes(req, dto); } cache.Set(cacheInfo.CacheKey, responseBytes, expiresIn); if (encoding != null) { res.AddHeader(HttpHeaders.ContentEncoding, encoding); responseBytes = responseBytes.CompressBytes(encoding); cache.Set(cacheKeyEncoded, responseBytes, expiresIn); } } else { var serializedDto = req.SerializeToString(dto); if (req.ResponseContentType.MatchesContentType(MimeTypes.Json)) { var jsonp = req.GetJsonpCallback(); if (jsonp != null) { serializedDto = jsonp + "(" + serializedDto + ")"; } } responseBytes = serializedDto.ToUtf8Bytes(); cache.Set(cacheInfo.CacheKey, responseBytes, expiresIn); if (encoding != null) { res.AddHeader(HttpHeaders.ContentEncoding, encoding); responseBytes = responseBytes.CompressBytes(encoding); cache.Set(cacheKeyEncoded, responseBytes, expiresIn); } } } var doHttpCaching = cacheInfo.MaxAge != null || cacheInfo.CacheControl != CacheControl.None; if (doHttpCaching) { var cacheControl = BuildCacheControlHeader(cacheInfo); if (cacheControl != null) { var lastModified = cacheInfo.LastModified.GetValueOrDefault(DateTime.UtcNow); cache.Set("date:" + cacheInfo.CacheKey, lastModified, expiresIn); res.AddHeaderLastModified(lastModified); res.AddHeader(HttpHeaders.CacheControl, cacheControl); if (encoding != null) { res.AddHeader(HttpHeaders.Vary, "Accept-Encoding"); } if (cacheInfo.VaryByUser) { res.AddHeader(HttpHeaders.Vary, "Cookie"); } } } if (httpResult != null) { foreach (var header in httpResult.Headers) { res.AddHeader(header.Key, header.Value); } } await res.WriteBytesToResponse(responseBytes, req.ResponseContentType); return(true); }