diff --git a/src/Our.Umbraco.AzureCDNToolkit/AzureCdnToolkit.cs b/src/Our.Umbraco.AzureCDNToolkit/AzureCdnToolkit.cs index 9cdf34c..f90a23e 100644 --- a/src/Our.Umbraco.AzureCDNToolkit/AzureCdnToolkit.cs +++ b/src/Our.Umbraco.AzureCDNToolkit/AzureCdnToolkit.cs @@ -60,6 +60,13 @@ public static AzureCdnToolkit Instance /// public string MediaContainer { get; set; } + /// + /// The timeout in milliseconds used when connecting to the cdn + /// The default value for the is 100 seconds + /// so this is specified as the default value here + /// + public int CdnConnectionTimeout { get; set; } = 100 * 1000; + /// /// Sets all properties /// @@ -68,7 +75,6 @@ public static AzureCdnToolkit Instance public void Refresh() { - if ((WebConfigurationManager.AppSettings["AzureCDNToolkit:UseAzureCdnToolkit"] != null)) { var useAzureCdnToolkit = bool.Parse(WebConfigurationManager.AppSettings["AzureCDNToolkit:UseAzureCdnToolkit"]); @@ -84,6 +90,15 @@ public void Refresh() this.CdnUrl = WebConfigurationManager.AppSettings["AzureCDNToolkit:CdnUrl"]; this.AssetsContainer = WebConfigurationManager.AppSettings["AzureCDNToolkit:AssetsContainer"] ?? "assets"; this.MediaContainer = WebConfigurationManager.AppSettings["AzureCDNToolkit:MediaContainer"] ?? "media"; + + if (!string.IsNullOrWhiteSpace(WebConfigurationManager.AppSettings["AzureCDNToolkit:CdnConnectionTimeout"])) + { + int cdnConnectionTimeout = 0; + if (int.TryParse(WebConfigurationManager.AppSettings["AzureCDNToolkit:CdnConnectionTimeout"], out cdnConnectionTimeout)) + { + CdnConnectionTimeout = cdnConnectionTimeout; + } + } } } diff --git a/src/Our.Umbraco.AzureCDNToolkit/Events/UmbracoEvents.cs b/src/Our.Umbraco.AzureCDNToolkit/Events/UmbracoEvents.cs index cebca26..c16e94d 100644 --- a/src/Our.Umbraco.AzureCDNToolkit/Events/UmbracoEvents.cs +++ b/src/Our.Umbraco.AzureCDNToolkit/Events/UmbracoEvents.cs @@ -25,7 +25,12 @@ protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplica private void ImageProcessingModule_ValidatingRequest(object sender, ImageProcessor.Web.Helpers.ValidatingRequestEventArgs args) { var securityToken = WebConfigurationManager.AppSettings["AzureCDNToolkit:SecurityToken"]; - var securityModeEnabled = bool.Parse(WebConfigurationManager.AppSettings["AzureCDNToolkit:SecurityModeEnabled"]); + var securityModeEnabled = false; + + if (!bool.TryParse(WebConfigurationManager.AppSettings["AzureCDNToolkit:SecurityModeEnabled"], out securityModeEnabled)) + { + securityModeEnabled = false; + } if (securityModeEnabled && !string.IsNullOrWhiteSpace(args.QueryString) && !string.IsNullOrEmpty(securityToken)) { diff --git a/src/Our.Umbraco.AzureCDNToolkit/UrlHelperRenderExtensions.cs b/src/Our.Umbraco.AzureCDNToolkit/UrlHelperRenderExtensions.cs index c387f0a..36f26d4 100644 --- a/src/Our.Umbraco.AzureCDNToolkit/UrlHelperRenderExtensions.cs +++ b/src/Our.Umbraco.AzureCDNToolkit/UrlHelperRenderExtensions.cs @@ -82,7 +82,7 @@ public static IHtmlString GetCropCdnUrl(this UrlHelper urlHelper, public static IHtmlString ResolveCdn(this UrlHelper urlHelper, string path, bool asset = true, bool htmlEncode = true) { - return ResolveCdn(urlHelper, path, AzureCdnToolkit.Instance.CdnPackageVersion, asset, htmlEncode:htmlEncode); + return ResolveCdn(urlHelper, path, AzureCdnToolkit.Instance.CdnPackageVersion, asset, htmlEncode: htmlEncode); } // Special version of the method with fallback image for TinyMce converter @@ -166,7 +166,7 @@ public static IHtmlString ResolveCdn(this UrlHelper urlHelper, string path, stri if (asset && !path.InvariantContains("/media/")) { - cdnPath = string.Format("{0}/{1}", AzureCdnToolkit.Instance.CdnUrl, AzureCdnToolkit.Instance.AssetsContainer); + cdnPath = string.Format("{0}/{1}", AzureCdnToolkit.Instance.CdnUrl, AzureCdnToolkit.Instance.AssetsContainer); } else { @@ -228,9 +228,12 @@ internal static IHtmlString UrlToCdnUrl(string cropUrl, bool htmlEncode, string // If toolkit disabled return orginal string if (!AzureCdnToolkit.Instance.UseAzureCdnToolkit) { + LogHelper.Info(typeof(UrlHelperRenderExtensions), "AzureCdnToolkit is disabled"); return new HtmlString(cropUrl); } + LogHelper.Info(typeof(UrlHelperRenderExtensions), "AzureCdnToolkit is enabled"); + if (string.IsNullOrEmpty(currentDomain)) { currentDomain = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority); @@ -260,28 +263,61 @@ internal static IHtmlString UrlToCdnUrl(string cropUrl, bool htmlEncode, string absoluteCropPath = string.Format("{0}&securitytoken={1}", absoluteCropPath, securityToken); } - // Retry five times before giving up to account for networking issues - TryFiveTimes(() => + // lazy load the image + Lazy LazyLoadCdnImage = new Lazy(() => { - var request = (HttpWebRequest)WebRequest.Create(absoluteCropPath); - request.Method = "HEAD"; - using (var response = (HttpWebResponse)request.GetResponse()) + string result = null; + + try { - var responseCode = response.StatusCode; - if (responseCode.Equals(HttpStatusCode.OK)) + + // parse the image through the handler directly + + // HttpContext.Current + + + + // Retry five times before giving up to account for networking issues + TryFiveTimes(() => { - var absoluteUri = response.ResponseUri.AbsoluteUri; - newCachedImage.CacheUrl = absoluteUri; + var request = (HttpWebRequest)WebRequest.Create(absoluteCropPath); + request.Timeout = AzureCdnToolkit.Instance.CdnConnectionTimeout; + request.Method = "HEAD"; + using (var response = (HttpWebResponse)request.GetResponse()) + { + var responseCode = response.StatusCode; + if (responseCode.Equals(HttpStatusCode.OK)) + { + result = response.ResponseUri.AbsoluteUri; + } + } + }); + } + catch(Exception ex) + { + LogHelper.Error(typeof(UrlHelperRenderExtensions), "Error resolving media url from the CDN", ex); - // this is to mark URLs returned direct to Blob by ImageProcessor as not fully resolved - newCachedImage.Resolved = absoluteUri.InvariantContains(AzureCdnToolkit.Instance.CdnUrl); + // we have tried 5 times and failed so let's cache the normal address + newCachedImage = new CachedImage { WebUrl = cropUrl }; + newCachedImage.Resolved = false; + newCachedImage.CacheUrl = cropUrl; + Cache.InsertCacheItem(cacheKey, () => newCachedImage); - Cache.InsertCacheItem(cacheKey, () => newCachedImage); - fullUrlPath = response.ResponseUri.AbsoluteUri; - } + result = cropUrl; } - }); + newCachedImage.CacheUrl = result; + // this is to mark URLs returned direct to Blob by ImageProcessor as not fully resolved + newCachedImage.Resolved = fullUrlPath.InvariantContains(AzureCdnToolkit.Instance.CdnUrl); + Cache.InsertCacheItem(cacheKey, () => newCachedImage); + + return result; + + }, + System.Threading.LazyThreadSafetyMode.PublicationOnly); + + + fullUrlPath = LazyLoadCdnImage.Value; } else {