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

Performance fixes #819

Open
wants to merge 4 commits into
base: dev
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
4 changes: 2 additions & 2 deletions Composite/Core/Localization/LocalizationParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
Expand All @@ -16,7 +16,7 @@ namespace Composite.Core.Localization
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static class LocalizationParser
{
private static readonly Regex _attributRegex = new Regex(@"\$\((?<type>[^:]+):(?<id>[^\)]+)\)");
private static readonly Regex _attributRegex = new Regex(@"\$\((?<type>[^:]+):(?<id>[^\)]+)\)", RegexOptions.Compiled);


/// <exclude />
Expand Down
82 changes: 74 additions & 8 deletions Composite/Core/UrlBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Util;
using Composite.Core.Extensions;
using Composite.Core.Routing.Pages;

Expand Down Expand Up @@ -85,28 +88,91 @@ public UrlBuilder(string url)

internal static class DefaultHttpEncoder
{
public static string UrlEncode(string urlPart)
private static readonly Func<string, string> _urlPathEncodeFunc;
private static readonly Func<string, Encoding, string> _urlDecodeFunc;
private static readonly Func<byte[], int, int, byte[]> _urlEncodeFunc;

static DefaultHttpEncoder()
{
HttpEncoder defaultHttpEncoder;
using (new NoHttpContext())
{
return HttpUtility.UrlEncode(urlPart);
defaultHttpEncoder = HttpEncoder.Current;
}

var instanceExpression = Expression.Constant(defaultHttpEncoder);

// Compiling: str => _defaultHttpEncoder.UrlPathEncode(str)
{
var stringParam = Expression.Parameter(typeof(string));
var methodInfo = typeof(HttpEncoder)
.GetMethod("UrlPathEncode", BindingFlags.Instance | BindingFlags.NonPublic, null, new []{typeof(string)}, null);
Verify.IsNotNull(methodInfo, "Failed to get method 'UrlPathEncode' from " + typeof(HttpEncoder).FullName);
var methodCallExpression = Expression.Call(instanceExpression, methodInfo, stringParam);
_urlPathEncodeFunc = Expression.Lambda<Func<string, string>>(methodCallExpression, stringParam).Compile();
}

// Compiling: (str, encoding) => _defaultHttpEncoder.UrlDecode(str, encoding);
{
var stringParam = Expression.Parameter(typeof(string));
var encodingParam = Expression.Parameter(typeof(Encoding));
var methodInfo = typeof(HttpEncoder)
.GetMethod("UrlDecode", BindingFlags.Instance | BindingFlags.NonPublic, null, new []{ typeof(string), typeof(Encoding) }, null);
Verify.IsNotNull(methodInfo, "Failed to get method 'UrlDecode' from " + typeof(HttpEncoder).FullName);

var methodCallExpression = Expression.Call(instanceExpression, methodInfo, stringParam, encodingParam);
_urlDecodeFunc = Expression.Lambda<Func<string, Encoding, string>>(
methodCallExpression, stringParam, encodingParam).Compile();
}

// Compiling: (bytes, offset, length) => _defaultHttpEncoder.UrlEncode(str, bytes, offset, length);
{
var bytesParam = Expression.Parameter(typeof(byte[]));
var offsetParam = Expression.Parameter(typeof(int));
var lengthParam = Expression.Parameter(typeof(int));
var methodInfo = typeof(HttpEncoder)
.GetMethod("UrlEncode", BindingFlags.Instance | BindingFlags.NonPublic, null,
new[] { typeof(byte[]), typeof(int), typeof(int) }, null);
Verify.IsNotNull(methodInfo, "Failed to get method 'UrlDecode' from " + typeof(HttpEncoder).FullName);

var methodCallExpression = Expression.Call(instanceExpression, methodInfo, bytesParam, offsetParam, lengthParam);
_urlEncodeFunc = Expression.Lambda<Func<byte[], int, int, byte[]>>(
methodCallExpression, bytesParam, offsetParam, lengthParam).Compile();
}
}

public static string UrlPathEncode(string urlPart)
private static byte[] UrlEncodeToBytes(string str, Encoding e)
{
using (new NoHttpContext())
if (str == null)
{
return HttpUtility.UrlPathEncode(urlPart);
return null;
}
byte[] bytes = e.GetBytes(str);

return _urlEncodeFunc(bytes, 0, bytes.Length);
}

public static string UrlDecode(string urlPart)

public static string UrlEncode(string urlPart)
{
using (new NoHttpContext())
if (urlPart == null)
{
return HttpUtility.UrlDecode(urlPart);
return null;
}

return Encoding.ASCII.GetString(UrlEncodeToBytes(urlPart, Encoding.UTF8));
}



public static string UrlPathEncode(string urlPart)
{
return _urlPathEncodeFunc(urlPart);
}

public static string UrlDecode(string urlPart)
{
return _urlDecodeFunc(urlPart, Encoding.UTF8);
}

private class NoHttpContext : IDisposable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private static XElement CopyWithoutNamespace(XElement source, XNamespace namespa
private static bool IsHtmlControlElement(XElement element)
{
var name = element.Name;
string xNamespace = element.Name.Namespace.NamespaceName;
string xNamespace = name.Namespace.NamespaceName;

return (xNamespace == Namespaces.Xhtml.NamespaceName || xNamespace == string.Empty)
&& !VoidElements.Contains(name.LocalName);
Expand Down Expand Up @@ -232,7 +232,9 @@ public static void CopyAttributes(this XElement source, HtmlControl target, bool
{
foreach (var attribute in source.Attributes())
{
if (attribute.Name.LocalName == "id")
string localName = attribute.Name.LocalName;

if (localName == "id")
{
target.ID = attribute.Value;
continue;
Expand All @@ -243,16 +245,15 @@ public static void CopyAttributes(this XElement source, HtmlControl target, bool
string namespaceName = attribute.Value;

if (namespaceName != "http://www.w3.org/1999/xhtml"
&& !namespaceName.StartsWith("http://www.composite.net/ns"))
&& !namespaceName.StartsWith("http://www.composite.net/ns", StringComparison.Ordinal))
{
target.Attributes.Add($"xmlns:{attribute.Name.LocalName}", attribute.Value);
target.Attributes.Add($"xmlns:{localName}", attribute.Value);
}

continue;
}

string localName = attribute.Name.LocalName;
if (localName != XName_Xmlns
if (localName != XName_Xmlns.LocalName
|| (copyXmlnsAttribute
&& (source.Parent == null || source.Name.Namespace != source.Parent.Name.Namespace)))
{
Expand Down
43 changes: 32 additions & 11 deletions Composite/Core/Xml/XhtmlPrettifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ namespace Composite.Core.Xml
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public static class XhtmlPrettifier
{
private static string _ampersandWord = "C1AMPERSAND";
private static readonly string _ampersandWord = "C1AMPERSAND";
private static readonly Regex _encodeCDataRegex = new Regex(@"<!\[CDATA\[((?:[^]]|\](?!\]>))*)\]\]>", RegexOptions.Compiled);
private static readonly Regex _decodeCDataRegex = new Regex("C1CDATAREPLACE(?<counter>[0-9]*)", RegexOptions.Compiled);
private static readonly Regex _encodeRegex = new Regex(@"&(?<tag>[^\;]+;)", RegexOptions.Compiled);
private static readonly Regex _decodeRegex = new Regex(@"C1AMPERSAND(?<tag>[^\;]+;)", RegexOptions.Compiled);

private static readonly char[] IncorrectEscapeSequenceCharacters = { '\'', '\"', '<', '>', ' ' };

Expand Down Expand Up @@ -168,7 +167,7 @@ private static void NodeTreeToString(IEnumerable<XmlNode> nodes, StringBuilder s
|| node.ParentNode == null
|| node.ParentNode.NamespaceByPrefix(attribute.Name) != node.NamespaceURI)
{
stringBuilder.Append(" ").Append(attribute.Name).Append("=\"").Append(EncodeAttributeString(attribute.Value)).Append("\"");
stringBuilder.Append(" ").Append(attribute.Name).Append("=\"").AppendEncodeAttributeString(attribute.Value).Append("\"");
}
}

Expand Down Expand Up @@ -236,7 +235,7 @@ private static void NodeTreeToString(IEnumerable<XmlNode> nodes, StringBuilder s
stringBuilder.Append(" ");
}

stringBuilder.Append(EncodeElementString(value));
stringBuilder.AppendEncodeElementString(value);

if (addSpaceToEnd)
{
Expand Down Expand Up @@ -281,7 +280,7 @@ private static void NodeTreeToString(IEnumerable<XmlNode> nodes, StringBuilder s
stringBuilder.AppendLine().AddIndent(node.Level, indentString);
}

stringBuilder.Append("<!--").Append(RemoveC1EncodedAmpersands(node.Value)).Append("-->");
stringBuilder.Append("<!--").AppendRemoveC1EncodedAmpersands(node.Value).Append("-->");
if (node.ParentNode != null && !node.ParentNode.IsBlockElement())
{
stringBuilder.AppendLine().AddIndent(node.Level - 1, indentString);
Expand Down Expand Up @@ -329,26 +328,48 @@ private static StringBuilder AddIndent(this StringBuilder sb, int level, string



private static string EncodeAttributeString(string value)
private static StringBuilder AppendEncodeAttributeString(this StringBuilder sb, string value)
{
value = value.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace("\"", "&quot;");

return RemoveC1EncodedAmpersands(value);
return sb.AppendRemoveC1EncodedAmpersands(value);
}



private static string EncodeElementString(string value)
private static StringBuilder AppendEncodeElementString(this StringBuilder sb, string value)
{
value = value.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;");

return RemoveC1EncodedAmpersands(value);
return sb.AppendRemoveC1EncodedAmpersands(value);
}


private static string RemoveC1EncodedAmpersands(string value)
private static StringBuilder AppendRemoveC1EncodedAmpersands(this StringBuilder sb, string value)
{
return _decodeRegex.Replace(value, match => "&" + match.Groups["tag"].Value);
var pointer = 0;
var ampersandWordPosition = value.IndexOf(_ampersandWord, StringComparison.Ordinal);
while (ampersandWordPosition > -1)
{
if (ampersandWordPosition > pointer)
{
sb.Append(value, pointer, ampersandWordPosition - pointer);
}

sb.Append('&');

pointer = ampersandWordPosition + _ampersandWord.Length;
if (pointer == value.Length) break;

ampersandWordPosition = value.IndexOf(_ampersandWord, pointer, StringComparison.Ordinal);
}

if (pointer < value.Length)
{
sb.Append(value, pointer, value.Length - pointer);
}

return sb;
}


Expand Down
4 changes: 2 additions & 2 deletions Composite/Data/DataServiceScopeManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
Expand Down Expand Up @@ -37,7 +37,7 @@ internal static void EnableServices()

internal static object GetService(Type t)
{
if (DisableServicesFlag.HasValue && DisableServicesFlag.Value)
if (DisableServicesFlag ?? false)
return null;

foreach(var serviceList in DataServiceScopeStack)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public string ToPublicUrl(string internalPageUrl, UrlSpace urlSpace)

try
{
anchor = new UrlBuilder(internalPageUrl).Anchor;
anchor = GetAnchor(internalPageUrl);
pageUrlData = PageUrls.UrlProvider.ParseInternalUrl(internalPageUrl);
}
catch
Expand Down Expand Up @@ -61,6 +61,14 @@ public string ToPublicUrl(string internalPageUrl, UrlSpace urlSpace)
return publicPageUrl;
}

private static string GetAnchor(string url)
{
int anchorIndex = url.IndexOf('#');
if (anchorIndex <= -1) return null;

return anchorIndex == url.Length - 1 ? string.Empty : url.Substring(anchorIndex + 1);
}

/// <inheritdoc />
public IDataReference ToDataReference(string internalUrl)
{
Expand Down