private static async Task TestAccessAsync(BlobContainerPublicAccessType accessType, CloudBlobContainer container, CloudBlob inputBlob)
        {
            StorageCredentials credentials = new StorageCredentials();

            container = new CloudBlobContainer(container.Uri, credentials);
            CloudPageBlob      blob    = new CloudPageBlob(inputBlob.Uri, credentials);
            OperationContext   context = new OperationContext();
            BlobRequestOptions options = new BlobRequestOptions();


            if (accessType.Equals(BlobContainerPublicAccessType.Container))
            {
                await blob.FetchAttributesAsync();

                await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, null, options, context);

                await container.FetchAttributesAsync();
            }
            else if (accessType.Equals(BlobContainerPublicAccessType.Blob))
            {
                await blob.FetchAttributesAsync();

                await TestHelper.ExpectedExceptionAsync(
                    async() => await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, null, options, context),
                    context,
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);

                await TestHelper.ExpectedExceptionAsync(
                    async() => await container.FetchAttributesAsync(null, options, context),
                    context,
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
            else
            {
                await TestHelper.ExpectedExceptionAsync(
                    async() => await blob.FetchAttributesAsync(null, options, context),
                    context,
                    "Fetch blob attributes while public access does not allow",
                    HttpStatusCode.NotFound);

                await TestHelper.ExpectedExceptionAsync(
                    async() => await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, null, options, context),
                    context,
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);

                await TestHelper.ExpectedExceptionAsync(
                    async() => await container.FetchAttributesAsync(null, options, context),
                    context,
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
        }
        private static async Task TestAccessAsync(BlobContainerPublicAccessType accessType, CloudBlobContainer container, CloudBlob inputBlob)
        {
            StorageCredentials credentials = new StorageCredentials();
            container = new CloudBlobContainer(container.Uri, credentials);
            CloudPageBlob blob = new CloudPageBlob(inputBlob.Uri, credentials);
            OperationContext context = new OperationContext();
            BlobRequestOptions options = new BlobRequestOptions();

            if (accessType.Equals(BlobContainerPublicAccessType.Container))
            {
                await blob.FetchAttributesAsync();
                await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, null, options, context);
                await container.FetchAttributesAsync();
            }
            else if (accessType.Equals(BlobContainerPublicAccessType.Blob))
            {
                await blob.FetchAttributesAsync();
                await TestHelper.ExpectedExceptionAsync(
                    async () => await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, null, options, context),
                    context,
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);
                await TestHelper.ExpectedExceptionAsync(
                    async () => await container.FetchAttributesAsync(null, options, context),
                    context,
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
            else
            {
                await TestHelper.ExpectedExceptionAsync(
                    async () => await blob.FetchAttributesAsync(null, options, context),
                    context,
                    "Fetch blob attributes while public access does not allow",
                    HttpStatusCode.NotFound);
                await TestHelper.ExpectedExceptionAsync(
                    async () => await container.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, null, null, options, context),
                    context,
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);
                await TestHelper.ExpectedExceptionAsync(
                    async () => await container.FetchAttributesAsync(null, options, context),
                    context,
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
        }
        private static void TestAccess(BlobContainerPublicAccessType accessType, CloudBlobContainer container, CloudBlob inputBlob)
        {
            StorageCredentials credentials = new StorageCredentials();
            container = new CloudBlobContainer(container.Uri, credentials);
            CloudPageBlob blob = new CloudPageBlob(inputBlob.Uri, credentials);

            if (accessType.Equals(BlobContainerPublicAccessType.Container))
            {
                blob.FetchAttributes();
                container.ListBlobs().ToArray();
                container.FetchAttributes();
            }
            else if (accessType.Equals(BlobContainerPublicAccessType.Blob))
            {
                blob.FetchAttributes();
                TestHelper.ExpectedException(
                    () => container.ListBlobs().ToArray(),
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);
                TestHelper.ExpectedException(
                    () => container.FetchAttributes(),
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
            else
            {
                TestHelper.ExpectedException(
                    () => blob.FetchAttributes(),
                    "Fetch blob attributes while public access does not allow",
                    HttpStatusCode.NotFound);
                TestHelper.ExpectedException(
                    () => container.ListBlobs().ToArray(),
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);
                TestHelper.ExpectedException(
                    () => container.FetchAttributes(),
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
        }
        private static void TestAccessTask(BlobContainerPublicAccessType accessType, CloudBlobContainer container, CloudBlob inputBlob)
        {
            StorageCredentials credentials = new StorageCredentials();
            container = new CloudBlobContainer(container.Uri, credentials);
            CloudPageBlob blob = new CloudPageBlob(inputBlob.Uri, credentials);

            if (accessType.Equals(BlobContainerPublicAccessType.Container))
            {
                blob.FetchAttributesAsync().Wait();
                BlobContinuationToken token = null;
                do
                {
                    BlobResultSegment results = container.ListBlobsSegmented(token);
                    results.Results.ToArray();
                    token = results.ContinuationToken;
                }
                while (token != null);
                container.FetchAttributesAsync().Wait();
            }
            else if (accessType.Equals(BlobContainerPublicAccessType.Blob))
            {
                blob.FetchAttributesAsync().Wait();

                TestHelper.ExpectedExceptionTask(
                    container.ListBlobsSegmentedAsync(null),
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);
                TestHelper.ExpectedExceptionTask(
                    container.FetchAttributesAsync(),
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
            else
            {
                TestHelper.ExpectedExceptionTask(
                    blob.FetchAttributesAsync(),
                    "Fetch blob attributes while public access does not allow",
                    HttpStatusCode.NotFound);
                TestHelper.ExpectedExceptionTask(
                    container.ListBlobsSegmentedAsync(null),
                    "List blobs while public access does not allow for listing",
                    HttpStatusCode.NotFound);
                TestHelper.ExpectedExceptionTask(
                    container.FetchAttributesAsync(),
                    "Fetch container attributes while public access does not allow",
                    HttpStatusCode.NotFound);
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="AzureBlobCache"/> class.
        /// </summary>
        /// <param name="requestPath">
        /// The request path for the image.
        /// </param>
        /// <param name="fullPath">
        /// The full path for the image.
        /// </param>
        /// <param name="querystring">
        /// The query string containing instructions.
        /// </param>
        public AzureBlobCache(string requestPath, string fullPath, string querystring)
            : base(requestPath, fullPath, querystring)
        {
            if (!(cloudCachedBlobContainer is null))
            {
                return;
            }

            lock (SyncLock)
            {
                if (!(cloudCachedBlobContainer is null))
                {
                    return;
                }

                // Retrieve storage accounts from connection string.
                var cloudCachedStorageAccount = CloudStorageAccount.Parse(this.Settings["CachedStorageAccount"]);

                // Create the blob clients.
                cloudCachedBlobClient = cloudCachedStorageAccount.CreateCloudBlobClient();

                // Set cloud cache container.
                BlobContainerPublicAccessType accessType = this.Settings.ContainsKey("AccessType")
                ? (BlobContainerPublicAccessType)Enum.Parse(typeof(BlobContainerPublicAccessType), this.Settings["AccessType"])
                : BlobContainerPublicAccessType.Blob;

                cloudCachedBlobContainer = CreateContainer(cloudCachedBlobClient, this.Settings["CachedBlobContainer"], accessType);

                // Set cloud source container.
                string sourceAccount = this.Settings.ContainsKey("SourceStorageAccount")
                                           ? this.Settings["SourceStorageAccount"]
                                           : string.Empty;

                // Repeat for source if it exists
                if (!string.IsNullOrWhiteSpace(sourceAccount))
                {
                    var             cloudSourceStorageAccount = CloudStorageAccount.Parse(this.Settings["SourceStorageAccount"]);
                    CloudBlobClient cloudSourceBlobClient     = cloudSourceStorageAccount.CreateCloudBlobClient();
                    cloudSourceBlobContainer = cloudSourceBlobClient.GetContainerReference(this.Settings["SourceBlobContainer"]);
                }

                // This setting was added to facilitate streaming of the blob resource directly instead of a redirect. This is beneficial for CDN purposes
                // but caution should be taken if not used with a CDN as it will add quite a bit of overhead to the site.
                // See: https://github.com/JimBobSquarePants/ImageProcessor/issues/161
                // If it's private, we also stream the response rather than redirect.
                streamCachedImage = accessType.Equals(BlobContainerPublicAccessType.Off) || (this.Settings.ContainsKey("StreamCachedImage") && string.Equals(this.Settings["StreamCachedImage"], "true", StringComparison.OrdinalIgnoreCase));

                cachedCdnRoot = this.Settings.ContainsKey("CachedCDNRoot")
                     ? this.Settings["CachedCDNRoot"]
                     : cloudCachedBlobContainer.Uri.ToString().TrimEnd(cloudCachedBlobContainer.Name.ToCharArray());

                if (this.Settings.ContainsKey("CachedCDNTimeout"))
                {
                    int.TryParse(this.Settings["CachedCDNTimeout"], out int t);
                    timeout = t;
                }

                // Do we insert the cache container? This seems to break some setups.
                useCachedContainerInUrl = this.Settings.ContainsKey("UseCachedContainerInUrl") &&
                                          !string.Equals(this.Settings["UseCachedContainerInUrl"], "false", StringComparison.OrdinalIgnoreCase);
            }
        }