Sitecore AbortStaticFiles with Pipeline
June 25, 2011 Leave a Comment
Sitecore has a very flexible pipeline implementation allowing developers to extend the basic behaviours of how a page/file etc will be handled. In certain cases you don’t want to kill the system and just let certain files be executed without the Sitecore context and let the files be returned to the user as quickly and with minimal processing required. Now why do we want this, if you allow ASP.NET to handle all traffic, such as turing off extensions and friendly media file names then ASP.NET is running over every single server request made to the system, such as calling static files like jquery-1.5.js etc.
HttpRequest Pipeline Processor
This is where the AbortStaticFilesProcessor comes in, it allows the developers to plug this in and allow the static files of the system to be excluded from executing automatically.
So what do we need to do, firstly create a new class called “AbortStaticFilesProcessor” and inheritate from the “Sitecore.Pipelines.HttpRequestProcessor class.
Basically copy the code below;
/// <summary>
/// Aborts the static files from the processor, this also has an exclude list
/// just incase we need to abort a certain file to have the security
/// </summary>
public class AbortStaticFilesProcessor : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
{
#region Private Static Variables
/// <summary>
/// The executed URLS
/// </summary>
private static System.Collections.Generic.Dictionary<string, bool> ExecutedURLs = new Dictionary<string, bool>();
#endregion
#region Private Variables
/// <summary>
/// The list of exclusions for static files
/// </summary>
private System.Collections.ArrayList _excludes = new System.Collections.ArrayList();
/// <summary>
/// The list of dynamic files to include in excluding pipeline calling
/// </summary>
private System.Collections.ArrayList _dynamicExclude = new System.Collections.ArrayList();
#endregion
#region Public Properties
/// <summary>
/// Adds the exclude item to the list
/// </summary>
/// <param name="exclude">The exclude path to add</param>
public void AddExcludes(string exclude)
{
this._excludes.Add(exclude.ToLower());
}
/// <summary>
/// Adds the dynamic exclude item to the list
/// </summary>
/// <param name="dynamicExclude">The dynamic exclude to add</param>
public void AddDynamicExclude(string dynamicExclude)
{
this._dynamicExclude.Add(dynamicExclude.ToLower());
}
#endregion
#region Process
/// <summary>
/// The initial processor which is used to access the data
/// </summary>
/// <param name="args">Sitecore Arguments for the http request</param>
public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
{
// make sure we have our arguments before we have any issues
Sitecore.Diagnostics.Assert.ArgumentNotNull(args, "args");
// make sure we have data to actualy process
if (args.Context != null
&& args.Context.Request != null
&& !string.IsNullOrEmpty(args.Context.Request.PhysicalPath)
&& !args.Context.Request.Path.StartsWith("/sitecore/")
&& !args.Context.Request.Path.StartsWith("/sitecore modules/"))
{
// detects if the file is static
using (new Sitecore.Diagnostics.ProfileSection("Static File Detection."))
{
// setup defaults
bool killPipeline = false;
string cacheKey = args.Context.Request.Url.PathAndQuery;
// has this already been executed
if (ExecutedURLs.ContainsKey(cacheKey))
{
// gets out the key
ExecutedURLs.TryGetValue(cacheKey, out killPipeline);
}
else
{
// do we need to abort the execution
System.IO.FileInfo file = new System.IO.FileInfo(args.Context.Request.PhysicalPath);
// does the file exist
if (file.Exists)
{
// we want to kill the pipeline
killPipeline = true;
// do we have any extensions to check
if (this._excludes != null && this._excludes.Count > 0)
{
// we need to only deliver actual files instead of fake data
if (this._excludes.Contains(args.Context.Request.Path.ToLower()))
{
killPipeline = false;
}
}
}
else
{
// is the file part of the dynamic exclude
if (this._dynamicExclude.Contains(args.Context.Request.Path.ToLower()))
{
// kill the pipeline
killPipeline = true;
}
}
// append the executed url
ExecutedURLs.Add(args.Context.Request.Url.PathAndQuery, killPipeline);
Sitecore.Diagnostics.Log.Debug(string.Format("AbortStaticFile: URL:'{0}', AbortProcessing:'{1}'", args.Context.Request.Url.PathAndQuery, killPipeline), typeof(AbortStaticFilesProcessor));
}
// kill the pipeline
if (killPipeline)
{
// kill the pipeline
args.AbortPipeline();
return;
}
}
}
}
#endregion
}
This code needs to run as soon as possible so we can abort the pipeline to stop the following pipeline items from executing, we also remember what files have been executed to determine if they required the pipeline to be aborted or not for extra speed and reduced processing. Additionally a DEBUG log is added for any new requests not previously executed so you can always see what’s happening if there are any URLS not being requested correctly.
Config Settings
Next we have to include our HttpRequestBegin processor to the pipeline, but as im a comment freak I’ve also commented this quite heavily as well (soz if you don’t like comments);
<!--
Abort Static File Processor
************************************************************************
Description:
This processor aborts the httpRequestBeing pipeline when static files are executed.
This performs checks on the file to make sure it's actually found but also it
undertakes various checks with the exclude list.
The exclude list allows developers to input the list of excluded files which are static
files but need to be run through the sitecore pipeline such as the /default.aspx file.
The "desc" is required to work with 6.4 but isn't needed for 6.4.1 which is a known bug.
Example:
<processor ..>
<excludelist hint="list:AddExcludes">
<file desc="default sitecore file">/default.aspx</file>
<file desc="some calling json">/json-handler.ashx</file>
<file desc="checks stuff">/remote-check.ashx</file>
</excludelist>
<dynamicfiles hint="list:AddDynamicExclude">
<file desc="the defautl script resource">/scriptresource.axd</file>
<file desc="the defautl web resoruce">/webresource.axd</file>
</dynamicfiles>
</processor>
-->
<processor patch:before="*[@type='Sitecore.Pipelines.PreprocessRequest.CheckIgnoreFlag, Sitecore.Kernel']"
type="Test.AbortStaticFilesProcessor, Test">
<excludelist hint="list:AddExcludes">
<file desc="default sitecore file">/default.aspx</file>
<file desc="json handler">/json-handler.ashx</file>
</excludelist>
<dynamicfiles hint="list:AddDynamicExclude">
<file desc="the defautl script resource">/scriptresource.axd</file>
<file desc="the defautl web resoruce">/webresource.axd</file>
</dynamicfiles>
</processor>
You will need to make the changes to the <processor patch:before=”..” type=”Test.AbortStaticFileProcessor, Test“> to put in the correct Assembly and Namespace.
Also you will notice the “patch:before”, if you don’t know what this is you should have a quick search on the net, but you can use this in external config files which are located in the app_config/include folder, this is how most of the new modules work these days and what I see it’s a lot easier to have it here then to try and remember what changes where done in the web.config file. NOTE: This is only for the <sitecore> node only.
What we have are two main lists that have to be used;
ExcludeList
This list is where the files, handlers etc are listed which need to be processed by Sitecore, we take a policy that we exclude everything unless it in this list including the default.aspx which Sitecore needs.
You will see that below the excludelist you have a collection of “file” nodes, these have the “Desc” attribute, this is required only for 6.4 as it’s a known bug but has been resolved in 6.4.1. The file location needs to be a relative path from the root location and requires the “/” at the start.
DynamicFiles
This list is where the dynamically bound resources such as .axd files are placed, you shouldn’t have to make any changes to this but it’s listed here just in case, the same sort of rules apply for this just like the excludelist.
Does this improve speed
Well to put it simply YES, it stops Sitecore from executing these files quite high up in the pipeline and once the request file has been processed by this pipeline processor the system remembers this and will stop and allow this execution without having to determine if it’s a file, exclude, dynamic etc.
Give it a play and see what you think.