/// <summary> /// If CDN is enabled for this request, replace the outgoing media url with the cdn version /// </summary> /// <param name="item"></param> /// <param name="options"></param> /// <returns></returns> public override string GetMediaUrl(Sitecore.Data.Items.MediaItem item, MediaUrlOptions options) { string hostname = CDNManager.GetCDNHostName(); string url = base.GetMediaUrl(item, options); bool shouldReplace = !string.IsNullOrEmpty(hostname) && // cdnHostname exists for site Sitecore.Context.PageMode.IsNormal; // PageMode is normal bool dontReplace = !CDNManager.IsMediaPubliclyAccessible(item) || // media is publicly accessible CDNManager.IsMediaAnalyticsTracked(item); // media is analytics tracked CDNUrlState contextState = CDNUrlSwitcher.CurrentValue; if (contextState == CDNUrlState.Enabled) { shouldReplace = true; } else if (contextState == CDNUrlState.Disabled) { UrlString url2 = new UrlString(url); url2[CDNManager.StopToken] = "1"; url = url2.ToString(); shouldReplace = false; } if (shouldReplace && !dontReplace) // media not DMS tracked { return(CDNManager.ReplaceMediaUrl(url, hostname)); } else { return(url); } }
public override void Process(HttpRequestArgs args) { Assert.ArgumentNotNull(args, "args"); if (!CDNSettings.Enabled) { return; } bool shouldFilter = (Sitecore.Context.Item != null || CDNManager.ShouldProcessRequest(args.Url.FilePathWithQueryString)) && // if an item is resolved (this is a page request) or file ext is listed in <processRequests> !CDNManager.ShouldExcludeProcessRequest(args.Url.FilePathWithQueryString) && // if the url is not on the excluded list Sitecore.Context.Site != null && // and a site was resolved Sitecore.Context.PageMode.IsNormal && // and the site is not in editing mode !string.IsNullOrEmpty(CDNManager.GetCDNHostName()); // and the current site is not in the excluded sites list // querystring cdn=1 to force replacement // querystring cdn=0 to force no replacement Tristate force = MainUtil.GetTristate(WebUtil.GetQueryString("cdn"), Tristate.Undefined); if (force == Tristate.False) { shouldFilter = false; } else if (force == Tristate.True) { shouldFilter = true; } if (shouldFilter) { var response = HttpContext.Current.Response; if (response != null) { // replace the default response filter stream with our replacer filter response.Filter = new MediaUrlFilter(response.Filter); } } }
/// <summary> /// This Method buffers the original Write payloads until the end of the end [/html] tag /// when replacement occurs /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> public override void Write(byte[] buffer, int offset, int count) { // preview the contents of the payload string content = UTF8Encoding.UTF8.GetString(buffer, offset, count); Regex eof = new Regex("</html>", RegexOptions.IgnoreCase); // if the content contains </html> we know we're at the end of the line // otherwise append the contents to the stringbuilder if (!eof.IsMatch(content)) { if (_isComplete) { _responseStream.Write(buffer, offset, count); } else { _sb.Append(content); } } else { _sb.Append(content.Substring(0, content.IndexOf("</html>") + 7)); string extra = content.Substring(content.IndexOf("</html>") + 7); try { using (new TimerReport("replaceMediaUrls")) { // parse complete document into HtmlDocument HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(_sb.ToString()); if (CDNSettings.DebugParser) { var parseErrors = doc.ParseErrors; if (parseErrors != null) { parseErrors = parseErrors.Where(pe => pe.Code == HtmlParseErrorCode.EndTagInvalidHere || pe.Code == HtmlParseErrorCode.TagNotClosed || pe.Code == HtmlParseErrorCode.TagNotOpened); } if (parseErrors != null && parseErrors.Any()) { StringBuilder sb = new StringBuilder(); foreach (var parseError in parseErrors) { sb.AppendLine(string.Format("PARSE ERROR: {0}", parseError.Reason)); sb.AppendLine(string.Format("Line: {0} Position: {1}", parseError.Line, parseError.LinePosition)); sb.AppendLine(string.Format("Source: {0}", parseError.SourceText)); sb.AppendLine(""); } Log.Error(string.Format("CDN Url Parsing Error - URL: {0} {1} {2}", WebUtil.GetRawUrl(), Environment.NewLine, sb.ToString()), this); } } // replace appropriate urls CDNManager.ReplaceMediaUrls(doc); StreamWriter writer = new StreamWriter(_responseStream); doc.Save(writer); writer.Flush(); } _isComplete = true; } catch (Exception ex) { Log.Error("CDN MediaURL Filter Error", ex, this); } if (!string.IsNullOrEmpty(extra)) { byte[] data = UTF8Encoding.UTF8.GetBytes(extra); _responseStream.Write(data, 0, data.Length); } } }
public void ProcessRequest(HttpContext context) { // set from an earlier in pipeline by CDNInterceptPipeline string minifyPath = StringUtil.GetString(context.Items["MinifyPath"]); if (string.IsNullOrEmpty(minifyPath)) { context.Response.StatusCode = 404; context.Response.End(); } UrlString url = new UrlString(minifyPath); string localPath = url.Path; string filePath = FileUtil.MapPath(localPath); // if the request is a .js file if (localPath.EndsWith(".js")) { HashEncryption hasher = new HashEncryption(HashEncryption.EncryptionProvider.MD5); if (!string.IsNullOrEmpty(localPath)) { // generate a unique filename for the cached .js version string cachedFilePath = FileUtil.MapPath(string.Format("/App_Data/MediaCache/{0}.js", hasher.Hash(url.ToString()))); // if it doesn't exist create it if (!FileUtil.FileExists(cachedFilePath)) { // if the original file exsits minify it if (FileUtil.FileExists(filePath)) { JsMinifier minifier = new JsMinifier(); string minified = minifier.Minify(filePath); FileUtil.WriteToFile(cachedFilePath, minified); } } if (FileUtil.FileExists(cachedFilePath)) { context.Response.ClearHeaders(); context.Response.Cache.SetExpires(DateTime.Now.AddDays(14)); context.Response.AddHeader("Content-Type", "application/x-javascript; charset=utf-8"); context.Response.WriteFile(cachedFilePath); context.Response.End(); _success = true; } } } // if the request is a .css file else if (localPath.EndsWith(".css")) { HashEncryption hasher = new HashEncryption(HashEncryption.EncryptionProvider.MD5); if (!string.IsNullOrEmpty(localPath)) { // generate a unique filename for the cached .css version string cachedFilePath = FileUtil.MapPath(string.Format("/App_Data/MediaCache/{0}.css", hasher.Hash(url.ToString()))); // if it doesn't exist create it if (!FileUtil.FileExists(cachedFilePath)) { // if the original file exsits minify it if (FileUtil.FileExists(filePath)) { CssMinifier minifier = new CssMinifier(); string minified = CssMinifier.Minify(FileUtil.ReadFromFile(filePath)); // if Css Processing is enabled, replace any urls inside the css file. if (CDNSettings.ProcessCss) { // find all css occurences of url([url]) Regex reReplaceUrl = new Regex("url\\(\\s*['\"]?([^\"')]+)['\"]?\\s*\\)"); try { // replacing url([url]) with url([cdnUrl]) in css minified = reReplaceUrl.Replace(minified, (m) => { string oldUrl = ""; if (m.Groups.Count > 1) { oldUrl = m.Groups[1].Value; } if (WebUtil.IsInternalUrl(oldUrl)) { if (oldUrl.StartsWith(".")) { oldUrl = VirtualPathUtility.Combine(url.Path, oldUrl); } string newUrl = CDNManager.ReplaceMediaUrl(oldUrl, string.Empty); if (!string.IsNullOrEmpty(newUrl)) { return(m.Value.Replace(m.Groups[1].Value, newUrl)); } } return(m.Value); }); } catch (Exception ex) { Log.Error("Minify error", ex, this); } } FileUtil.WriteToFile(cachedFilePath, minified); } } if (FileUtil.FileExists(cachedFilePath)) { context.Response.ClearHeaders(); context.Response.Cache.SetExpires(DateTime.Now.AddDays(14)); context.Response.AddHeader("Content-Type", "text/css; charset=utf-8"); context.Response.TransmitFile(cachedFilePath); context.Response.End(); _success = true; } } } }