|
|
@ -1,5 +1,7 @@
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System;
|
|
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
|
|
|
|
|
|
namespace ZeroLevel.Services.Web
|
|
|
|
namespace ZeroLevel.Services.Web
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -60,6 +62,78 @@ namespace ZeroLevel.Services.Web
|
|
|
|
return str == null ? null : UrlEncodeUnicodeStringToStringInternal(str, false);
|
|
|
|
return str == null ? null : UrlEncodeUnicodeStringToStringInternal(str, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
|
|
/// https://github.com/tmenier/Flurl
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
public static string Combine(params string[] parts)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (parts == null)
|
|
|
|
|
|
|
|
throw new ArgumentNullException(nameof(parts));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string result = "";
|
|
|
|
|
|
|
|
bool inQuery = false, inFragment = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string CombineEnsureSingleSeparator(string a, string b, char separator)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(a)) return b;
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(b)) return a;
|
|
|
|
|
|
|
|
return a.TrimEnd(separator) + separator + b.TrimStart(separator);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var part in parts)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(part))
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (result.EndsWith("?") || part.StartsWith("?"))
|
|
|
|
|
|
|
|
result = CombineEnsureSingleSeparator(result, part, '?');
|
|
|
|
|
|
|
|
else if (result.EndsWith("#") || part.StartsWith("#"))
|
|
|
|
|
|
|
|
result = CombineEnsureSingleSeparator(result, part, '#');
|
|
|
|
|
|
|
|
else if (inFragment)
|
|
|
|
|
|
|
|
result += part;
|
|
|
|
|
|
|
|
else if (inQuery)
|
|
|
|
|
|
|
|
result = CombineEnsureSingleSeparator(result, part, '&');
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
result = CombineEnsureSingleSeparator(result, part, '/');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (part.Contains("#"))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
inQuery = false;
|
|
|
|
|
|
|
|
inFragment = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (!inFragment && part.Contains("?"))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
inQuery = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return EncodeIllegalCharacters(result);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
|
|
/// https://github.com/tmenier/Flurl
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
public static string EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus = false)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(s))
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (encodeSpaceAsPlus)
|
|
|
|
|
|
|
|
s = s.Replace(" ", "+");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Uri.EscapeUriString mostly does what we want - encodes illegal characters only - but it has a quirk
|
|
|
|
|
|
|
|
// in that % isn't illegal if it's the start of a %-encoded sequence https://stackoverflow.com/a/47636037/62600
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// no % characters, so avoid the regex overhead
|
|
|
|
|
|
|
|
if (!s.Contains("%"))
|
|
|
|
|
|
|
|
return Uri.EscapeUriString(s);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// pick out all %-hex-hex matches and avoid double-encoding
|
|
|
|
|
|
|
|
return Regex.Replace(s, "(.*?)((%[0-9A-Fa-f]{2})|$)", c => {
|
|
|
|
|
|
|
|
var a = c.Groups[1].Value; // group 1 is a sequence with no %-encoding - encode illegal characters
|
|
|
|
|
|
|
|
var b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!
|
|
|
|
|
|
|
|
return Uri.EscapeUriString(a) + b;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string UrlEncodeUnicodeStringToStringInternal(string s, bool ignoreAscii)
|
|
|
|
private static string UrlEncodeUnicodeStringToStringInternal(string s, bool ignoreAscii)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var l = s.Length;
|
|
|
|
var l = s.Length;
|
|
|
|