public override string ToString() { if (_username.Length == 0 && _password.Length > 0) { throw new UriFormatException(SR.net_uri_BadUserPassword); } if (_scheme.Length != 0) { UriParser?syntax = UriParser.GetSyntax(_scheme); if (syntax != null) { _schemeDelimiter = syntax.InFact(UriSyntaxFlags.MustHaveAuthority) || (_host.Length != 0 && syntax.NotAny(UriSyntaxFlags.MailToLikeUri) && syntax.InFact(UriSyntaxFlags.OptionalAuthority)) ? Uri.SchemeDelimiter : ":"; } else { _schemeDelimiter = _host.Length != 0 ? Uri.SchemeDelimiter : ":"; } } string result = _scheme.Length != 0 ? (_scheme + _schemeDelimiter) : string.Empty; return(result + _username + ((_password.Length > 0) ? (":" + _password) : string.Empty) + ((_username.Length > 0) ? "@" : string.Empty) + _host + (((_port != -1) && (_host.Length > 0)) ? (":" + _port.ToString()) : string.Empty) + (((_host.Length > 0) && (_path.Length != 0) && (_path[0] != '/')) ? "/" : string.Empty) + _path + _query + _fragment); }
//schemeStr must be in lower case! internal static UriParser FindOrFetchAsUnknownV1Syntax(string lwrCaseScheme) { // check may be other thread just added one UriParser?syntax = (UriParser?)s_table[lwrCaseScheme]; if (syntax != null) { return(syntax); } syntax = (UriParser?)s_tempTable[lwrCaseScheme]; if (syntax != null) { return(syntax); } lock (s_table) { if (s_tempTable.Count >= c_MaxCapacity) { s_tempTable = new Hashtable(c_InitialTableSize); } syntax = new BuiltInUriParser(lwrCaseScheme, NoDefaultPort, UnknownV1SyntaxFlags); s_tempTable[lwrCaseScheme] = syntax; return(syntax); } }
private static void FetchSyntax(UriParser syntax, string lwrCaseSchemeName, int defaultPort) { if (syntax.SchemeName.Length != 0) { throw new InvalidOperationException(SR.Format(SR.net_uri_NeedFreshParser, syntax.SchemeName)); } lock (s_table) { syntax._flags &= ~UriSyntaxFlags.V1_UnknownUri; UriParser?oldSyntax = (UriParser?)s_table[lwrCaseSchemeName]; if (oldSyntax != null) { throw new InvalidOperationException(SR.Format(SR.net_uri_AlreadyRegistered, oldSyntax.SchemeName)); } oldSyntax = (UriParser?)s_tempTable[syntax.SchemeName]; if (oldSyntax != null) { // optimization on schemeName, will try to keep the first reference lwrCaseSchemeName = oldSyntax._scheme; s_tempTable.Remove(lwrCaseSchemeName); } syntax.OnRegister(lwrCaseSchemeName, defaultPort); syntax._scheme = lwrCaseSchemeName; syntax.CheckSetIsSimpleFlag(); syntax._port = defaultPort; s_table[syntax.SchemeName] = syntax; } }
// // a Uri.TryCreate() method goes through here. // internal static Uri?CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException?e) { // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow // to be used here. if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) { throw new ArgumentException(SR.Format(SR.net_uri_InvalidUriKind, uriKind)); } UriParser? syntax = null; Flags flags = Flags.Zero; ParsingError err = ParseScheme(uriString, ref flags, ref syntax); if (dontEscape) { flags |= Flags.UserEscaped; } // We won't use User factory for these errors if (err != ParsingError.None) { // If it looks as a relative Uri, custom factory is ignored if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex) { return(new Uri((flags & Flags.UserEscaped), null, uriString)); } return(null); } // Cannot be relative Uri if came here Debug.Assert(syntax != null); Uri result = new Uri(flags, syntax, uriString); // Validate instance using ether built in or a user Parser try { result.InitializeUri(err, uriKind, out e); if (e == null) { result.DebugSetLeftCtor(); return(result); } return(null); } catch (UriFormatException ee) { Debug.Assert(!syntax !.IsSimple, "A UriPraser threw on InitializeAndValidate."); e = ee; // A precaution since custom Parser should never throw in this case. return(null); } }
// Should never be used except by the below method private Uri(Flags flags, UriParser?uriParser, string uri) { _flags = flags; _syntax = uriParser !; _string = uri; if (uriParser is null) { // Relative Uris are fully initialized after the call to this constructor // Absolute Uris will be initialized with a call to InitializeUri on the newly created instance DebugSetLeftCtor(); } }
// // Is a Uri scheme known to System.Uri? // public static bool IsKnownScheme(string schemeName) { ArgumentNullException.ThrowIfNull(schemeName); if (!Uri.CheckSchemeName(schemeName)) { throw new ArgumentOutOfRangeException(nameof(schemeName)); } UriParser?syntax = UriParser.GetSyntax(schemeName.ToLowerInvariant()); return(syntax != null && syntax.NotAny(UriSyntaxFlags.V1_UnknownUri)); }
// Should never be used except by the below method private Uri(Flags flags, UriParser?uriParser, string uri) { _flags = flags; _syntax = uriParser !; _string = uri; }
internal static unsafe void UnescapeString(char *pStr, int start, int end, ref ValueStringBuilder dest, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly) { dest.Append(pStr + start, end - start); return; } bool escapeReserved = false; bool iriParsing = Uri.IriParsingStatic(syntax) && ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.EscapeUnescape); for (int next = start; next < end;) { char ch = (char)0; for (; next < end; ++next) { if ((ch = pStr[next]) == '%') { if ((unescapeMode & UnescapeMode.Unescape) == 0) { // re-escape, don't check anything else escapeReserved = true; } else if (next + 2 < end) { ch = DecodeHexChars(pStr[next + 1], pStr[next + 2]); // Unescape a good sequence if full unescape is requested if (unescapeMode >= UnescapeMode.UnescapeAll) { if (ch == Uri.c_DummyChar) { if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.net_uri_BadString); } continue; } } // re-escape % from an invalid sequence else if (ch == Uri.c_DummyChar) { if ((unescapeMode & UnescapeMode.Escape) != 0) { escapeReserved = true; } else { continue; // we should throw instead but since v1.0 would just print '%' } } // Do not unescape '%' itself unless full unescape is requested else if (ch == '%') { next += 2; continue; } // Do not unescape a reserved char unless full unescape is requested else if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) { next += 2; continue; } // Do not unescape a dangerous char unless it's V1ToStringFlags mode else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && IsNotSafeForUnescape(ch)) { next += 2; continue; } else if (iriParsing && ((ch <= '\x9F' && IsNotSafeForUnescape(ch)) || (ch > '\x9F' && !IriHelper.CheckIriUnicodeRange(ch, isQuery)))) { // check if unenscaping gives a char outside iri range // if it does then keep it escaped next += 2; continue; } // unescape escaped char or escape % break; } else if (unescapeMode >= UnescapeMode.UnescapeAll) { if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.net_uri_BadString); } // keep a '%' as part of a bogus sequence continue; } else { escapeReserved = true; } // escape (escapeReserved==true) or otherwise unescape the sequence break; } else if ((unescapeMode & (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) == (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) { continue; } else if ((unescapeMode & UnescapeMode.Escape) != 0) { // Could actually escape some of the characters if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) { // found an unescaped reserved character -> escape it escapeReserved = true; break; } else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F'))) { // found an unescaped reserved character -> escape it escapeReserved = true; break; } } } //copy off previous characters from input while (start < next) { dest.Append(pStr[start++]); } if (next != end) { if (escapeReserved) { EscapeAsciiChar((byte)pStr[next], ref dest); escapeReserved = false; next++; } else if (ch <= 127) { dest.Append(ch); next += 3; } else { // Unicode int charactersRead = PercentEncodingHelper.UnescapePercentEncodedUTF8Sequence( pStr + next, end - next, ref dest, isQuery, iriParsing); Debug.Assert(charactersRead > 0); next += charactersRead; } start = next; } } }
internal static unsafe void UnescapeString(ReadOnlySpan <char> input, ref ValueStringBuilder dest, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { fixed(char *pStr = &MemoryMarshal.GetReference(input)) { UnescapeString(pStr, 0, input.Length, ref dest, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery); } }
// // This method will assume that any good Escaped Sequence will be unescaped in the output // - Assumes Dest.Length - detPosition >= end-start // - UnescapeLevel controls various modes of operation // - Any "bad" escape sequence will remain as is or '%' will be escaped. // - destPosition tells the starting index in dest for placing the result. // On return destPosition tells the last character + 1 position in the "dest" array. // - The control chars and chars passed in rsdvX parameters may be re-escaped depending on UnescapeLevel // - It is a RARE case when Unescape actually needs escaping some characters mentioned above. // For this reason it returns a char[] that is usually the same ref as the input "dest" value. // internal static unsafe void UnescapeString(string input, int start, int end, ref ValueStringBuilder dest, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { fixed(char *pStr = input) { UnescapeString(pStr, start, end, ref dest, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery); } }
internal static unsafe char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { ValueStringBuilder vsb = new ValueStringBuilder(dest.Length); vsb.Append(dest.AsSpan(0, destPosition)); UnescapeString(pStr, start, end, ref vsb, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery); if (vsb.Length > dest.Length) { dest = vsb.AsSpan().ToArray(); } else { vsb.AsSpan(destPosition).TryCopyTo(dest.AsSpan(destPosition)); } destPosition = vsb.Length; vsb.Dispose(); return(dest); }
internal static unsafe char[] UnescapeString(string input, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { fixed(char *pStr = input) { return(UnescapeString(pStr, start, end, dest, ref destPosition, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery)); } }
internal static unsafe char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser?syntax, bool isQuery) { byte[]? bytes = null; byte escapedReallocations = 0; bool escapeReserved = false; int next = start; bool iriParsing = Uri.IriParsingStatic(syntax) && ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.EscapeUnescape); char[]? unescapedChars = null; while (true) { // we may need to re-pin dest[] fixed(char *pDest = dest) { if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly) { while (start < end) { pDest[destPosition++] = pStr[start++]; } return(dest); } while (true) { char ch = (char)0; for (; next < end; ++next) { if ((ch = pStr[next]) == '%') { if ((unescapeMode & UnescapeMode.Unescape) == 0) { // re-escape, don't check anything else escapeReserved = true; } else if (next + 2 < end) { ch = EscapedAscii(pStr[next + 1], pStr[next + 2]); // Unescape a good sequence if full unescape is requested if (unescapeMode >= UnescapeMode.UnescapeAll) { if (ch == Uri.c_DummyChar) { if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.net_uri_BadString); } continue; } } // re-escape % from an invalid sequence else if (ch == Uri.c_DummyChar) { if ((unescapeMode & UnescapeMode.Escape) != 0) { escapeReserved = true; } else { continue; // we should throw instead but since v1.0 would just print '%' } } // Do not unescape '%' itself unless full unescape is requested else if (ch == '%') { next += 2; continue; } // Do not unescape a reserved char unless full unescape is requested else if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) { next += 2; continue; } // Do not unescape a dangerous char unless it's V1ToStringFlags mode else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && IsNotSafeForUnescape(ch)) { next += 2; continue; } else if (iriParsing && ((ch <= '\x9F' && IsNotSafeForUnescape(ch)) || (ch > '\x9F' && !IriHelper.CheckIriUnicodeRange(ch, isQuery)))) { // check if unenscaping gives a char outside iri range // if it does then keep it escaped next += 2; continue; } // unescape escaped char or escape % break; } else if (unescapeMode >= UnescapeMode.UnescapeAll) { if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) { // Should be a rare case where the app tries to feed an invalid escaped sequence throw new UriFormatException(SR.net_uri_BadString); } // keep a '%' as part of a bogus sequence continue; } else { escapeReserved = true; } // escape (escapeReserved==true) or otherwise unescape the sequence break; } else if ((unescapeMode & (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) == (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) { continue; } else if ((unescapeMode & UnescapeMode.Escape) != 0) { // Could actually escape some of the characters if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) { // found an unescaped reserved character -> escape it escapeReserved = true; break; } else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F'))) { // found an unescaped reserved character -> escape it escapeReserved = true; break; } } } //copy off previous characters from input while (start < next) { pDest[destPosition++] = pStr[start++]; } if (next != end) { if (escapeReserved) { //escape that char // Since this should be _really_ rare case, reallocate with constant size increase of 30 rsvd-type characters. if (escapedReallocations == 0) { escapedReallocations = 30; char[] newDest = new char[dest.Length + escapedReallocations * 3]; fixed(char *pNewDest = &newDest[0]) { for (int i = 0; i < destPosition; ++i) { pNewDest[i] = pDest[i]; } } dest = newDest; // re-pin new dest[] array goto dest_fixed_loop_break; } else { --escapedReallocations; EscapeAsciiChar(pStr[next], dest, ref destPosition); escapeReserved = false; start = ++next; continue; } } // unescaping either one Ascii or possibly multiple Unicode if (ch <= '\x7F') { //ASCII dest[destPosition++] = ch; next += 3; start = next; continue; } // Unicode int byteCount = 1; // lazy initialization of max size, will reuse the array for next sequences if ((object?)bytes == null) { bytes = new byte[end - next]; } bytes[0] = (byte)ch; next += 3; while (next < end) { // Check on exit criterion if ((ch = pStr[next]) != '%' || next + 2 >= end) { break; } // already made sure we have 3 characters in str ch = EscapedAscii(pStr[next + 1], pStr[next + 2]); //invalid hex sequence ? if (ch == Uri.c_DummyChar) { break; } // character is not part of a UTF-8 sequence ? else if (ch < '\x80') { break; } else { //a UTF-8 sequence bytes[byteCount++] = (byte)ch; next += 3; } } if (unescapedChars == null || unescapedChars.Length < bytes.Length) { unescapedChars = new char[bytes.Length]; } int charCount = s_noFallbackCharUTF8.GetChars(bytes, 0, byteCount, unescapedChars, 0); start = next; // match exact bytes // Do not unescape chars not allowed by Iri // need to check for invalid utf sequences that may not have given any chars MatchUTF8Sequence(pDest, dest, ref destPosition, unescapedChars.AsSpan(0, charCount), charCount, bytes, byteCount, isQuery, iriParsing); } if (next == end) { goto done; } } dest_fixed_loop_break :; } } done : return(dest); }
internal static bool IriParsingStatic(UriParser?syntax) { throw new NotImplementedException(); }