/// <summary> /// Writes a given path to the stream /// </summary> /// <param name="type"></param> /// <param name="path">The path could be a local url or an absolute url</param> /// <param name="context"></param> /// <param name="sw"></param> /// <returns>If successful returns a CompositeFileDefinition, otherwise returns null</returns> public CompositeFileDefinition WritePathToStream(ClientDependencyType type, string path, HttpContextBase context, StreamWriter sw) { CompositeFileDefinition def = null; if (!string.IsNullOrEmpty(path)) { try { var fi = new FileInfo(context.Server.MapPath(path)); //all config based extensions and all extensions registered by file writers var fileBasedExtensions = ClientDependencySettings.Instance.FileBasedDependencyExtensionList .Union(FileWriters.GetRegisteredExtensions()); if (fileBasedExtensions.Contains(fi.Extension.ToUpper())) { //if the file doesn't exist, then we'll assume it is a URI external request def = !fi.Exists ? WriteFileToStream(sw, path, type, context) //external request : WriteFileToStream(sw, fi, type, path, context); //internal request } else { //if it's not a file based dependency, try to get the request output. def = WriteFileToStream(sw, path, type, context); } } catch (Exception ex) { if (ex is NotSupportedException || ex is ArgumentException || ex is HttpException) { //could not parse the string into a fileinfo or couldn't mappath, so we assume it is a URI def = WriteFileToStream(sw, path, type, context); } else { //if this fails, log the exception, but continue ClientDependencySettings.Instance.Logger.Error(string.Format("Could not load file contents from {0}. EXCEPTION: {1}", path, ex.Message), ex); } } } if (type == ClientDependencyType.Javascript) { sw.Write(";;;"); //write semicolons in case the js isn't formatted correctly. This also helps for debugging. } return(def); }
/// <summary> /// Writes a given path to the stream /// </summary> /// <param name="type"></param> /// <param name="path">The path could be a local url or an absolute url</param> /// <param name="context"></param> /// <param name="sw"></param> /// <returns>If successful returns a CompositeFileDefinition, otherwise returns null</returns> public CompositeFileDefinition WritePathToStream(ClientDependencyType type, string path, HttpContextBase context, StreamWriter sw) { path = path.Trim(); if (string.IsNullOrEmpty(path) || !PathHelper.TryGetFileExtension(path, out var extension)) { return(null); } CompositeFileDefinition def = null; //all config based extensions and all extensions registered by file writers var fileBasedExtensions = ClientDependencySettings.Instance.FileBasedDependencyExtensionList .Union(FileWriters.GetRegisteredExtensions()) .ToList(); try { if (fileBasedExtensions.Contains(extension, StringComparer.InvariantCultureIgnoreCase)) { if (CanProcessLocally(context, path, out IVirtualFileWriter virtualWriter)) { //internal request if (virtualWriter != null) { var vf = virtualWriter.FileProvider.GetFile(path); def = WriteVirtualFileToStream(sw, vf, virtualWriter, type, context); } else { if (PathHelper.TryGetFileInfo(path, context, out var fi)) { def = WriteFileToStream(sw, fi, type, path, context); } } } else { //Before we try to load it by URI, we want to check if the URI is a local file request. //We can try to detect if it is and try to load it from the file system. //If the file isn't local and doesn't exist then we'll continue trying to load it via the URI. //NOTE: At this stage we've already validated that the file type is based on the file types registered with CDF. if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out Uri uri)) { if (uri.IsLocalUri(context)) { //extract the path of the request and ensure it starts with the virtual path marker (~/) so that the file //can only be looked up local to this website. var absPath = uri.AbsolutePath.EnsureStartsWith("/").EnsureStartsWith("~"); if (PathHelper.TryGetFileInfo(absPath, context, out var fi)) { //Re-validate the extension since URIs and file names can parse differently extension = fi.Extension; if (fileBasedExtensions.Contains(extension, StringComparer.InvariantCultureIgnoreCase)) { def = WriteFileToStream(sw, fi, type, path, context); } } } else { //external request to a file based dependency def = WriteFileToStream(sw, path, type, context); } } } } else { //if it's not a file based dependency, try to get the request output. def = WriteFileToStream(sw, path, type, context); } } catch (Exception ex) { //if this fails, log the exception, but continue ClientDependencySettings.Instance.Logger.Error($"Could not load file contents from {path}. EXCEPTION: {ex.Message}", ex); } if (def == null) { return(null); } if (type == ClientDependencyType.Javascript) { sw.Write(";;;"); //write semicolons in case the js isn't formatted correctly. This also helps for debugging. } return(def); }
/// <summary> /// Writes a given path to the stream /// </summary> /// <param name="type"></param> /// <param name="path">The path could be a local url or an absolute url</param> /// <param name="context"></param> /// <param name="sw"></param> /// <returns>If successful returns a CompositeFileDefinition, otherwise returns null</returns> public CompositeFileDefinition WritePathToStream(ClientDependencyType type, string path, HttpContextBase context, StreamWriter sw) { CompositeFileDefinition def = null; if (!string.IsNullOrEmpty(path)) { try { var fi = new FileInfo(context.Server.MapPath(path)); //all config based extensions and all extensions registered by file writers var fileBasedExtensions = ClientDependencySettings.Instance.FileBasedDependencyExtensionList .Union(FileWriters.GetRegisteredExtensions()); if (fileBasedExtensions.Contains(fi.Extension.ToUpper())) { //if the file doesn't exist, then we'll assume it is a URI external request def = !fi.Exists ? WriteFileToStream(sw, path, type, context) //external request : WriteFileToStream(sw, fi, type, path, context); //internal request } else { //if it's not a file based dependency, try to get the request output. def = WriteFileToStream(sw, path, type, context); } } catch (Exception ex) { if (ex is NotSupportedException || ex is ArgumentException || ex is HttpException) { //could not parse the string into a fileinfo or couldn't mappath, so we assume it is a URI //before we try to load it by URI, we want to check if the URI is a local request, we'll try to detect if it is and // then try to load it from the file system, if the file isn't there then we'll continue trying to load it via the URI. Uri uri; if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out uri) && uri.IsLocalUri(context)) { var localPath = uri.PathAndQuery; var fi = new FileInfo(context.Server.MapPath(localPath)); if (fi.Exists) { try { WriteFileToStream(sw, fi, type, path, context); //internal request } catch (Exception ex1) { ClientDependencySettings.Instance.Logger.Error(string.Format("Could not load file contents from {0}. EXCEPTION: {1}", path, ex1.Message), ex1); } } } def = WriteFileToStream(sw, path, type, context); } else { //if this fails, log the exception, but continue ClientDependencySettings.Instance.Logger.Error(string.Format("Could not load file contents from {0}. EXCEPTION: {1}", path, ex.Message), ex); } } } if (type == ClientDependencyType.Javascript) { sw.Write(";;;"); //write semicolons in case the js isn't formatted correctly. This also helps for debugging. } return(def); }
/// <summary> /// Because we can have both internal and external dependencies rendered, we need to stagger the script tag output... if they are external, we need to stop the compressing/combining /// and write out the external dependency, then resume the compressing/combining handler. /// </summary> /// <param name="dependencies"></param> /// <param name="http"></param> /// <param name="builder"></param> /// <param name="renderCompositeFiles"></param> /// <param name="renderSingle"></param> protected void WriteStaggeredDependencies( IEnumerable <IClientDependencyFile> dependencies, HttpContextBase http, StringBuilder builder, Func <IEnumerable <IClientDependencyFile>, HttpContextBase, IDictionary <string, string>, string> renderCompositeFiles, Func <string, IDictionary <string, string>, string> renderSingle) { var fileBasedExtensions = ClientDependencySettings.Instance.FileBasedDependencyExtensionList .Union(FileWriters.GetRegisteredExtensions()) .ToArray(); var currNonRemoteFiles = new List <IClientDependencyFile>(); foreach (var f in dependencies) { //need to parse out the request's extensions and remove query strings //need to force non-bundled lines for items with query parameters or a hash value. var extension = f.FilePath.Contains('?') || f.FilePath.Contains('#') ? "" : Path.GetExtension(f.FilePath); var stringExt = ""; #if !Net35 if (!string.IsNullOrWhiteSpace(extension)) #else if (!string.IsNullOrEmpty(extension)) #endif { stringExt = extension.ToUpper().Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0]; } //if this is a protocol-relative/protocol-less uri, then we need to add the protocol for the remaining // logic to work properly if (f.FilePath.StartsWith("//")) { f.FilePath = Regex.Replace(f.FilePath, @"^\/\/", http.Request.Url.GetLeftPart(UriPartial.Scheme)); } // if it is an external resource OR // if it is a non-standard JS/CSS resource (i.e. a server request) // then we need to break the sequence // unless it has been explicitely required that the dependency be bundled if ((!http.IsAbsolutePath(f.FilePath) && !fileBasedExtensions.Contains(stringExt)) //now check for external resources || (http.IsAbsolutePath(f.FilePath) //remote dependencies aren't local && !new Uri(f.FilePath, UriKind.RelativeOrAbsolute).IsLocalUri(http) // not required to be bundled && !f.ForceBundle)) { //we've encountered an external dependency, so we need to break the sequence and restart it after //we output the raw script tag if (currNonRemoteFiles.Count > 0) { //render the current buffer StaggerOnDifferentAttributes(http, builder, currNonRemoteFiles, renderCompositeFiles); //clear the buffer currNonRemoteFiles.Clear(); } //write out the single script tag builder.Append(renderSingle(f.FilePath, GetHtmlAttributes(f))); } else { //its a normal registration, add to the buffer currNonRemoteFiles.Add(f); } } //now check if there's anything in the buffer to render if (currNonRemoteFiles.Count > 0) { //render the current buffer StaggerOnDifferentAttributes(http, builder, currNonRemoteFiles, renderCompositeFiles); } }
/// <summary> /// Because we can have both internal and external dependencies rendered, we need to stagger the script tag output... if they are external, we need to stop the compressing/combining /// and write out the external dependency, then resume the compressing/combining handler. /// </summary> /// <param name="dependencies"></param> /// <param name="http"></param> /// <param name="builder"></param> /// <param name="renderCompositeFiles"></param> /// <param name="renderSingle"></param> protected void WriteStaggeredDependencies( IEnumerable <IClientDependencyFile> dependencies, HttpContextBase http, StringBuilder builder, Func <IEnumerable <IClientDependencyFile>, HttpContextBase, IDictionary <string, string>, string> renderCompositeFiles, Func <string, IDictionary <string, string>, string> renderSingle) { //This action will stagger the output based on whether or not the html attribute declarations are the same for each dependency Action <IEnumerable <IClientDependencyFile> > staggerOnDifferentAttributes = (list) => { var sameAttributes = new List <IClientDependencyFile>(); var currHtmlAttr = GetHtmlAttributes(list.ElementAt(0)); foreach (var c in list) { if (!currHtmlAttr.IsEqualTo(GetHtmlAttributes(c))) { //if the attributes are different we need to stagger if (sameAttributes.Any()) { //render the current buffer builder.Append(renderCompositeFiles(sameAttributes, http, currHtmlAttr)); //clear the buffer sameAttributes.Clear(); } } //add the item to the buffer and set the current html attributes sameAttributes.Add(c); currHtmlAttr = GetHtmlAttributes(c); } //if there's anything in the buffer then write the remaining if (sameAttributes.Any()) { builder.Append(renderCompositeFiles(sameAttributes, http, currHtmlAttr)); } }; var fileBasedExtensions = ClientDependencySettings.Instance.FileBasedDependencyExtensionList .Union(FileWriters.GetRegisteredExtensions()) .ToArray(); var currNonRemoteFiles = new List <IClientDependencyFile>(); foreach (var f in dependencies) { // if it is an external resource OR // if it is a non-standard JS/CSS resource (i.e. a server request) // then we need to break the sequence // unless it has been explicitely required that the dependency be bundled if (!http.IsAbsolutePath(f.FilePath) && !fileBasedExtensions.Contains(Path.GetExtension(f.FilePath).ToUpper()) //now check for external resources || (http.IsAbsolutePath(f.FilePath) //remote dependencies aren't local && !new Uri(f.FilePath, UriKind.RelativeOrAbsolute).IsLocalUri(http) // not required to be bundled && !f.ForceBundle)) { //we've encountered an external dependency, so we need to break the sequence and restart it after //we output the raw script tag if (currNonRemoteFiles.Count > 0) { //render the current buffer staggerOnDifferentAttributes(currNonRemoteFiles); //clear the buffer currNonRemoteFiles.Clear(); } //write out the single script tag builder.Append(renderSingle(f.FilePath, GetHtmlAttributes(f))); } else { //its a normal registration, add to the buffer currNonRemoteFiles.Add(f); } } //now check if there's anything in the buffer to render if (currNonRemoteFiles.Count > 0) { //render the current buffer staggerOnDifferentAttributes(currNonRemoteFiles); } }