Skip to content

Commit

Permalink
Fixing (hopefully) CDN/DXC-S support on Azure
Browse files Browse the repository at this point in the history
  • Loading branch information
vnbaaij committed Oct 10, 2018
1 parent 1bbd6a5 commit 9c722fb
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 64 deletions.
7 changes: 3 additions & 4 deletions samples/AlloySampleAzure/config/imageprocessor/cache.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
<caches>
<cache name="AzureBlobCacheEpi" type="ImageProcessor.Web.Episerver.Azure.AzureBlobCache, ImageProcessor.Web.Episerver.Azure" maxDays="1">
<settings>
<!--<setting key="UseCachedContainerInUrl" value="false" />
<setting key="StreamCachedImage" value="false" />-->
<!--<setting key="CachedCDNRoot" value="[CdnRootUrl]"/>
<setting key="CachedCDNTimeout" value="1000"/>-->
<setting key="StreamCachedImage" value="true" />
<!--<setting key="CachedCDNRoot" value="[CdnRootUrl]"/>-->
<setting key="CDNTimeout" value="1000"/>
</settings>
</cache>
</caches>
Expand Down
83 changes: 33 additions & 50 deletions src/ImageProcessor.Web.Episerver.Azure/AzureBlobCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
using EPiServer.Logging;
using EPiServer.Web.Routing;

using ImageProcessor.Web.Episerver;
using ImageProcessor.Web.Caching;
using ImageProcessor.Web.HttpModules;
//using ImageProcessor.Web.HttpModules;

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
Expand All @@ -30,7 +31,7 @@ public class AzureBlobCache : ImageCacheBase
/// <summary>
/// The assembly version.
/// </summary>
private static readonly string AssemblyVersion = typeof(ImageProcessingModule).Assembly.GetName().Version.ToString();
private static readonly string AssemblyVersion = typeof(EpiserverImageProcessingModule).Assembly.GetName().Version.ToString();

/// <summary>
/// Use the configured logging mechanism
Expand All @@ -42,11 +43,6 @@ public class AzureBlobCache : ImageCacheBase
/// </summary>
private static CloudBlobContainer rootContainer;

/// <summary>
/// The cached root url for a content delivery network.
/// </summary>
private readonly string cachedCdnRoot;

/// <summary>
/// Determines if the CDN request is redirected or rewritten
/// </summary>
Expand All @@ -57,18 +53,8 @@ public class AzureBlobCache : ImageCacheBase
/// </summary>
private readonly int timeout = 1000;

/// <summary>
/// The cached rewrite path.
/// </summary>
private string cachedRewritePath;

public string blobPath;

/// <summary>
/// The provider name
/// </summary>
private static string providerName;

private const string prefix = "3p!_";

/// <summary>
Expand All @@ -87,7 +73,7 @@ public AzureBlobCache(string requestPath, string fullPath, string querystring)
: base(requestPath, fullPath, querystring)
{
// Get the name of the configured blob provider
providerName = EPiServerFrameworkSection.Instance.Blob.DefaultProvider;
string providerName = EPiServerFrameworkSection.Instance.Blob.DefaultProvider;


if (rootContainer == null)
Expand All @@ -105,16 +91,19 @@ public AzureBlobCache(string requestPath, string fullPath, string querystring)
rootContainer = cloudBlobClient.GetContainerReference(EPiServerFrameworkSection.Instance.Blob.Providers[providerName].Parameters["container"]);
}

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

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

// 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
streamCachedImage = Settings.ContainsKey("StreamCachedImage") && Settings["StreamCachedImage"].ToLower() == "true";
}

Expand All @@ -140,10 +129,6 @@ public override async Task<bool> IsNewOrUpdatedAsync()

blobPath = $"{containerName}/{cachedFilename}";

bool useCachedContainerInUrl = this.Settings.ContainsKey("UseCachedContainerInUrl") && this.Settings["UseCachedContainerInUrl"].ToLower() != "false";

cachedRewritePath = (useCachedContainerInUrl ? Path.Combine(cachedCdnRoot, containerName) : cachedCdnRoot) + FullPath;

CachedPath = $"{rootContainer.Uri.ToString()}/{containerName}/{cachedFilename}";

bool isUpdated = false;
Expand All @@ -152,7 +137,7 @@ public override async Task<bool> IsNewOrUpdatedAsync()
if (cachedImage == null)
{
CloudBlockBlob blockBlob = rootContainer.GetBlockBlobReference(blobPath);
string t = GetSaSForBlob(blockBlob, SharedAccessBlobPermissions.Read);
//string t = GetSaSForBlob(blockBlob, SharedAccessBlobPermissions.Read);

if (await blockBlob.ExistsAsync())
{
Expand All @@ -168,7 +153,7 @@ public override async Task<bool> IsNewOrUpdatedAsync()
CreationTimeUtc = blockBlob.Properties.LastModified.Value.UtcDateTime
};

CacheIndexer.Add(cachedImage);
CacheIndexer.Add(cachedImage, ImageCacheMaxMinutes);
}
}
}
Expand Down Expand Up @@ -328,11 +313,9 @@ private async Task<bool> IsUpdatedAsync(DateTime creationDate)
public override void RewritePath(HttpContext context)
{

//CloudBlockBlob blockBlob = rootContainer.GetBlockBlobReference(blobPath);
//string p = GetSaSForBlob(blockBlob, SharedAccessBlobPermissions.Read);
//HttpWebRequest request = (HttpWebRequest)WebRequest.Create(p);

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.cachedRewritePath);
CloudBlockBlob blockBlob = rootContainer.GetBlockBlobReference(blobPath);
string blobWithSAS = GetSaSForBlob(blockBlob, SharedAccessBlobPermissions.Read);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(blobWithSAS);

if (streamCachedImage)
{
Expand Down Expand Up @@ -366,7 +349,7 @@ public override void RewritePath(HttpContext context)
// It appears that some CDN's on Azure (Akamai) do not work properly when making head requests.
// They will return a response url and other headers but a 500 status code.
if (ex.Response != null && (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotModified
|| ex.Response.ResponseUri.AbsoluteUri.Equals(cachedRewritePath, StringComparison.OrdinalIgnoreCase)))
|| ex.Response.ResponseUri.AbsoluteUri.Equals(CachedPath, StringComparison.OrdinalIgnoreCase)))
{
response = (HttpWebResponse)ex.Response;
}
Expand Down Expand Up @@ -401,7 +384,7 @@ public override void RewritePath(HttpContext context)
}

cachedStream.CopyTo(contextResponse.OutputStream); // Will be empty on 304s
ImageProcessingModule.SetHeaders(
EpiserverImageProcessingModule.SetHeaders(
context,
response.StatusCode == HttpStatusCode.NotModified ? null : response.ContentType,
null,
Expand All @@ -415,12 +398,12 @@ public override void RewritePath(HttpContext context)
else
{
// Prevent redundant metadata request if paths match.
if (this.CachedPath == this.cachedRewritePath)
{
ImageProcessingModule.AddCorsRequestHeaders(context);
context.Response.Redirect(this.CachedPath, false);
return;
}
//if (this.CachedPath == this.cachedRewritePath)
//{
// ImageProcessingModule.AddCorsRequestHeaders(context);
// context.Response.Redirect(this.CachedPath, false);
// return;
//}


// Redirect the request to the blob URL
Expand All @@ -432,8 +415,8 @@ public override void RewritePath(HttpContext context)
{
response = (HttpWebResponse)request.GetResponse();
response.Dispose();
ImageProcessingModule.AddCorsRequestHeaders(context);
context.Response.Redirect(cachedRewritePath, false);
EpiserverImageProcessingModule.AddCorsRequestHeaders(context);
context.Response.Redirect(blobWithSAS, false);
}
catch (WebException ex)
{
Expand All @@ -446,11 +429,11 @@ public override void RewritePath(HttpContext context)
// A 304 (NotModified) is not an error
// It appears that some CDN's on Azure (Akamai) do not work properly when making head requests.
// They will return a response url and other headers but a 500 status code.
if (responseCode == HttpStatusCode.NotModified || response.ResponseUri.AbsoluteUri.Equals(cachedRewritePath, StringComparison.OrdinalIgnoreCase))
if (responseCode == HttpStatusCode.NotModified || response.ResponseUri.AbsoluteUri.Equals(CachedPath, StringComparison.OrdinalIgnoreCase))
{
response.Dispose();
ImageProcessingModule.AddCorsRequestHeaders(context);
context.Response.Redirect(cachedRewritePath, false);
EpiserverImageProcessingModule.AddCorsRequestHeaders(context);
context.Response.Redirect(CachedPath, false);
}
else
{
Expand All @@ -461,7 +444,7 @@ public override void RewritePath(HttpContext context)
else
{
// It's a 404, we should redirect to the cached path we have just saved to.
ImageProcessingModule.AddCorsRequestHeaders(context);
EpiserverImageProcessingModule.AddCorsRequestHeaders(context);
context.Response.Redirect(CachedPath, false);
}
}
Expand Down
45 changes: 39 additions & 6 deletions src/ImageProcessor.Web.Episerver.Azure/AzureBlobCacheAlt.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
Expand Down Expand Up @@ -131,8 +132,7 @@ public AzureBlobCacheAlt(string requestPath, string fullPath, string querystring

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

Expand Down Expand Up @@ -169,7 +169,7 @@ public override async Task<bool> IsNewOrUpdatedAsync()

CachedPath = $"{cloudCachedBlobContainer.Uri.ToString()}/{containerName}/{cachedFileName}";

cachedRewritePath = (useCachedContainerInUrl ? Path.Combine(cachedCdnRoot, containerName) : cachedCdnRoot) + RequestPath; // + FullPath;
cachedRewritePath = (useCachedContainerInUrl ? Path.Combine(cachedCdnRoot, cloudCachedBlobContainer.Name, "/") : cachedCdnRoot) + blobPath;//RequestPath; // + FullPath;

bool isUpdated = false;
CachedImage cachedImage = CacheIndexer.Get(CachedPath);
Expand All @@ -193,6 +193,7 @@ public override async Task<bool> IsNewOrUpdatedAsync()
{
string blobPath = CachedPath.Substring(cloudCachedBlobContainer.Uri.ToString().Length + 1);
CloudBlockBlob blockBlob = cloudCachedBlobContainer.GetBlockBlobReference(blobPath);
cachedRewritePath = GetSaSForBlob(blockBlob, SharedAccessBlobPermissions.Read);

if (await blockBlob.ExistsAsync())
{
Expand Down Expand Up @@ -525,11 +526,9 @@ protected virtual async Task<bool> IsUpdatedAsync(DateTime creationDate)
/// <param name="request">The current request</param>
private static void TrySetIfModifiedSinceDate(HttpContext context, HttpWebRequest request)
{
DateTime ifModifiedDate;

string ifModifiedFromRequest = context.Request.Headers["If-Modified-Since"];

if (DateTime.TryParse(ifModifiedFromRequest, out ifModifiedDate))
if (DateTime.TryParse(ifModifiedFromRequest, out DateTime ifModifiedDate))
{
request.IfModifiedSince = ifModifiedDate;
}
Expand Down Expand Up @@ -570,5 +569,39 @@ private static void TrySetIfModifiedSinceDate(HttpContext context, HttpWebReques

// return container;
//}

/// <summary>
/// Creates a SAS URI for the blob container.
/// </summary>
/// <param name="blobContainer"></param>
/// <param name="permission"></param>
/// <returns></returns>
static string GetSaSForBlobContainer(CloudBlobContainer blobContainer, SharedAccessBlobPermissions permission)
{
var sas = blobContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = permission,
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),//SAS Start time is back by 5 minutes to take clock skewness into consideration
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(15),
});
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", blobContainer.Uri, sas);
}

/// <summary>
/// Creates a SAS URI for the blob.
/// </summary>
/// <param name="blob"></param>
/// <param name="permission"></param>
/// <returns></returns>
static string GetSaSForBlob(CloudBlockBlob blob, SharedAccessBlobPermissions permission)
{
var sas = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()
{
Permissions = permission,
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(15),
});
return string.Format(CultureInfo.InvariantCulture, "{0}{1}", blob.Uri, sas);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AzureBlobCache.cs" />
<Compile Include="AzureBlobCacheAlt.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -200,5 +199,11 @@
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ImageProcessor.Web.Episerver\ImageProcessor.Web.Episerver.csproj">
<Project>{2edae29a-d622-4b01-8853-d26f4d1c0fd8}</Project>
<Name>ImageProcessor.Web.Episerver</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.0.0.0")]
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyVersion("4.2.0.0")]
[assembly: AssemblyFileVersion("4.2.0.0")]
Loading

0 comments on commit 9c722fb

Please sign in to comment.