/// <summary> /// Gets the HTTP result. /// </summary> private IHasHeaders GetHttpResult(IRequest requestContext, object content, string contentType, bool addCachePrevention, IDictionary <string, string> responseHeaders = null) { IHasHeaders result; var stream = content as Stream; if (stream != null) { result = new StreamWriter(stream, contentType, _logger); } else { var bytes = content as byte[]; if (bytes != null) { result = new StreamWriter(bytes, contentType, _logger); } else { var text = content as string; if (text != null) { var compressionType = requestContext == null ? null : GetCompressionType(requestContext); if (string.IsNullOrEmpty(compressionType)) { result = new StreamWriter(Encoding.UTF8.GetBytes(text), contentType, _logger); } else { var isHeadRequest = string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase); result = GetCompressedResult(Encoding.UTF8.GetBytes(text), compressionType, responseHeaders, isHeadRequest, contentType); } } else { result = new HttpResult(content, contentType, HttpStatusCode.OK); } } } if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(); } string expires; if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires)) { responseHeaders["Expires"] = "-1"; } AddResponseHeaders(result, responseHeaders); return(result); }
/// <summary> /// Pres the process optimized result. /// </summary> private object GetCachedResult(IRequest requestContext, IDictionary <string, string> responseHeaders, StaticResultOptions options) { bool noCache = (requestContext.Headers[HeaderNames.CacheControl].ToString()).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1; AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified); if (!noCache) { if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var ifModifiedSinceHeader)) { _logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]); return(null); } if (IsNotModified(ifModifiedSinceHeader, options.CacheDuration, options.DateLastModified)) { AddAgeHeader(responseHeaders, options.DateLastModified); var result = new HttpResult(Array.Empty <byte>(), options.ContentType ?? "text/html", HttpStatusCode.NotModified); AddResponseHeaders(result, responseHeaders); return(result); } } return(null); }
private object GetOptimizedResultInternal <T>(IRequest requestContext, T result, bool addCachePrevention, IDictionary <string, string> responseHeaders = null) where T : class { if (result == null) { throw new ArgumentNullException("result"); } var optimizedResult = ToOptimizedResult(requestContext, result); if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } if (addCachePrevention) { responseHeaders["Expires"] = "-1"; } // Apply headers var hasHeaders = optimizedResult as IHasHeaders; if (hasHeaders != null) { AddResponseHeaders(hasHeaders, responseHeaders); } return(optimizedResult); }
/// <summary> /// Returns the optimized result for the IRequestContext. /// Does not use or store results in any cache. /// </summary> /// <param name="request"></param> /// <param name="dto"></param> /// <returns></returns> public object ToOptimizedResult <T>(IRequest request, T dto) { var compressionType = GetCompressionType(request); if (compressionType == null) { var contentType = request.ResponseContentType; switch (GetRealContentType(contentType)) { case "application/xml": case "text/xml": case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml return(SerializeToXmlString(dto)); case "application/json": case "text/json": return(_jsonSerializer.SerializeToString(dto)); } } // Do not use the memoryStreamFactory here, they don't place nice with compression using (var ms = new MemoryStream()) { ContentTypes.Instance.SerializeToStream(request, dto, ms); ms.Position = 0; var responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); return(GetCompressedResult(ms, compressionType, responseHeaders, false, request.ResponseContentType).Result); } }
public async Task <object> GetStaticResult(IRequest requestContext, StaticResultOptions options) { var cacheKey = options.CacheKey; options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var contentType = options.ContentType; if (cacheKey == Guid.Empty) { throw new ArgumentNullException("cacheKey"); } if (options.ContentFactory == null) { throw new ArgumentNullException("factoryFn"); } var key = cacheKey.ToString("N"); // See if the result is already cached in the browser var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType); if (result != null) { return(result); } var compress = ShouldCompressResponse(requestContext, contentType); var hasHeaders = await GetStaticResult(requestContext, options, compress).ConfigureAwait(false); AddResponseHeaders(hasHeaders, options.ResponseHeaders); return(hasHeaders); }
/// <summary> /// Gets the optimized result using cache. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="requestContext">The request context.</param> /// <param name="cacheKey">The cache key.</param> /// <param name="lastDateModified">The last date modified.</param> /// <param name="cacheDuration">Duration of the cache.</param> /// <param name="factoryFn">The factory fn.</param> /// <param name="responseHeaders">The response headers.</param> /// <returns>System.Object.</returns> /// <exception cref="System.ArgumentNullException">cacheKey /// or /// factoryFn</exception> public object GetOptimizedResultUsingCache <T>(IRequest requestContext, Guid cacheKey, DateTime?lastDateModified, TimeSpan?cacheDuration, Func <T> factoryFn, IDictionary <string, string> responseHeaders = null) where T : class { if (cacheKey == Guid.Empty) { throw new ArgumentNullException("cacheKey"); } if (factoryFn == null) { throw new ArgumentNullException("factoryFn"); } var key = cacheKey.ToString("N"); if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } // See if the result is already cached in the browser var result = GetCachedResult(requestContext, responseHeaders, cacheKey, key, lastDateModified, cacheDuration, null); if (result != null) { return(result); } return(GetOptimizedResultInternal(requestContext, factoryFn(), false, responseHeaders)); }
public Task <object> GetStaticFileResult(IRequest requestContext, StaticFileResultOptions options) { var path = options.Path; var fileShare = options.FileShare; if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite) { throw new ArgumentException("FileShare must be either Read or ReadWrite"); } if (string.IsNullOrWhiteSpace(options.ContentType)) { options.ContentType = MimeTypes.GetMimeType(path); } if (!options.DateLastModified.HasValue) { options.DateLastModified = _fileSystem.GetLastWriteTimeUtc(path); } var cacheKey = path + options.DateLastModified.Value.Ticks; options.CacheKey = cacheKey.GetMD5(); options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare)); return(GetStaticResult(requestContext, options)); }
public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request) { _logger = logger; this._response = response; Items = new Dictionary <string, object>(); Request = request; }
/// <summary> /// Returns the optimized result for the IRequestContext. /// Does not use or store results in any cache. /// </summary> /// <param name="request"></param> /// <param name="dto"></param> /// <returns></returns> public object ToOptimizedResult <T>(IRequest request, T dto) { var contentType = request.ResponseContentType; switch (GetRealContentType(contentType)) { case "application/xml": case "text/xml": case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml return(SerializeToXmlString(dto)); case "application/json": case "text/json": return(_jsonSerializer.SerializeToString(dto)); default: { var ms = new MemoryStream(); var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); writerFn(dto, ms); ms.Position = 0; if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase)) { return(GetHttpResult(new byte[] { }, contentType, true)); } return(GetHttpResult(ms, contentType, true)); } } }
/// <summary> /// Pres the process optimized result. /// </summary> private object GetCachedResult(IRequest requestContext, IDictionary <string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime?lastDateModified, TimeSpan?cacheDuration, string contentType) { responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString); var noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1; if (!noCache) { if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration)) { AddAgeHeader(responseHeaders, lastDateModified); AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration); var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified); AddResponseHeaders(result, responseHeaders); return(result); } } AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration); return(null); }
private object ToOptimizedResultInternal <T>(IRequest request, T dto, IDictionary <string, string> responseHeaders = null) { var contentType = request.ResponseContentType; switch (GetRealContentType(contentType)) { case "application/xml": case "text/xml": case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml return(GetHttpResult(request, SerializeToXmlString(dto), contentType, false, responseHeaders)); case "application/json": case "text/json": return(GetHttpResult(request, _jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders)); default: break; } var isHeadRequest = string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase); var ms = new MemoryStream(); var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); writerFn(dto, ms); ms.Position = 0; if (isHeadRequest) { return(GetHttpResult(request, new byte[] { }, contentType, true, responseHeaders)); } return(GetHttpResult(request, ms, contentType, true, responseHeaders)); }
/// <summary> /// Determines whether [is not modified] [the specified cache key]. /// </summary> /// <param name="requestContext">The request context.</param> /// <param name="cacheKey">The cache key.</param> /// <param name="lastDateModified">The last date modified.</param> /// <param name="cacheDuration">Duration of the cache.</param> /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns> private bool IsNotModified(IRequest requestContext, Guid cacheKey, DateTime?lastDateModified, TimeSpan?cacheDuration) { //var isNotModified = true; var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since"); if (!string.IsNullOrEmpty(ifModifiedSinceHeader) && DateTime.TryParse(ifModifiedSinceHeader, out DateTime ifModifiedSince) && IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified)) { return(true); } var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); bool hasCacheKey = !cacheKey.Equals(Guid.Empty); // Validate If-None-Match if ((hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))) { ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"'); if (Guid.TryParse(ifNoneMatchHeader, out Guid ifNoneMatch) && cacheKey.Equals(ifNoneMatch)) { return(true); } } return(false); }
private async Task <IHasHeaders> GetStaticResult(IRequest requestContext, StaticResultOptions options, bool compress) { var isHeadRequest = options.IsHeadRequest; var factoryFn = options.ContentFactory; var contentType = options.ContentType; var responseHeaders = options.ResponseHeaders; var requestedCompressionType = GetCompressionType(requestContext); if (!compress || string.IsNullOrEmpty(requestedCompressionType)) { var rangeHeader = requestContext.Headers.Get("Range"); if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) { return(new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) { OnComplete = options.OnComplete, OnError = options.OnError, FileShare = options.FileShare }); } if (!string.IsNullOrEmpty(rangeHeader)) { var stream = await factoryFn().ConfigureAwait(false); return(new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger) { OnComplete = options.OnComplete }); } else { var stream = await factoryFn().ConfigureAwait(false); responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); if (isHeadRequest) { stream.Dispose(); return(GetHttpResult(new byte[] { }, contentType, true)); } return(new StreamWriter(stream, contentType, _logger) { OnComplete = options.OnComplete, OnError = options.OnError }); } } using (var stream = await factoryFn().ConfigureAwait(false)) { return(await GetCompressedResult(stream, requestedCompressionType, responseHeaders, isHeadRequest, contentType).ConfigureAwait(false)); } }
/// <summary> /// Gets the optimized result. /// </summary> /// <typeparam name="T"></typeparam> public object GetResult <T>(IRequest requestContext, T result, IDictionary <string, string> responseHeaders = null) where T : class { if (result == null) { throw new ArgumentNullException(nameof(result)); } if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } responseHeaders[HeaderNames.Expires] = "0"; return(ToOptimizedResultInternal(requestContext, result, responseHeaders)); }
public Task <object> GetStaticFileResult(IRequest requestContext, StaticFileResultOptions options) { var path = options.Path; var fileShare = options.FileShare; if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite) { throw new ArgumentException("FileShare must be either Read or ReadWrite"); } if (string.IsNullOrWhiteSpace(options.ContentType)) { options.ContentType = MimeTypes.GetMimeType(path); } if (!options.DateLastModified.HasValue) { options.DateLastModified = _fileSystem.GetLastWriteTimeUtc(path); } var cacheKey = path + options.DateLastModified.Value.Ticks; options.CacheKey = cacheKey.GetMD5(); options.ContentFactory = () => Task.FromResult(GetFileStream(path, fileShare)); options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); if (!options.ResponseHeaders.ContainsKey("Content-Disposition")) { // Quotes are valid in linux. They'll possibly cause issues here var filename = (Path.GetFileName(path) ?? string.Empty).Replace("\"", string.Empty); if (!string.IsNullOrWhiteSpace(filename)) { options.ResponseHeaders["Content-Disposition"] = "inline; filename=\"" + filename + "\""; } } return(GetStaticResult(requestContext, options)); }
/// <summary> /// Gets the HTTP result. /// </summary> private IHasHeaders GetHttpResult(IRequest requestContext, byte[] content, string contentType, bool addCachePrevention, IDictionary <string, string> responseHeaders = null) { string compressionType = null; bool isHeadRequest = false; if (requestContext != null) { compressionType = GetCompressionType(requestContext, content, contentType); isHeadRequest = string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase); } IHasHeaders result; if (string.IsNullOrEmpty(compressionType)) { var contentLength = content.Length; if (isHeadRequest) { content = Array.Empty <byte>(); } result = new StreamWriter(content, contentType, contentLength, _logger); } else { result = GetCompressedResult(content, compressionType, responseHeaders, isHeadRequest, contentType); } if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(); } string expires; if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires)) { responseHeaders["Expires"] = "-1"; } AddResponseHeaders(result, responseHeaders); return(result); }
/// <summary> /// Determines whether [is not modified] [the specified cache key]. /// </summary> /// <param name="requestContext">The request context.</param> /// <param name="cacheKey">The cache key.</param> /// <param name="lastDateModified">The last date modified.</param> /// <param name="cacheDuration">Duration of the cache.</param> /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns> private bool IsNotModified(IRequest requestContext, Guid cacheKey) { var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); bool hasCacheKey = !cacheKey.Equals(Guid.Empty); // Validate If-None-Match if (hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader)) { if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch) && cacheKey.Equals(ifNoneMatch)) { return(true); } } return(false); }
public static string GetCompressionType(IRequest request) { var acceptEncoding = request.Headers["Accept-Encoding"]; if (!string.IsNullOrWhiteSpace(acceptEncoding)) { if (acceptEncoding.Contains("deflate")) { return("deflate"); } if (acceptEncoding.Contains("gzip")) { return("gzip"); } } return(null); }
/// <summary> /// Pres the process optimized result. /// </summary> /// <param name="requestContext">The request context.</param> /// <param name="responseHeaders">The responseHeaders.</param> /// <param name="cacheKey">The cache key.</param> /// <param name="cacheKeyString">The cache key string.</param> /// <param name="lastDateModified">The last date modified.</param> /// <param name="cacheDuration">Duration of the cache.</param> /// <param name="contentType">Type of the content.</param> /// <returns>System.Object.</returns> private object GetCachedResult(IRequest requestContext, IDictionary <string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime?lastDateModified, TimeSpan?cacheDuration, string contentType) { responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString); if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration)) { AddAgeHeader(responseHeaders, lastDateModified); AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration); var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified); AddResponseHeaders(result, responseHeaders); return(result); } AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration); return(null); }
private IHasHeaders GetCompressedResult(IRequest requestContext, byte[] content, string requestedCompressionType, IDictionary <string, string> responseHeaders, bool isHeadRequest, string contentType) { if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } try { content = Compress(content, requestedCompressionType); responseHeaders["Content-Encoding"] = requestedCompressionType; //var url = requestContext == null ? string.Empty : requestContext.RawUrl; //_logger.Debug("Compressed to {0} {1}!", requestedCompressionType, url); } catch (Exception ex) { //_logger.Error("Error compressing: " + ex.Message); } responseHeaders["Vary"] = "Accept-Encoding"; var contentLength = content.Length; if (isHeadRequest) { var result = new StreamWriter(Array.Empty <byte>(), contentType, contentLength, _logger); AddResponseHeaders(result, responseHeaders); return(result); } else { var result = new StreamWriter(content, contentType, contentLength, _logger); AddResponseHeaders(result, responseHeaders); return(result); } }
/// <summary> /// To the cached result. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="requestContext">The request context.</param> /// <param name="cacheKey">The cache key.</param> /// <param name="lastDateModified">The last date modified.</param> /// <param name="cacheDuration">Duration of the cache.</param> /// <param name="factoryFn">The factory fn.</param> /// <param name="contentType">Type of the content.</param> /// <param name="responseHeaders">The response headers.</param> /// <returns>System.Object.</returns> /// <exception cref="System.ArgumentNullException">cacheKey</exception> public object GetCachedResult <T>(IRequest requestContext, Guid cacheKey, DateTime?lastDateModified, TimeSpan?cacheDuration, Func <T> factoryFn, string contentType, IDictionary <string, string> responseHeaders = null) where T : class { if (cacheKey == Guid.Empty) { throw new ArgumentNullException("cacheKey"); } if (factoryFn == null) { throw new ArgumentNullException("factoryFn"); } var key = cacheKey.ToString("N"); if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } // See if the result is already cached in the browser var result = GetCachedResult(requestContext, responseHeaders, cacheKey, key, lastDateModified, cacheDuration, contentType); if (result != null) { return(result); } result = factoryFn(); // Apply caching headers var hasHeaders = result as IHasHeaders; if (hasHeaders != null) { AddResponseHeaders(hasHeaders, responseHeaders); return(hasHeaders); } return(GetHttpResult(result, contentType, false, responseHeaders)); }
/// <summary> /// Gets the HTTP result. /// </summary> private IHasHeaders GetHttpResult(IRequest requestContext, string content, string contentType, bool addCachePrevention, IDictionary <string, string> responseHeaders = null) { IHasHeaders result; var bytes = Encoding.UTF8.GetBytes(content); var compressionType = requestContext == null ? null : GetCompressionType(requestContext, bytes, contentType); var isHeadRequest = requestContext == null ? false : string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase); if (string.IsNullOrEmpty(compressionType)) { var contentLength = bytes.Length; if (isHeadRequest) { bytes = Array.Empty <byte>(); } result = new StreamWriter(bytes, contentType, contentLength); } else { result = GetCompressedResult(bytes, compressionType, responseHeaders, isHeadRequest, contentType); } if (responseHeaders == null) { responseHeaders = new Dictionary <string, string>(); } if (addCachePrevention && !responseHeaders.TryGetValue(HeaderNames.Expires, out string _)) { responseHeaders[HeaderNames.Expires] = "0"; } AddResponseHeaders(result, responseHeaders); return(result); }
private static string GetCompressionType(IRequest request) { var acceptEncoding = request.Headers[HeaderNames.AcceptEncoding].ToString(); if (string.IsNullOrEmpty(acceptEncoding)) { //if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1) // return "br"; if (acceptEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) != -1) { return("deflate"); } if (acceptEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1) { return("gzip"); } } return(null); }
/// <summary> /// Shoulds the compress response. /// </summary> /// <param name="requestContext">The request context.</param> /// <param name="contentType">Type of the content.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> private bool ShouldCompressResponse(IRequest requestContext, string contentType) { // It will take some work to support compression with byte range requests if (!string.IsNullOrEmpty(requestContext.Headers.Get("Range"))) { return(false); } // Don't compress media if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase)) { return(false); } // Don't compress images if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { return(false); } if (contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase)) { return(false); } if (contentType.StartsWith("application/", StringComparison.OrdinalIgnoreCase)) { if (string.Equals(contentType, "application/x-javascript", StringComparison.OrdinalIgnoreCase)) { return(true); } if (string.Equals(contentType, "application/xml", StringComparison.OrdinalIgnoreCase)) { return(true); } return(false); } return(true); }
/// <summary> /// Determines whether [is not modified] [the specified cache key]. /// </summary> /// <param name="requestContext">The request context.</param> /// <param name="cacheKey">The cache key.</param> /// <param name="lastDateModified">The last date modified.</param> /// <param name="cacheDuration">Duration of the cache.</param> /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns> private bool IsNotModified(IRequest requestContext, Guid?cacheKey, DateTime?lastDateModified, TimeSpan?cacheDuration) { //var isNotModified = true; var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since"); if (!string.IsNullOrEmpty(ifModifiedSinceHeader)) { DateTime ifModifiedSince; if (DateTime.TryParse(ifModifiedSinceHeader, out ifModifiedSince)) { if (IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified)) { return(true); } } } var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); // Validate If-None-Match if ((cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader))) { Guid ifNoneMatch; ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"'); if (Guid.TryParse(ifNoneMatchHeader, out ifNoneMatch)) { if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch) { return(true); } } } return(false); }