private unsafe ParsingError PrivateParseMinimal()
{
ushort idx = (ushort)(_flags & Flags.IndexMask);
ushort length = (ushort)_string.Length;
string newHost = null; // stores newly parsed host when original strings are being switched
// Means a custom UriParser did call "base" InitializeAndValidate()
_flags &= ~(Flags.IndexMask | Flags.UserDrivenParsing);
//STEP2: Parse up to the port
fixed (char* pUriString = ((_iriParsing &&
((_flags & Flags.HasUnicode) != 0) &&
((_flags & Flags.HostUnicodeNormalized) == 0)) ? _originalUnicodeString : _string))
{
// Cut trailing spaces in m_String
if (length > idx && UriHelper.IsLWS(pUriString[length - 1]))
{
--length;
while (length != idx && UriHelper.IsLWS(pUriString[--length]))
;
++length;
}
// Old Uri parser tries to figure out on a DosPath in all cases.
// Hence http://c:/ is treated as DosPath without the host while it should be a host "c", port 80
//
// This block is compatible with Old Uri parser in terms it will look for the DosPath if the scheme
// syntax allows both empty hostnames and DosPath
//
if (_syntax.IsAllSet(UriSyntaxFlags.AllowEmptyHost | UriSyntaxFlags.AllowDOSPath)
&& NotAny(Flags.ImplicitFile) && (idx + 1 < length))
{
char c;
ushort i = (ushort)idx;
// V1 Compat: Allow _compression_ of > 3 slashes only for File scheme.
// This will skip all slashes and if their number is 2+ it sets the AuthorityFound flag
for (; i < length; ++i)
{
if (!((c = pUriString[i]) == '\\' || c == '/'))
break;
}
if (_syntax.InFact(UriSyntaxFlags.FileLikeUri) || i - idx <= 3)
{
// if more than one slash after the scheme, the authority is present
if (i - idx >= 2)
{
_flags |= Flags.AuthorityFound;
}
// DOS-like path?
if (i + 1 < (ushort)length && ((c = pUriString[i + 1]) == ':' || c == '|') &&
UriHelper.IsAsciiLetter(pUriString[i]))
{
if (i + 2 >= (ushort)length || ((c = pUriString[i + 2]) != '\\' && c != '/'))
{
// report an error but only for a file: scheme
if (_syntax.InFact(UriSyntaxFlags.FileLikeUri))
return ParsingError.MustRootedPath;
}
else
{
// This will set IsDosPath
_flags |= Flags.DosPath;
if (_syntax.InFact(UriSyntaxFlags.MustHaveAuthority))
{
// when DosPath found and Authority is required, set this flag even if Authority is empty
_flags |= Flags.AuthorityFound;
}
if (i != idx && i - idx != 2)
{
//This will remember that DosPath is rooted
idx = (ushort)(i - 1);
}
else
{
idx = i;
}
}
}
else if (_syntax.InFact(UriSyntaxFlags.FileLikeUri) && (i - idx >= 2 && i - idx != 3 &&
i < length && pUriString[i] != '?' && pUriString[i] != '#'))
{
// V1.0 did not support file:///, fixing it with minimal behavior change impact
// Only FILE scheme may have UNC Path flag set
_flags |= Flags.UncPath;
idx = i;
}
}
}
//
//STEP 1.5 decide on the Authority component
//
if ((_flags & (Flags.UncPath | Flags.DosPath)) != 0)
{
}
else if ((idx + 2) <= length)
{
char first = pUriString[idx];
char second = pUriString[idx + 1];
if (_syntax.InFact(UriSyntaxFlags.MustHaveAuthority))
{
// (V1.0 compatibility) This will allow http:\\ http:\/ http:/\
if ((first == '/' || first == '\\') && (second == '/' || second == '\\'))
{
_flags |= Flags.AuthorityFound;
idx += 2;
}
else
{
return ParsingError.BadAuthority;
}
}
else if (_syntax.InFact(UriSyntaxFlags.OptionalAuthority) && (InFact(Flags.AuthorityFound) ||
(first == '/' && second == '/')))
{
_flags |= Flags.AuthorityFound;
idx += 2;
}
else if (_syntax.NotAny(UriSyntaxFlags.MailToLikeUri))
{
// There is no Authority component, save the Path index
// Note: mailto is the only guy who is treated specially, should be not.
_flags |= ((Flags)idx | Flags.UnknownHostType);
return ParsingError.None;
}
}
else if (_syntax.InFact(UriSyntaxFlags.MustHaveAuthority))
{
return ParsingError.BadAuthority;
}
else if (_syntax.NotAny(UriSyntaxFlags.MailToLikeUri))
{
// There is no Authority component, save the Path index
// mailto is treated specially.
_flags |= ((Flags)idx | Flags.UnknownHostType);
return ParsingError.None;
}
// vsmacros://c:\path\file
// Note that two slashes say there must be an Authority but instead the path goes
// Fro V1 compat the next block allow this case but not for schemes like http
if (InFact(Flags.DosPath))
{
_flags |= (((_flags & Flags.AuthorityFound) != 0) ? Flags.BasicHostType : Flags.UnknownHostType);
_flags |= (Flags)idx;
return ParsingError.None;
}
//STEP 2: Check the syntax of authority expecting at least one character in it
//
// Note here we do know that there is an authority in the string OR it's a DOS path
// We may find a userInfo and the port when parsing an authority
// Also we may find a registry based authority.
// We must ensure that known schemes do use a server-based authority
{
ParsingError err = ParsingError.None;
idx = CheckAuthorityHelper(pUriString, idx, (ushort)length, ref err, ref _flags, _syntax, ref newHost);
if (err != ParsingError.None)
return err;
// This will disallow '\' as the host terminator for any scheme that is not implicitFile or cannot have a Dos Path
if ((idx < (ushort)length && pUriString[idx] == '\\') && NotAny(Flags.ImplicitFile) &&
_syntax.NotAny(UriSyntaxFlags.AllowDOSPath))
{
return ParsingError.BadAuthorityTerminator;
}
}
// The Path (or Port) parsing index is reloaded on demand in CreateUriInfo when accessing a Uri property
_flags |= (Flags)idx;
// The rest of the string will be parsed on demand
// The Host/Authority is all checked, the type is known but the host value string
// is not created/canonicalized at this point.
}
if ((s_IdnScope != UriIdnScope.None) || _iriParsing)
PrivateParseMinimalIri(newHost, idx);
return ParsingError.None;
}