예제 #1
0
 /// <summary>
 /// Sets the cache-control header.
 /// </summary>
 /// <param name="bucketInfo">The bucket info dictionary.</param>
 /// <param name="value">The cache control values.</param>
 public static void SetCacheControl(this BucketInfo bucketInfo, CacheControlHeaderValue value)
 {
     if (value != null)
     {
         bucketInfo.Add("Cache-Control", value.ToString());
     }
 }
예제 #2
0
        public void ToString_UseRequestDirectiveValues_AllSerializedCorrectly()
        {
            CacheControlHeaderValue cacheControl = new CacheControlHeaderValue();

            Assert.Equal("", cacheControl.ToString());

            // Note that we allow all combinations of all properties even though the RFC specifies rules what value
            // can be used together.
            // Also for property pairs (bool property + collection property) like 'NoCache' and 'NoCacheHeaders' the
            // caller needs to set the bool property in order for the collection to be populated as string.

            // Cache Request Directive sample
            cacheControl.NoStore = true;
            Assert.Equal("no-store", cacheControl.ToString());
            cacheControl.NoCache = true;
            Assert.Equal("no-store, no-cache", cacheControl.ToString());
            cacheControl.MaxAge = new TimeSpan(0, 1, 10);
            Assert.Equal("no-store, no-cache, max-age=70", cacheControl.ToString());
            cacheControl.MaxStale = true;
            Assert.Equal("no-store, no-cache, max-age=70, max-stale", cacheControl.ToString());
            cacheControl.MaxStaleLimit = new TimeSpan(0, 2, 5);
            Assert.Equal("no-store, no-cache, max-age=70, max-stale=125", cacheControl.ToString());
            cacheControl.MinFresh = new TimeSpan(0, 3, 0);
            Assert.Equal("no-store, no-cache, max-age=70, max-stale=125, min-fresh=180", cacheControl.ToString());

            cacheControl             = new CacheControlHeaderValue();
            cacheControl.NoTransform = true;
            Assert.Equal("no-transform", cacheControl.ToString());
            cacheControl.OnlyIfCached = true;
            Assert.Equal("no-transform, only-if-cached", cacheControl.ToString());
            cacheControl.Extensions.Add(new NameValueHeaderValue("custom"));
            cacheControl.Extensions.Add(new NameValueHeaderValue("customName", "customValue"));
            Assert.Equal("no-transform, only-if-cached, custom, customName=customValue", cacheControl.ToString());

            cacheControl = new CacheControlHeaderValue();
            cacheControl.Extensions.Add(new NameValueHeaderValue("custom"));
            Assert.Equal("custom", cacheControl.ToString());
        }
예제 #3
0
        public void ToString_UseResponseDirectiveValues_AllSerializedCorrectly()
        {
            CacheControlHeaderValue cacheControl = new CacheControlHeaderValue();

            Assert.Equal("", cacheControl.ToString());

            cacheControl.NoCache = true;
            Assert.Equal("no-cache", cacheControl.ToString());
            cacheControl.NoCacheHeaders.Add("token1");
            Assert.Equal("no-cache=\"token1\"", cacheControl.ToString());
            cacheControl.Public = true;
            Assert.Equal("public, no-cache=\"token1\"", cacheControl.ToString());

            cacheControl         = new CacheControlHeaderValue();
            cacheControl.Private = true;
            Assert.Equal("private", cacheControl.ToString());
            cacheControl.PrivateHeaders.Add("token2");
            cacheControl.PrivateHeaders.Add("token3");
            Assert.Equal("private=\"token2, token3\"", cacheControl.ToString());
            cacheControl.MustRevalidate = true;
            Assert.Equal("must-revalidate, private=\"token2, token3\"", cacheControl.ToString());
            cacheControl.ProxyRevalidate = true;
            Assert.Equal("must-revalidate, proxy-revalidate, private=\"token2, token3\"", cacheControl.ToString());
        }
        public static IResponse EnableCaching(this IResponse response, TimeSpan?timeSpan = null)
        {
            return(new ResponseDecorator(response, ctx =>
            {
#if !DEBUG
                var cacheControl = new CacheControlHeaderValue
                {
                    Public = true,
                    MaxAge = timeSpan ?? TimeSpan.FromDays(150)
                };
#else
                var cacheControl = new CacheControlHeaderValue
                {
                    NoCache = true,
                };
#endif
                ctx.Response.Headers[HeaderNames.CacheControl] = cacheControl.ToString();
            }));
        }
예제 #5
0
        public void ToString_NegativeValues_UsesMinusSignRegardlessOfCurrentCulture()
        {
            RemoteExecutor.Invoke(() =>
            {
                var cacheControl = new CacheControlHeaderValue()
                {
                    MaxAge        = new TimeSpan(0, 0, -1),
                    MaxStale      = true,
                    MaxStaleLimit = new TimeSpan(0, 0, -2),
                    MinFresh      = new TimeSpan(0, 0, -3),
                    SharedMaxAge  = new TimeSpan(0, 0, -4)
                };

                var ci = (CultureInfo)CultureInfo.CurrentCulture.Clone();
                ci.NumberFormat.NegativeSign = "n";
                CultureInfo.CurrentCulture   = ci;

                Assert.Equal("max-age=-1, s-maxage=-4, max-stale=-2, min-fresh=-3", cacheControl.ToString());
            }).Dispose();
        }
예제 #6
0
        private static void NoCacheHeaders(ActionExecutedContext context)
        {
            var headerCache = new CacheControlHeaderValue()
            {
                NoCache = true
            };

            context.HttpContext.Response.Headers.Add("Cache-Control", headerCache.ToString());

            StringValues dateValue;

            context.HttpContext.Response.Headers.TryGetValue("Date", out dateValue);

            if (string.IsNullOrEmpty(dateValue))
            {
                context.HttpContext.Response.Headers.Add("Date", DateTimeOffset.UtcNow.ToString());
            }

            context.HttpContext.Response.Headers.Add("Expires", DateTimeOffset.UtcNow.ToString());
        }
        private static void InitializeAzureStorage(IServiceProvider services, AzureBlobStorageImageProviderOptions options)
        {
            // Upload an image to the Azure Test Storage;
            AzureBlobContainerClientOptions containerOptions = options.BlobContainers.First();
            var container = new BlobContainerClient(containerOptions.ConnectionString, containerOptions.ContainerName);

            container.CreateIfNotExists(PublicAccessType.Blob);

#if NETCOREAPP2_1
            IHostingEnvironment environment = services.GetRequiredService <IHostingEnvironment>();
#else
            IWebHostEnvironment environment = services.GetRequiredService <IWebHostEnvironment>();
#endif

            BlobClient blob = container.GetBlobClient(TestConstants.ImagePath);

            if (!blob.Exists())
            {
                IFileInfo file = environment.WebRootFileProvider.GetFileInfo(TestConstants.ImagePath);
                using Stream stream = file.CreateReadStream();

                // Set the max-age property so we get coverage for testing is in our Azure provider.
                var cacheControl = new CacheControlHeaderValue
                {
                    Public         = true,
                    MaxAge         = TimeSpan.FromDays(7),
                    MustRevalidate = true
                };

                var headers = new BlobHttpHeaders
                {
                    CacheControl = cacheControl.ToString(),
                };

                blob.Upload(stream, httpHeaders: headers);
            }
        }
예제 #8
0
        private void AddCacheHeaders(ActionExecutedContext context)
        {
            if (context.HttpContext.Response.StatusCode == 200)
            {
                var cacheControlHeaderValue = new CacheControlHeaderValue()
                {
                    NoCache = false,
                    Public  = (CacheType == ClientCacheControl.Public),
                    Private = (CacheType == ClientCacheControl.Private),
                    MaxAge  = TimeSpan.FromSeconds(CacheSeconds)
                };

                context.HttpContext.Response.Headers.Add("Cache-Control", cacheControlHeaderValue.ToString());
                StringValues dateValue;
                context.HttpContext.Response.Headers.TryGetValue("Date", out dateValue);
                if (string.IsNullOrEmpty(dateValue))
                {
                    context.HttpContext.Response.Headers.Add("Date", DateTimeOffset.UtcNow.ToString());
                    context.HttpContext.Response.Headers.TryGetValue("Date", out dateValue);
                }

                context.HttpContext.Response.Headers.Add("Expires", DateTimeOffset.UtcNow.AddSeconds(CacheSeconds).ToString());
            }
        }
예제 #9
0
        /// <summary>
        /// Adds caching for GET and PUT if
        /// cache control provided is not null
        /// With PUT, since cache has been alreay invalidated,
        /// we provide the new ETag (old one has been cleared in invalidation phase)
        /// </summary>
        /// <param name="entityTagKey"></param>
        /// <param name="request"></param>
        /// <param name="response"></param>
        /// <param name="varyHeaders"></param>
        /// <returns></returns>
        internal Action AddCaching(
            EntityTagKey entityTagKey,
            HttpRequestMessage request,
            HttpResponseMessage response,
            IEnumerable <KeyValuePair <string, IEnumerable <string> > > varyHeaders)
        {
            return
                (() =>
            {
                CacheControlHeaderValue cacheControlHeaderValue = CacheControlHeaderProvider(request);
                if (cacheControlHeaderValue == null)
                {
                    return;
                }

                TimedEntityTagHeaderValue eTagValue;

                string uri = UriTrimmer(request.RequestUri);

                // in case of GET and no ETag
                // in case of PUT, we should return the new ETag of the resource
                // NOTE: No need to check if it is in the cache. If it were, it would not get
                // here
                if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Put)
                {
                    // create new ETag only if it does not already exist
                    if (!_entityTagStore.TryGetValue(entityTagKey, out eTagValue))
                    {
                        eTagValue = new TimedEntityTagHeaderValue(ETagValueGenerator(uri, varyHeaders));
                        _entityTagStore.AddOrUpdate(entityTagKey, eTagValue);
                    }

                    // set ETag
                    response.Headers.ETag = eTagValue.ToEntityTagHeaderValue();

                    // set last-modified
                    if (AddLastModifiedHeader && response.Content != null &&
                        !response.Content.Headers.Any(x => x.Key.Equals(HttpHeaderNames.LastModified,
                                                                        StringComparison.CurrentCultureIgnoreCase)))
                    {
                        response.Content.Headers.Add(HttpHeaderNames.LastModified, eTagValue.LastModified.ToString("r"));
                    }

                    // set Vary
                    if (AddVaryHeader && _varyByHeaders != null && _varyByHeaders.Length > 0)
                    {
                        response.Headers.Add(HttpHeaderNames.Vary, _varyByHeaders);
                    }

                    response.Headers.TryAddWithoutValidation(HttpHeaderNames.CacheControl, cacheControlHeaderValue.ToString());
                }
            });
        }
예제 #10
0
        public void SetCacheControlHeaders(HttpCacheability?httpCacheability = null,
                                           DateTime?lastModified             = null, TimeSpan?maxAge = null)
        {
            // Cache control headers are stored temporarily in an aggregated object
            // and written to the single response header. The reason behind this is
            // that this method may be called multiple times with different kinds
            // of parameters.

            var writeCacheControlHeader = false;

            try
            {
                if (httpCacheability.HasValue)
                {
                    switch (httpCacheability)
                    {
                    case HttpCacheability.NoCache:
                        _cacheHeaders.NoCache         = true;
                        _cacheHeaders.NoStore         = true;
                        _cacheHeaders.ProxyRevalidate = true;
                        _cacheHeaders.MustRevalidate  = true;
                        break;

                    case HttpCacheability.Private:
                        _cacheHeaders.Private = true;
                        break;

                    case HttpCacheability.Public:
                        _cacheHeaders.Public = true;
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(httpCacheability), httpCacheability, null);
                    }

                    writeCacheControlHeader = true;
                }

                if (lastModified.HasValue)
                {
                    // make sure that the date is in the past
                    var t = lastModified.Value;
                    if (t > DateTime.UtcNow)
                    {
                        t = DateTime.UtcNow;
                    }

                    _context.Response.Headers[HeaderNames.LastModified] = t.ToUniversalTime().ToString("r");
                }

                if (maxAge.HasValue)
                {
                    _cacheHeaders.MaxAge    = maxAge.Value;
                    writeCacheControlHeader = true;
                }

                if (writeCacheControlHeader)
                {
                    _context.Response.Headers[HeaderNames.CacheControl] = _cacheHeaders.ToString();
                }
            }
            catch (Exception ex)
            {
                SnLog.WriteException(ex,
                                     "Exception in SetCacheControlHeaders. " +
                                     $"httpCacheability:'{httpCacheability}' lastModified:'{lastModified}' maxAge:'{maxAge}'",
                                     EventId.Portal);
            }
        }
예제 #11
0
        /// <summary>
        /// Gets a FilePathResult based on the file's path. It sets the mime type based on the file's extension.
        /// </summary>
        /// <param name="downloadFileName">If specified, the browser will not try to process the file directly (such as PDF files) and instead always opens the file download dialogue.</param>
        protected async Task <ActionResult> File(Blob file,
                                                 string downloadFileName = null, CacheControlHeaderValue cacheControl = null, bool showInline = false)
        {
            if (cacheControl != null)
            {
                Response.Headers.Remove(Microsoft.Net.Http.Headers.HeaderNames.Pragma);
                Response.Headers.Remove(Microsoft.Net.Http.Headers.HeaderNames.CacheControl);
                Response.Headers.Add(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, cacheControl.ToString());
            }

            var cd = new System.Net.Mime.ContentDisposition
            {
                FileName = downloadFileName.Or(file.FileName),
                Inline   = showInline,
            };

            if (showInline)
            {
                Response.Headers.Add("X-Content-Type-Options", "nosniff");
            }

            Response.Headers.Remove("Content-Disposition");
            Response.Headers.Add(Microsoft.Net.Http.Headers.HeaderNames.ContentDisposition, cd.ToString());

            return(File(await file.GetFileDataAsync(), file.GetMimeType()));
        }
예제 #12
0
        public static async Task <ActionResult> GenerateActionResult(ControllerBase controller, ICacheableDataProvider provider, TimeSpan?maxAge = null)
        {
            const string CacheControlHeaderKey    = "Cache-Control";
            const string IfNonMatchHeaderKey      = "If-None-Match";
            const string IfModifiedSinceHeaderKey = "If-Modified-Since";
            const string ETagHeaderKey            = "ETag";
            const string LastModifiedHeaderKey    = "Last-Modified";

            string GenerateCacheControlHeaderValue()
            {
                var cacheControlHeader = new CacheControlHeaderValue()
                {
                    NoCache        = true,
                    NoStore        = false,
                    MaxAge         = maxAge ?? TimeSpan.FromDays(14),
                    Private        = true,
                    MustRevalidate = true
                };

                return(cacheControlHeader.ToString());
            }

            var digest = await provider.GetDigest();

            var eTagValue = $"\"{digest.ETag}\"";
            var eTag      = new EntityTagHeaderValue(eTagValue);

            ActionResult Generate304Result()
            {
                controller.Response.Headers.Add(ETagHeaderKey, eTagValue);
                controller.Response.Headers.Add(LastModifiedHeaderKey, digest.LastModified.ToString("R"));
                controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
                return(controller.StatusCode(StatusCodes.Status304NotModified, null));
            }

            if (controller.Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var ifNonMatchHeaderValue))
            {
                if (!EntityTagHeaderValue.TryParseList(ifNonMatchHeaderValue, out var eTagList))
                {
                    return(controller.BadRequest(new CommonResponse(ErrorCodes.Common.Header.IfNonMatch_BadFormat, "Header If-None-Match is of bad format.")));
                }

                if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
                {
                    return(Generate304Result());
                }
            }
            else if (controller.Request.Headers.TryGetValue(IfModifiedSinceHeaderKey, out var ifModifiedSinceHeaderValue))
            {
                if (!DateTime.TryParse(ifModifiedSinceHeaderValue, out var headerValue))
                {
                    return(controller.BadRequest(new CommonResponse(ErrorCodes.Common.Header.IfModifiedSince_BadFormat, "Header If-Modified-Since is of bad format.")));
                }

                if (headerValue > digest.LastModified)
                {
                    return(Generate304Result());
                }
            }

            var data = await provider.GetData();

            controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
            return(controller.File(data.Data, data.ContentType, digest.LastModified, eTag));
        }
 public static string ToSafeString(this CacheControlHeaderValue cacheControl)
 {
     return(cacheControl == null ? "(null)" : cacheControl.ToString());
 }
예제 #14
0
        /// <summary>
        /// Gets a FilePathResult based on the file's path. It sets the mime type based on the file's extension.
        /// </summary>
        /// <param name="downloadFileName">If specified, the browser will not try to process the file directly (such as PDF files) and instead always opens the file download dialogue.</param>
        protected async Task <ActionResult> File(Blob file,
                                                 string downloadFileName = null, CacheControlHeaderValue cacheControl = null)
        {
            if (cacheControl != null)
            {
                Response.Headers.Remove(Microsoft.Net.Http.Headers.HeaderNames.Pragma);
                Response.Headers.Remove(Microsoft.Net.Http.Headers.HeaderNames.CacheControl);
                Response.Headers.Add(Microsoft.Net.Http.Headers.HeaderNames.CacheControl, cacheControl.ToString());
            }

            return(File(await file.GetFileDataAsync(), file.GetMimeType(), downloadFileName.Or(file.FileName)));
        }
예제 #15
0
        private static async Task InitializeAWSStorageAsync(IServiceProvider services, AWSS3StorageImageProviderOptions options)
        {
            // Upload an image to the AWS Test Storage;
            AWSS3BucketClientOptions bucketOptions       = options.S3Buckets.First();
            AmazonS3Client           amazonS3Client      = AmazonS3ClientFactory.CreateClient(bucketOptions);
            ListBucketsResponse      listBucketsResponse = await amazonS3Client.ListBucketsAsync();

            bool foundBucket = false;

            foreach (S3Bucket b in listBucketsResponse.Buckets)
            {
                if (b.BucketName == bucketOptions.BucketName)
                {
                    foundBucket = true;
                    break;
                }
            }

            if (!foundBucket)
            {
                try
                {
                    var putBucketRequest = new PutBucketRequest
                    {
                        BucketName   = bucketOptions.BucketName,
                        BucketRegion = bucketOptions.Region,
                        CannedACL    = S3CannedACL.PublicRead
                    };

                    await amazonS3Client.PutBucketAsync(putBucketRequest);
                }
                catch (AmazonS3Exception e)
                {
                    // CI tests are run in parallel and can sometimes return a
                    // false negative for the existance of a bucket.
                    if (string.Equals(e.ErrorCode, "BucketAlreadyExists"))
                    {
                        return;
                    }

                    throw;
                }
            }

#if NETCOREAPP2_1
            IHostingEnvironment environment = services.GetRequiredService <IHostingEnvironment>();
#else
            IWebHostEnvironment environment = services.GetRequiredService <IWebHostEnvironment>();
#endif

            try
            {
                GetObjectRequest request = new()
                {
                    BucketName = bucketOptions.BucketName,
                    Key        = TestConstants.ImagePath
                };

                await amazonS3Client.GetObjectAsync(request);
            }
            catch
            {
                IFileInfo file = environment.WebRootFileProvider.GetFileInfo(TestConstants.ImagePath);
                using Stream stream = file.CreateReadStream();

                // Set the max-age property so we get coverage for testing in our AWS provider.
                var cacheControl = new CacheControlHeaderValue
                {
                    Public         = true,
                    MaxAge         = TimeSpan.FromDays(7),
                    MustRevalidate = true
                };

                var putRequest = new PutObjectRequest()
                {
                    BucketName = bucketOptions.BucketName,
                    Key        = TestConstants.ImagePath,
                    Headers    =
                    {
                        CacheControl = cacheControl.ToString()
                    },
                    ContentType = "	image/png",
                    InputStream = stream
                };

                await amazonS3Client.PutObjectAsync(putRequest);
            }
        }