Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resource bundle #141

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
562 changes: 562 additions & 0 deletions RequestReduce.Facts/Module/ResponseFilterFacts.cs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions RequestReduce.Facts/Module/ResponseTransformerFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace RequestReduce.Facts.Module
{
public class ResponseTransformerFacts
{
private class TestableResponseTransformer : Testable<ResponseTransformer>
internal class TestableResponseTransformer : Testable<ResponseTransformer>
{
public TestableResponseTransformer()
{
Expand Down Expand Up @@ -791,7 +791,6 @@ public void WillTransformHeadWithDuplicateScriptsAndInlineScript()
Assert.Equal(transformed, result);
RRContainer.Current = null;
}

}
}
}
19 changes: 18 additions & 1 deletion RequestReduce.Facts/RRContainerFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ public System.Collections.Generic.IEnumerable<string> SupportedMimeTypes
get { throw new System.NotImplementedException(); }
}

public string TransformedMarkupTag(string url)
public int BundleId(string resource)
{
throw new System.NotImplementedException();
}

public string TransformedMarkupTag(string url, int bundle)
{
throw new System.NotImplementedException();
}
Expand All @@ -41,6 +46,18 @@ public System.Text.RegularExpressions.Regex ResourceRegex


public System.Func<string, string, bool> TagValidator { get; set; }


public bool IsLoadDeferred(int bundle)
{
throw new NotImplementedException();
}


public bool IsDynamicLoad(int bundle)
{
throw new NotImplementedException();
}
}

[Fact]
Expand Down
51 changes: 42 additions & 9 deletions RequestReduce/Module/ResponseFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Text;
using System.Web;
using RequestReduce.Api;
using RequestReduce.Utilities;

namespace RequestReduce.Module
{
Expand All @@ -15,6 +16,7 @@ public class ResponseFilter : AbstractFilter
private readonly HttpContextBase context;
private readonly Encoding encoding;
private readonly IResponseTransformer responseTransformer;
private static readonly RegexCache Regex = new RegexCache();
private byte[][] startStringUpper;
private bool[] currentStartStringsToSkip;
private byte[][] startStringLower;
Expand Down Expand Up @@ -95,6 +97,7 @@ public override bool CanWrite

public override void Close()
{
EmptyDeferredBundles();
Closed = true;
BaseStream.Close();
}
Expand All @@ -106,9 +109,10 @@ public override void Flush()
{
var transformed =
encoding.GetBytes(responseTransformer.Transform(encoding.GetString(transformBuffer.ToArray())));
BaseStream.Write(transformed, 0, transformed.Length);
WriteBaseStream(transformed, 0, transformed.Length);
transformBuffer.Clear();
}

BaseStream.Flush();
RRTracer.Trace("Flushing Filter");
var filterQs = context != null && context.Request != null ? context.Request.QueryString["rrfilter"] : null;
Expand Down Expand Up @@ -138,18 +142,18 @@ public override void Write(byte[] buffer, int offset, int count)
if (endTransformPosition > 0)
{
if ((actualOffset + actualLength) - endTransformPosition > 0)
BaseStream.Write(buffer, endTransformPosition, (actualOffset + actualLength) - endTransformPosition);
WriteBaseStream(buffer, endTransformPosition, (actualOffset + actualLength) - endTransformPosition);
}
else
BaseStream.Write(buffer, actualOffset, actualLength);
WriteBaseStream(buffer, actualOffset, actualLength);
break;
case SearchState.MatchingStart:
case SearchState.MatchingStartClose:
case SearchState.LookForStop:
case SearchState.MatchingStop:
case SearchState.LookForAdjacentScript:
if (startTransformPosition > actualOffset)
BaseStream.Write(buffer, actualOffset, startTransformPosition - actualOffset);
WriteBaseStream(buffer, actualOffset, startTransformPosition - actualOffset);
break;
}
RRTracer.Trace("Ending Filter Write");
Expand Down Expand Up @@ -186,7 +190,7 @@ private int HandleMatchingStartCloseMatch(int i, byte b)
return 0;
}
if (i - originalOffset < transformBuffer.Count)
BaseStream.Write(transformBuffer.ToArray(), 0, (transformBuffer.Count - (i - originalOffset)));
WriteBaseStream(transformBuffer.ToArray(), 0, (transformBuffer.Count - (i - originalOffset)));
transformBuffer.Clear();
state = SearchState.LookForStart;
return 0;
Expand Down Expand Up @@ -231,12 +235,12 @@ private void DoTransform(byte[] buffer, ref int startTransformPosition)
{
currentStartStringsToSkip = new bool[currentStartStringsToSkip.Length];
if ((startTransformPosition - actualOffset) >= 0)
BaseStream.Write(buffer, actualOffset, startTransformPosition - actualOffset);
WriteBaseStream(buffer, actualOffset, startTransformPosition - actualOffset);
try
{
var transformed =
encoding.GetBytes(responseTransformer.Transform(encoding.GetString(transformBuffer.ToArray())));
BaseStream.Write(transformed, 0, transformed.Length);
WriteBaseStream(transformed, 0, transformed.Length);
}
catch (Exception ex)
{
Expand All @@ -247,7 +251,7 @@ private void DoTransform(byte[] buffer, ref int startTransformPosition)
RRTracer.Trace(ex.ToString());
if (Registry.CaptureErrorAction != null)
Registry.CaptureErrorAction(wrappedException);
BaseStream.Write(transformBuffer.ToArray(), 0, transformBuffer.Count);
WriteBaseStream(transformBuffer.ToArray(), 0, transformBuffer.Count);
}
startTransformPosition = 0;
transformBuffer.Clear();
Expand Down Expand Up @@ -297,7 +301,7 @@ private int HandleMatchingStartMatch(byte b, ref int i, byte[] buffer, ref int e
else
{
if(i-originalOffset < transformBuffer.Count)
BaseStream.Write(transformBuffer.ToArray(), 0, (transformBuffer.Count - (i - originalOffset)));
WriteBaseStream(transformBuffer.ToArray(), 0, (transformBuffer.Count - (i - originalOffset)));
transformBuffer.Clear();
}
state = SearchState.LookForStart;
Expand Down Expand Up @@ -428,5 +432,34 @@ public override long Position
throw new NotSupportedException();
}
}

private void EmptyDeferredBundles()
{
string deferred = responseTransformer.EmptyDeferredBundles();
byte[] buffer = encoding.GetBytes(deferred);
BaseStream.Write(buffer, 0, buffer.Length);
}

private void WriteBaseStream(byte[] inputBuffer, int offset, int count)
{
string stringToWrite = encoding.GetString(inputBuffer, offset, count);

stringToWrite = TryEmptyDeferredBundles(stringToWrite, Regex.BodyEndPattern);
stringToWrite = TryEmptyDeferredBundles(stringToWrite, Regex.HtmlEndPattern);

byte[] bufferToWrite = encoding.GetBytes(stringToWrite);
BaseStream.Write(bufferToWrite, 0, bufferToWrite.Length);
}

private string TryEmptyDeferredBundles(string str, System.Text.RegularExpressions.Regex regex)
{
var match = regex.Match(str);
if (match.Success)
{
str = regex.Replace(str, responseTransformer.EmptyDeferredBundles() + match.ToString(), 1);
}

return str;
}
}
}
115 changes: 105 additions & 10 deletions RequestReduce/Module/ResponseTransformer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace RequestReduce.Module
public interface IResponseTransformer
{
string Transform(string preTransform);
string EmptyDeferredBundles();
}

public class ResponseTransformer : IResponseTransformer
Expand All @@ -24,6 +25,7 @@ public class ResponseTransformer : IResponseTransformer
private static readonly RegexCache Regex = new RegexCache();
private readonly IReducingQueue reducingQueue;
private readonly HttpContextBase context;
private readonly Dictionary<IResourceType, Dictionary<int, IList<KeyValuePair<string, string>>>> deferredResources = new Dictionary<IResourceType, Dictionary<int, IList<KeyValuePair<string, string>>>>();

public ResponseTransformer(IReductionRepository reductionRepository, IReducingQueue reducingQueue, HttpContextBase context, IRRConfiguration config, IUriBuilder uriBuilder)
{
Expand Down Expand Up @@ -51,8 +53,10 @@ private string Transform<T>(string preTransform) where T : IResourceType
{
var urls = new StringBuilder();
var transformableMatches = new List<string>();
foreach (var match in matches)
int currentBundle = 0;
for (int cursor = 0; cursor < matches.Count; cursor++)
{
var match = matches[cursor];
var strMatch = match.ToString();
var urlMatch = Regex.UrlPattern.Match(strMatch);
bool matched = false;
Expand All @@ -61,23 +65,54 @@ private string Transform<T>(string preTransform) where T : IResourceType
var url = RelativeToAbsoluteUtility.ToAbsolute(config.BaseAddress == null ? context.Request.Url : new Uri(config.BaseAddress), urlMatch.Groups["url"].Value);
if ((resource.TagValidator == null || resource.TagValidator(strMatch, url)) && (RRContainer.Current.GetAllInstances<IFilter>().Where(x => (x is CssFilter && typeof(T) == typeof(CssResource)) || (x is JavascriptFilter && typeof(T) == typeof(JavaScriptResource))).FirstOrDefault(y => y.IgnoreTarget(new CssJsFilterContext(context.Request, url, strMatch))) == null))
{
matched = true;
urls.Append(url);
urls.Append(GetMedia(strMatch));
urls.Append("::");
transformableMatches.Add(strMatch);
int matchBundle = resource.BundleId(strMatch);
if(!resource.IsLoadDeferred(matchBundle))
{
if ((transformableMatches.Count == 0) || (matchBundle == currentBundle))
{
matched = true;
currentBundle = resource.BundleId(strMatch);
urls.Append(url);
urls.Append(GetMedia(strMatch));
urls.Append("::");
transformableMatches.Add(strMatch);
}
else
{
cursor--; // This resource into next bundle
}
}
else
{
// This resource into deferred bundle

var idx = preTransform.IndexOf(strMatch, StringComparison.Ordinal);
preTransform = preTransform.Remove(idx, strMatch.Length);

if(!deferredResources.ContainsKey(resource))
{
deferredResources[resource] = new Dictionary<int, IList<KeyValuePair<string, string>>>();
}
var deferredResourceBundles = deferredResources[resource];
if(!deferredResourceBundles.ContainsKey(matchBundle))
{
deferredResourceBundles[matchBundle] = new List<KeyValuePair<string, string>>();
}
var deferredBundle = deferredResourceBundles[matchBundle];
deferredBundle.Add(new KeyValuePair<string, string>(url, strMatch));
}
}
}
if (!matched && transformableMatches.Count > 0)
{
preTransform = DoTransform<T>(preTransform, urls, transformableMatches, noCommentTransform);
preTransform = DoTransform<T>(preTransform, urls, transformableMatches, noCommentTransform, currentBundle);
urls.Length = 0;
transformableMatches.Clear();
}
}
if (transformableMatches.Count > 0)
{
preTransform = DoTransform<T>(preTransform, urls, transformableMatches, noCommentTransform);
preTransform = DoTransform<T>(preTransform, urls, transformableMatches, noCommentTransform, currentBundle);
urls.Length = 0;
transformableMatches.Clear();
}
Expand All @@ -93,7 +128,7 @@ private string GetMedia(string strMatch)
return null;
}

private string DoTransform<T>(string preTransform, StringBuilder urls, List<string> transformableMatches, string noCommentTransform) where T : IResourceType
private string DoTransform<T>(string preTransform, StringBuilder urls, List<string> transformableMatches, string noCommentTransform, int bundle) where T : IResourceType
{
var resource = RRContainer.Current.GetInstance<T>();
RRTracer.Trace("Looking for reduction for {0}", urls);
Expand All @@ -110,7 +145,7 @@ private string DoTransform<T>(string preTransform, StringBuilder urls, List<stri
preTransform.IndexOf(transformableMatches[0], StringComparison.Ordinal) && resource is CssResource)
? firstScriptIndex - 1
: preTransform.IndexOf(transformableMatches[0], StringComparison.Ordinal) - 1;
preTransform = preTransform.Insert(insertionIdx + 1, resource.TransformedMarkupTag(transform));
preTransform = preTransform.Insert(insertionIdx + 1, resource.TransformedMarkupTag(transform, bundle));
}
var result = preTransform;
foreach (var match in transformableMatches)
Expand All @@ -130,5 +165,65 @@ private string DoTransform<T>(string preTransform, StringBuilder urls, List<stri
RRTracer.Trace("No reduction found for {0}. Enqueuing.", urls);
return preTransform;
}

// Last chance to empty deferred buckets
public string EmptyDeferredBundles()
{
return EmptyDeferredResourceBundle<JavaScriptResource>();
}

public string EmptyDeferredResourceBundle<T>() where T : IResourceType
{
var resource = RRContainer.Current.GetInstance<T>();
StringBuilder completeTransform = new StringBuilder();

if (deferredResources.ContainsKey(resource))
{
var bundles = deferredResources[resource];
if (bundles != null)
{
foreach (int bundleId in bundles.Keys)
{
if (bundles[bundleId] != null)
{
var urls = new StringBuilder();
var transformableMatches = new List<string>();
StringBuilder transformBuilder = new StringBuilder();

var matches = bundles[bundleId];
if (matches.Count > 0)
{
foreach (var match in matches)
{
string url = match.Key;
string strMatch = match.Value;
urls.Append(url);
urls.Append(GetMedia(strMatch));
urls.Append("::");
transformableMatches.Add(strMatch);
transformBuilder.Append(strMatch);
}
bundles[bundleId].Clear();

string transform = transformBuilder.ToString();
transform = DoTransform<T>(transform, urls, transformableMatches, transform, bundleId);

if (resource.IsDynamicLoad(bundleId))
{
completeTransform.Insert(0, transform);
}
else
{
completeTransform.Append(transform);
}
}
}
}
}
}

return completeTransform.ToString();
}

}
}
1 change: 1 addition & 0 deletions RequestReduce/RequestReduce.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
<Reference Include="System.Drawing" />
<Reference Include="System.Transactions" />
<Reference Include="System.Web" />
<Reference Include="System.Web.RegularExpressions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
Expand Down
18 changes: 17 additions & 1 deletion RequestReduce/ResourceTypes/CssResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ public IEnumerable<string> SupportedMimeTypes
get { return new[] { "text/css" }; }
}

public string TransformedMarkupTag(string url)
public int BundleId(string resource)
{
return 0;
}

public string TransformedMarkupTag(string url, int bundle)
{
return string.Format(CssFormat, url);
}
Expand All @@ -31,5 +36,16 @@ public Regex ResourceRegex


public Func<string, string, bool> TagValidator { get; set; }


public bool IsLoadDeferred(int bundle)
{
return false;
}

public bool IsDynamicLoad(int bundle)
{
return false;
}
}
}
Loading