AngleSharp by AngleSharp

<PackageReference Include="AngleSharp" Version="1.1.1-beta.390" />

.NET API 956,416 bytes

 Url

public sealed class Url : IEquatable<Url>
Represents an Url class according to RFC3986. This is the base for all internal Url manipulation. Specification for the API used from https://url.spec.whatwg.org/#api.
using AngleSharp.Attributes; using AngleSharp.Io; using AngleSharp.Text; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Text; namespace AngleSharp.Dom { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] [DomName("URL")] [DomExposed("Window")] [DomExposed("Worker")] public sealed class Url : IEquatable<Url> { private static readonly string CurrentDirectory = "."; private static readonly string CurrentDirectoryAlternative = "%2e"; private static readonly string UpperDirectory = ".."; private static readonly string[] UpperDirectoryAlternatives = new string[3] { "%2e%2e", ".%2e", "%2e." }; private static readonly Url DefaultBase = new Url(string.Empty, string.Empty, string.Empty); private static readonly char[] C0ControlAndSpace = (from c in Enumerable.Range(0, 33) select (char)c).ToArray(); private static readonly IdnMapping DefaultIdnMapping = new IdnMapping { AllowUnassigned = false, UseStd3AsciiRules = false }; [System.Runtime.CompilerServices.Nullable(2)] private string _fragment; [System.Runtime.CompilerServices.Nullable(2)] private string _query; private string _path; private string _scheme; private string _port; private string _host; [System.Runtime.CompilerServices.Nullable(2)] private string _username; [System.Runtime.CompilerServices.Nullable(2)] private string _password; private bool _relative; private string _schemeData; [System.Runtime.CompilerServices.Nullable(2)] private UrlSearchParams _params; private bool _error; [System.Runtime.CompilerServices.Nullable(2)] [DomName("origin")] public string Origin { [System.Runtime.CompilerServices.NullableContext(2)] get { if (_scheme.Is(ProtocolNames.Blob)) { Url url = new Url(_schemeData); if (!url.IsInvalid) return url.Origin; } else if (ProtocolNames.IsOriginable(_scheme)) { StringBuilder stringBuilder = StringBuilderPool.Obtain(); if (!string.IsNullOrEmpty(_host)) { if (!string.IsNullOrEmpty(_scheme)) stringBuilder.Append(_scheme).Append(':'); stringBuilder.Append('/').Append('/').Append(_host); if (!string.IsNullOrEmpty(_port)) stringBuilder.Append(':').Append(_port); } return stringBuilder.ToPool(); } return null; } } public bool IsInvalid => _error; public bool IsRelative { get { if (_relative) return string.IsNullOrEmpty(_scheme); return false; } } public bool IsAbsolute => !IsRelative; [System.Runtime.CompilerServices.Nullable(2)] [DomName("username")] public string UserName { [System.Runtime.CompilerServices.NullableContext(2)] get { return _username ?? string.Empty; } [System.Runtime.CompilerServices.NullableContext(2)] set { _username = value; } } [System.Runtime.CompilerServices.Nullable(2)] [DomName("password")] public string Password { [System.Runtime.CompilerServices.NullableContext(2)] get { return _password ?? string.Empty; } [System.Runtime.CompilerServices.NullableContext(2)] set { _password = value; } } public string Data => _schemeData; [System.Runtime.CompilerServices.Nullable(2)] public string Fragment { [System.Runtime.CompilerServices.NullableContext(2)] get { return _fragment; } [System.Runtime.CompilerServices.NullableContext(2)] set { if (value == null) _fragment = null; else ParseFragment(value, 0, value.Length); } } [DomName("hash")] public string Hash { get { if (!string.IsNullOrEmpty(_fragment)) return "#" + _fragment; return string.Empty; } set { if (string.IsNullOrEmpty(value)) Fragment = null; else if (value[0] == '#') { Fragment = value.Substring(1); } else { Fragment = value; } } } [DomName("host")] public string Host { get { return HostName + (string.IsNullOrEmpty(_port) ? string.Empty : (":" + _port)); } set { string text = value ?? string.Empty; ParseHostName(text, 0, text.Length, false, true); } } [DomName("hostname")] public string HostName { get { return _host; } set { string text = value ?? string.Empty; ParseHostName(text, 0, text.Length, true, false); } } [DomName("href")] public string Href { get { return Serialize(); } set { _error = ParseUrl(value ?? string.Empty, this); } } public string Path { get { return _path; } set { string text = value ?? string.Empty; ParsePath(text, 0, text.Length, true); } } [DomName("pathname")] public string PathName { get { return "/" + _path; } set { Path = value; } } [DomName("port")] public string Port { get { return _port; } set { string text = value ?? string.Empty; ParsePort(text, 0, text.Length, true); } } public string Scheme { get { return _scheme; } set { string text = value ?? string.Empty; ParseScheme(text, text.Length, true); } } [DomName("protocol")] public string Protocol { get { return _scheme + ":"; } set { Scheme = value; } } [System.Runtime.CompilerServices.Nullable(2)] public string Query { [System.Runtime.CompilerServices.NullableContext(2)] get { return _query; } [System.Runtime.CompilerServices.NullableContext(2)] set { if (value == null) { _query = null; _params?.Reset(); } else ParseQuery(value, 0, value.Length, true, false); } } [DomName("search")] public string Search { get { if (!string.IsNullOrEmpty(_query)) return "?" + _query; return string.Empty; } set { if (string.IsNullOrEmpty(value)) Query = null; else if (value[0] == '?') { Query = value.Substring(1); } else { Query = value; } } } [DomName("searchParams")] public UrlSearchParams SearchParams { get { return _params ?? (_params = new UrlSearchParams(this)); } } private Url(string scheme, string host, string port) { _schemeData = string.Empty; _path = string.Empty; _scheme = scheme; _host = host; _port = port; _relative = ProtocolNames.IsRelative(_scheme); } [System.Runtime.CompilerServices.NullableContext(0)] [DomConstructor] public Url(string url, string baseAddress = null) { if (baseAddress != null) { Url baseUrl = new Url(baseAddress); _error = ParseUrl(url, baseUrl); } else _error = ParseUrl(url, null); } [System.Runtime.CompilerServices.NullableContext(0)] public Url(string address) { _error = ParseUrl(address, null); } [System.Runtime.CompilerServices.NullableContext(0)] public Url(Url baseAddress, string relativeAddress) { _error = ParseUrl(relativeAddress, baseAddress); } public Url(Url address) { _fragment = address._fragment; _query = address._query; _path = address._path; _scheme = address._scheme; _port = address._port; _host = address._host; _username = address._username; _password = address._password; _relative = address._relative; _schemeData = address._schemeData; } public static Url Create(string address) { return new Url(address); } public static Url Convert(Uri uri) { return new Url(uri.OriginalString); } public override int GetHashCode() { return (((((((((((((((((_fragment != null) ? StringComparer.Ordinal.GetHashCode(_fragment) : 0) * 397) ^ ((_query != null) ? StringComparer.Ordinal.GetHashCode(_query) : 0)) * 397) ^ ((_path != null) ? StringComparer.Ordinal.GetHashCode(_path) : 0)) * 397) ^ ((_scheme != null) ? StringComparer.OrdinalIgnoreCase.GetHashCode(_scheme) : 0)) * 397) ^ ((_port != null) ? StringComparer.Ordinal.GetHashCode(_port) : 0)) * 397) ^ ((_host != null) ? StringComparer.OrdinalIgnoreCase.GetHashCode(_host) : 0)) * 397) ^ ((_username != null) ? StringComparer.Ordinal.GetHashCode(_username) : 0)) * 397) ^ ((_password != null) ? StringComparer.Ordinal.GetHashCode(_password) : 0)) * 397) ^ ((_schemeData != null) ? StringComparer.Ordinal.GetHashCode(_schemeData) : 0); } [System.Runtime.CompilerServices.NullableContext(2)] public override bool Equals(object obj) { if (this != obj) { Url url = obj as Url; if (url != null) return Equals(url); return false; } return true; } [System.Runtime.CompilerServices.NullableContext(2)] public bool Equals(Url other) { if (other != null && _fragment.Is(other._fragment) && _query.Is(other._query) && _path.Is(other._path) && _scheme.Isi(other._scheme) && _port.Is(other._port) && _host.Isi(other._host) && _username.Is(other._username) && _password.Is(other._password)) return _schemeData.Is(other._schemeData); return false; } public static implicit operator Uri(Url value) { return new Uri(value.Serialize(), (!value.IsRelative) ? UriKind.Absolute : UriKind.Relative); } [DomName("toJSON")] public string ToJson() { return Serialize(); } public override string ToString() { return Serialize(); } private string Serialize() { StringBuilder stringBuilder = StringBuilderPool.Obtain(); if (!string.IsNullOrEmpty(_scheme)) stringBuilder.Append(_scheme).Append(':'); if (_relative) { if (!string.IsNullOrEmpty(_host) || !string.IsNullOrEmpty(_scheme)) { stringBuilder.Append('/').Append('/'); if (!string.IsNullOrEmpty(_username) || _password != null) { stringBuilder.Append(_username); if (_password != null) stringBuilder.Append(':').Append(_password); stringBuilder.Append('@'); } stringBuilder.Append(_host); if (!string.IsNullOrEmpty(_port)) stringBuilder.Append(':').Append(_port); stringBuilder.Append('/'); } stringBuilder.Append(_path); } else stringBuilder.Append(_schemeData); if (_query != null) stringBuilder.Append('?').Append(_query); if (_fragment != null) stringBuilder.Append('#').Append(_fragment); return stringBuilder.ToPool(); } private bool ParseUrl(string input, [System.Runtime.CompilerServices.Nullable(2)] Url baseUrl = null) { Reset(baseUrl ?? DefaultBase); string text = NormalizeInput(input); int length = text.Length; return !ParseScheme(text, length, false); } private void Reset(Url baseUrl) { _schemeData = string.Empty; _scheme = baseUrl._scheme; _host = baseUrl._host; _path = baseUrl._path; _query = baseUrl._query; _port = baseUrl._port; _relative = ProtocolNames.IsRelative(_scheme); } private bool ParseScheme(string input, int length, bool onlyScheme = false) { if (length > 0 && input[0].IsLetter()) { for (int i = 1; i < length; i++) { char c = input[i]; if (!c.IsAlphanumericAscii()) { switch (c) { case '+': case '-': case '.': break; case ':': goto IL_004f; default: goto IL_0184; } } continue; IL_004f: string scheme = _scheme; _scheme = input.Substring(0, i).ToLowerInvariant(); if (!onlyScheme) { _relative = ProtocolNames.IsRelative(_scheme); if (_scheme.Is(ProtocolNames.File)) { _host = string.Empty; _port = string.Empty; _query = null; return RelativeState(input, i + 1, length); } if (!_relative) { _host = string.Empty; _port = string.Empty; _path = string.Empty; _query = null; return ParseSchemeData(input, i + 1, length); } if (_scheme.Is(scheme)) { if (++i < length) { c = input[i]; if (c == '/' && i + 2 < length && input[i + 1] == '/') return IgnoreSlashesState(input, i + 2, length); return RelativeState(input, i, length); } return false; } if (i + 1 < length && input[++i] == '/' && ++i < length && input[i] == '/') i++; return IgnoreSlashesState(input, i, length); } return true; } } goto IL_0184; IL_0184: if (!onlyScheme) return RelativeState(input, 0, length); return false; } private bool ParseSchemeData(string input, int index, int length) { StringBuilder stringBuilder = StringBuilderPool.Obtain(); while (index < length) { char c = input[index]; switch (c) { case '?': _schemeData = stringBuilder.ToPool(); return ParseQuery(input, index + 1, length, false, false); case '#': _schemeData = stringBuilder.ToPool(); return ParseFragment(input, index + 1, length); case '%': if (index + 2 < length && input[index + 1].IsHex() && input[index + 2].IsHex()) { stringBuilder.Append(input[index++]); stringBuilder.Append(input[index++]); stringBuilder.Append(input[index]); break; } goto default; default: if (c.IsInRange(32, 126)) stringBuilder.Append(c); break; } index++; } _schemeData = stringBuilder.ToPool(); return true; } private bool RelativeState(string input, int index, int length) { _relative = true; if (index != length) { switch (input[index]) { case '?': return ParseQuery(input, index + 1, length, false, false); case '#': return ParseFragment(input, index + 1, length); case '/': case '\\': if (index != length - 1) { char c2 = input[++index]; if ((c2 == '/' || c2 == '\\') ? true : false) { if (_scheme.Is(ProtocolNames.File)) return ParseFileHost(input, index + 1, length); return IgnoreSlashesState(input, index + 1, length); } if (_scheme.Is(ProtocolNames.File)) { _host = string.Empty; _port = string.Empty; } return ParsePath(input, index - 1, length, false); } return ParsePath(input, index, length, false); default: { bool flag = input[index].IsLetter() && _scheme.Is(ProtocolNames.File) && index + 1 < length; if (flag) { char c = input[index + 1]; bool flag2 = (c == '/' || c == ':') ? true : false; flag = flag2; } bool flag3 = flag; if (flag3) { bool flag2 = index + 2 == length; if (!flag2) { bool flag4; switch (input[index + 2]) { case '#': case '/': case '?': case '\\': flag4 = true; break; default: flag4 = false; break; } flag2 = flag4; } flag3 = flag2; } if (flag3) { _host = string.Empty; _path = string.Empty; _port = string.Empty; } return ParsePath(input, index, length, false); } } } return true; } private bool IgnoreSlashesState(string input, int index, int length) { while (index < length) { char c = input[index]; if ((c != '/' && c != '\\') || 1 == 0) return ParseAuthority(input, index, length); index++; } return false; } private bool ParseAuthority(string input, int index, int length) { int index2 = index; StringBuilder stringBuilder = StringBuilderPool.Obtain(); string text = null; string password = null; while (true) { if (index < length) { char c = input[index]; switch (c) { case '@': if (text == null) text = stringBuilder.ToString(); else password = stringBuilder.ToString(); _username = text; _password = password; stringBuilder.Append("%40"); index2 = index + 1; goto IL_0134; case ':': if (text != null) goto default; text = stringBuilder.ToString(); password = string.Empty; stringBuilder.Clear(); goto IL_0134; default: { if (c == '%' && index + 2 < length && input[index + 1].IsHex() && input[index + 2].IsHex()) stringBuilder.Append(input[index++]).Append(input[index++]).Append(input[index]); else { bool flag; switch (c) { case '#': case '/': case '?': case '\\': flag = true; break; default: flag = false; break; } if (flag) break; switch (c) { default: if (c.IsNormalPathCharacter()) goto case '#'; goto case ':'; case '#': case '?': stringBuilder.Append(c); break; case ':': index += Utf8PercentEncode(stringBuilder, input, index); break; } } goto IL_0134; } IL_0134: index++; continue; } } break; } stringBuilder.ReturnToPool(); return ParseHostName(input, index2, length, false, false); } private bool ParseFileHost(string input, int index, int length) { int num = index; _path = string.Empty; bool flag; while (index < length) { switch (input[index]) { case '#': case '/': case '?': case '\\': flag = true; break; default: flag = false; break; } if (flag) break; index++; } int num2 = index - num; flag = (num2 == 2 && input[num].IsLetter()); if (flag) { char c = input[num + 1]; bool flag2 = (c == ':' || c == '|') ? true : false; flag = flag2; } if (flag) return ParsePath(input, index - 2, length, false); if (num2 != 0 && !TrySanatizeHost(input, num, num2, out _host)) return false; return ParsePath(input, index, length, false); } private bool ParseHostName(string input, int index, int length, bool onlyHost = false, bool onlyPort = false) { bool flag = false; int num = index; while (index < length) { switch (input[index]) { case ']': flag = false; break; case '[': flag = true; break; case ':': if (!flag) { if (!TrySanatizeHost(input, num, index - num, out _host)) return false; if (!onlyHost) return ParsePort(input, index + 1, length, onlyPort); return true; } break; case '#': case '/': case '?': case '\\': { if (!TrySanatizeHost(input, num, index - num, out _host)) return false; bool flag2 = string.IsNullOrEmpty(_host); if (!onlyHost) { _port = string.Empty; if (ParsePath(input, index, length, false)) return !flag2; return false; } return !flag2; } } index++; } if (!TrySanatizeHost(input, num, index - num, out _host)) return false; if (!onlyHost) { _path = string.Empty; _port = string.Empty; _query = null; _fragment = null; _params?.Reset(); } return true; } private bool ParsePort(string input, int index, int length, bool onlyPort = false) { int num = index; while (index < length) { char c = input[index]; if (c == '?' || c == '/' || c == '\\' || c == '#') break; if (!c.IsDigit()) return false; index++; } _port = SanatizePort(input, num, index - num); if (PortNumbers.GetDefaultPort(_scheme) == _port) _port = string.Empty; if (!onlyPort) { _path = string.Empty; return ParsePath(input, index, length, false); } return true; } private bool ParsePath(string input, int index, int length, bool onlyPath = false) { int num = index; if (index < length && (input[index] == '/' || input[index] == '\\')) index++; List<string> list = new List<string>(); if (!onlyPath && !string.IsNullOrEmpty(_path) && index - num == 0) { string[] array = _path.Split(new char[1] { '/' }); if (array.Length > 1) { list.AddRange(array); list.RemoveAt(array.Length - 1); } } int count = list.Count; StringBuilder stringBuilder = StringBuilderPool.Obtain(); while (index <= length) { char c = (index == length) ? '￿' : input[index]; bool flag = !onlyPath && (c == '#' || c == '?'); if ((c == '￿' || c == '/' || c == '\\') | flag) { string text = stringBuilder.ToString(); bool flag2 = false; stringBuilder.Clear(); if (text.Isi(CurrentDirectoryAlternative)) text = CurrentDirectory; else if (text.Isi(UpperDirectoryAlternatives[0]) || text.Isi(UpperDirectoryAlternatives[1]) || text.Isi(UpperDirectoryAlternatives[2])) { text = UpperDirectory; } if (text.Is(UpperDirectory)) { if (list.Count > 0) list.RemoveAt(list.Count - 1); flag2 = true; } else if (!text.Is(CurrentDirectory)) { if (_scheme.Is(ProtocolNames.File) && list.Count == count && text.Length == 2 && text[0].IsLetter() && text[1] == '|') { text = text.Replace('|', ':'); list.Clear(); } list.Add(text); } else { flag2 = true; } if (flag2 && c != '/' && c != '\\') list.Add(string.Empty); if (flag) break; } else if (c == '%' && index + 2 < length && input[index + 1].IsHex() && input[index + 2].IsHex()) { stringBuilder.Append(input[index++]); stringBuilder.Append(input[index++]); stringBuilder.Append(input[index]); } else if (c.IsNormalPathCharacter()) { stringBuilder.Append(c); } else { index += Utf8PercentEncode(stringBuilder, input, index); } index++; } stringBuilder.ReturnToPool(); _path = string.Join("/", list); _query = null; if (index < length) { if (input[index] == '?') return ParseQuery(input, index + 1, length, false, false); return ParseFragment(input, index + 1, length); } return true; } internal bool ParseQuery(string input, int index, int length, bool onlyQuery = false, bool fromParams = false) { StringBuilder stringBuilder = StringBuilderPool.Obtain(); bool flag = false; while (index < length) { char c = input[index]; flag = (!onlyQuery && input[index] == '#'); if (flag) break; if (c.IsNormalQueryCharacter()) stringBuilder.Append(c); else index += Utf8PercentEncode(stringBuilder, input, index); index++; } _query = stringBuilder.ToPool(); if (!fromParams) _params?.ChangeTo(_query, true); if (!flag) return true; return ParseFragment(input, index + 1, length); } private bool ParseFragment(string input, int index, int length) { StringBuilder stringBuilder = StringBuilderPool.Obtain(); while (index < length) { char c = input[index]; if (c != 0 && c != '￿') stringBuilder.Append(c); index++; } _fragment = stringBuilder.ToPool(); return true; } private static string NormalizeInput(string input) { string text = input.Trim(C0ControlAndSpace); StringBuilder stringBuilder = StringBuilderPool.Obtain(); string text2 = text; foreach (char c in text2) { switch (c) { default: stringBuilder.Append(c); break; case '\t': case '\n': case '\r': break; } } return stringBuilder.ToPool(); } private static string Utf8PercentDecode(string source) { byte[] bytes = TextEncoding.Utf8.GetBytes(source); int num = bytes.Length; int num2 = 0; int num3 = 0; while (num2 < bytes.Length) { char c = (char)bytes[num2]; if (c == '%' && num2 + 2 < bytes.Length && ((char)bytes[num2 + 1]).IsHex() && ((char)bytes[num2 + 2]).IsHex()) { c = (char)(((char)bytes[num2 + 1]).FromHex() * 16 + ((char)bytes[num2 + 2]).FromHex()); num2 += 2; num -= 2; } bytes[num3] = (byte)c; num2++; num3++; } return TextEncoding.Utf8.GetString(bytes, 0, num); } private static int Utf8PercentEncode(StringBuilder buffer, string source, int index) { int num = (!char.IsSurrogatePair(source, index)) ? 1 : 2; byte[] bytes = TextEncoding.Utf8.GetBytes(source.Substring(index, num)); for (int i = 0; i < bytes.Length; i++) { buffer.Append('%').Append(bytes[i].ToString("X2")); } return num - 1; } private static bool TrySanatizeHost(string hostName, int start, int length, out string sanatizedHostName) { if (length == 0) { sanatizedHostName = string.Empty; return true; } if (length > 1 && hostName[start] == '[' && hostName[start + length - 1] == ']') { sanatizedHostName = hostName.Substring(start, length); return true; } string unicode = Utf8PercentDecode(hostName.Substring(start, length)); string ascii; try { ascii = DefaultIdnMapping.GetAscii(unicode); } catch (ArgumentException) { sanatizedHostName = hostName.Substring(start, length); return false; } StringBuilder stringBuilder = StringBuilderPool.Obtain(); string text = ascii; foreach (char c in text) { switch (c) { case '': case '\t': case '\n': case '\r': case ' ': case '#': case '%': case '/': case ':': case '?': case '@': case '[': case '\\': case ']': stringBuilder.ReturnToPool(); sanatizedHostName = hostName.Substring(start, length); return false; } stringBuilder.Append(char.ToLowerInvariant(c)); } sanatizedHostName = stringBuilder.ToPool(); return true; } private unsafe static string SanatizePort(string port, int start, int length) { <>c__DisplayClass104_0 <>c__DisplayClass104_ = default(<>c__DisplayClass104_0); <>c__DisplayClass104_.start = start; <>c__DisplayClass104_.length = length; <>c__DisplayClass104_.port = port; if (<>c__DisplayClass104_.length < 128) { int length2 = <>c__DisplayClass104_.length; return <SanatizePort>g__Go|104_0(new Span<char>(stackalloc byte[(int)checked(unchecked((ulong)(uint)length2) * 2)], length2), ref <>c__DisplayClass104_); } return <SanatizePort>g__Go|104_0(new char[<>c__DisplayClass104_.length], ref <>c__DisplayClass104_); } } }