< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 265
Coverable lines: 265
Total lines: 411
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 98
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%110%
.ctor(...)0%220%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)0%12120%
.cctor()100%110%
Equals(...)0%220%
GetHashCode()100%110%
EncodeUserInfo(...)0%220%
SetFieldsFromUri()0%440%
ToString()0%32320%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.Uri/src/System/UriBuilder.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Text;
 8
 9namespace System
 10{
 11    public class UriBuilder
 12    {
 013        private string _scheme = "http";
 014        private string _username = string.Empty;
 015        private string _password = string.Empty;
 016        private string _host = "localhost";
 017        private int _port = -1;
 018        private string _path = "/";
 019        private string _query = string.Empty;
 020        private string _fragment = string.Empty;
 21
 022        private bool _changed = true;
 23        private Uri? _uri;
 24
 025        public UriBuilder()
 026        {
 027        }
 28
 029        public UriBuilder([StringSyntax(StringSyntaxAttribute.Uri)] string uri)
 030        {
 31            // setting allowRelative=true for a string like www.acme.org
 032            _uri = new Uri(uri, UriKind.RelativeOrAbsolute);
 33
 034            if (!_uri.IsAbsoluteUri)
 035            {
 036                _uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri);
 037            }
 38
 039            SetFieldsFromUri();
 040        }
 41
 042        public UriBuilder(Uri uri)
 043        {
 044            ArgumentNullException.ThrowIfNull(uri);
 45
 046            _uri = uri;
 047            SetFieldsFromUri();
 048        }
 49
 050        public UriBuilder(string? schemeName, string? hostName)
 051        {
 052            Scheme = schemeName;
 053            Host = hostName;
 054        }
 55
 56        public UriBuilder(string? scheme, string? host, int portNumber)
 057            : this(scheme, host)
 058        {
 059            Port = portNumber;
 060        }
 61
 62        public UriBuilder(string? scheme, string? host, int port, string? pathValue)
 063            : this(scheme, host, port)
 064        {
 065            Path = pathValue;
 066        }
 67
 68        public UriBuilder(string? scheme, string? host, int port, string? path, string? extraValue)
 069            : this(scheme, host, port, path)
 070        {
 071            if (!string.IsNullOrEmpty(extraValue))
 072            {
 073                if (extraValue[0] == '#')
 074                {
 075                    _fragment = extraValue;
 076                }
 077                else if (extraValue[0] == '?')
 078                {
 079                    int fragmentIndex = extraValue.IndexOf('#');
 080                    if (fragmentIndex == -1)
 081                    {
 082                        _query = extraValue;
 083                    }
 84                    else
 085                    {
 086                        _query = extraValue.Substring(0, fragmentIndex);
 087                        _fragment = extraValue.Substring(fragmentIndex);
 088                    }
 089                }
 90                else
 091                {
 092                    throw new ArgumentException(SR.Argument_ExtraNotValid, nameof(extraValue));
 93                }
 94
 095                if (_query.Length == 1)
 096                {
 097                    _query = string.Empty;
 098                }
 99
 0100                if (_fragment.Length == 1)
 0101                {
 0102                    _fragment = string.Empty;
 0103                }
 0104            }
 0105        }
 106
 107        [AllowNull]
 108        public string Scheme
 109        {
 0110            get => _scheme;
 111            set
 0112            {
 0113                value ??= string.Empty;
 114
 0115                if (value.Length != 0)
 0116                {
 0117                    if (!Uri.CheckSchemeName(value))
 0118                    {
 0119                        int index = value.IndexOf(':');
 0120                        if (index != -1)
 0121                        {
 0122                            value = value.Substring(0, index);
 0123                        }
 124
 0125                        if (!Uri.CheckSchemeName(value))
 0126                        {
 0127                            throw new ArgumentException(SR.net_uri_BadScheme, nameof(value));
 128                        }
 0129                    }
 130
 0131                    value = value.ToLowerInvariant();
 0132                }
 133
 0134                _scheme = value;
 0135                _changed = true;
 0136            }
 137        }
 138
 139        [AllowNull]
 140        public string UserName
 141        {
 0142            get => _username;
 143            set
 0144            {
 0145                _username = value ?? string.Empty;
 0146                _changed = true;
 0147            }
 148        }
 149
 150        [AllowNull]
 151        public string Password
 152        {
 0153            get => _password;
 154            set
 0155            {
 0156                _password = value ?? string.Empty;
 0157                _changed = true;
 0158            }
 159        }
 160
 161        /// <summary>
 162        /// Problematic characters that could result in the Host component escaping into other components like the Path.
 163        /// </summary>
 0164        private static readonly SearchValues<char> s_hostReservedChars = SearchValues.Create(@":/\?#@[]");
 0165        private static readonly SearchValues<char> s_hostReservedCharsExceptColon = SearchValues.Create(@"/\?#@[]");
 166
 167        [AllowNull]
 168        public string Host
 169        {
 0170            get => _host;
 171            set
 0172            {
 0173                if (!string.IsNullOrEmpty(value) && value.AsSpan().ContainsAny(s_hostReservedChars))
 0174                {
 0175                    if (value.Contains(':'))
 0176                    {
 0177                        if (!value.StartsWith('[') || !value.EndsWith(']'))
 0178                        {
 179                            // probable ipv6 address - Note: this is only supported for cases where the authority is ine
 0180                            value = $"[{value}]";
 0181                        }
 182
 0183                        if (value.AsSpan(1, value.Length - 2).ContainsAny(s_hostReservedCharsExceptColon))
 0184                        {
 185                            // Reject inputs like "[::]/path" or "::]/path".
 0186                            throw new ArgumentException(SR.net_uri_BadHostName, nameof(value));
 187                        }
 0188                    }
 189                    else
 0190                    {
 191                        // Reject inputs like "contoso.com/path" or "user@contoso.com".
 192                        // Nonsensical inputs will only be allowed by a custom parser with GenericUriParserOptions.Gener
 0193                        throw new ArgumentException(SR.net_uri_BadHostName, nameof(value));
 194                    }
 0195                }
 196
 0197                _host = value ?? string.Empty;
 0198                _changed = true;
 0199            }
 200        }
 201
 202        public int Port
 203        {
 0204            get => _port;
 205            set
 0206            {
 0207                ArgumentOutOfRangeException.ThrowIfLessThan(value, -1);
 0208                ArgumentOutOfRangeException.ThrowIfGreaterThan(value, 0xFFFF);
 0209                _port = value;
 0210                _changed = true;
 0211            }
 212        }
 213
 214        [AllowNull]
 215        public string Path
 216        {
 0217            get => _path;
 218            set
 0219            {
 0220                _path = string.IsNullOrEmpty(value)
 0221                    ? "/"
 0222                    : Uri.InternalEscapeString(value.Replace('\\', '/'));
 0223                _changed = true;
 0224            }
 225        }
 226
 227        [AllowNull]
 228        public string Query
 229        {
 0230            get => _query;
 231            set
 0232            {
 0233                if (!string.IsNullOrEmpty(value) && value[0] != '?')
 0234                {
 0235                    value = '?' + value;
 0236                }
 237
 0238                _query = value ?? string.Empty;
 0239                _changed = true;
 0240            }
 241        }
 242
 243        [AllowNull]
 244        public string Fragment
 245        {
 0246            get => _fragment;
 247            set
 0248            {
 0249                if (!string.IsNullOrEmpty(value) && value[0] != '#')
 0250                {
 0251                    value = '#' + value;
 0252                }
 253
 0254                _fragment = value ?? string.Empty;
 0255                _changed = true;
 0256            }
 257        }
 258
 259        public Uri Uri
 260        {
 261            get
 0262            {
 0263                if (_changed)
 0264                {
 0265                    _uri = new Uri(ToString());
 0266                    SetFieldsFromUri();
 0267                    _changed = false;
 0268                }
 269                else
 0270                {
 0271                    Debug.Assert(_uri is not null);
 0272                }
 0273                return _uri;
 0274            }
 275        }
 276
 0277        public override bool Equals([NotNullWhen(true)] object? rparam) => rparam is not null && Uri.Equals(rparam.ToStr
 278
 0279        public override int GetHashCode() => Uri.GetHashCode();
 280
 281        // The following characters ("/" / "\" / "?" / "#" / "@") are from the gen-delims group.
 282        // We have to escape them to avoid corrupting the rest of the Uri string.
 283        // Other characters like spaces or non-ASCII will be escaped by Uri, we can ignore them here.
 0284        private static readonly SearchValues<char> s_userInfoReservedChars =
 0285            SearchValues.Create(@"/\?#@");
 286
 287        private static string EncodeUserInfo(string input)
 0288        {
 0289            if (!input.AsSpan().ContainsAny(s_userInfoReservedChars))
 0290            {
 0291                return input;
 292            }
 293
 0294            return input
 0295                .Replace("/", "%2F", StringComparison.Ordinal)
 0296                .Replace(@"\", "%5C", StringComparison.Ordinal)
 0297                .Replace("?", "%3F", StringComparison.Ordinal)
 0298                .Replace("#", "%23", StringComparison.Ordinal)
 0299                .Replace("@", "%40", StringComparison.Ordinal);
 0300        }
 301
 302        private void SetFieldsFromUri()
 0303        {
 0304            Debug.Assert(_uri is not null);
 0305            _scheme = _uri.Scheme;
 0306            _host = _uri.Host;
 0307            _port = _uri.Port;
 0308            _path = _uri.AbsolutePath;
 0309            _query = _uri.Query;
 0310            _fragment = _uri.Fragment;
 311
 0312            string userInfo = _uri.UserInfo;
 313
 0314            if (userInfo.Length > 0)
 0315            {
 0316                int index = userInfo.IndexOf(':');
 317
 0318                if (index != -1)
 0319                {
 0320                    _password = userInfo.Substring(index + 1);
 0321                    _username = userInfo.Substring(0, index);
 0322                }
 323                else
 0324                {
 0325                    _username = userInfo;
 0326                }
 0327            }
 0328        }
 329
 330        public override string ToString()
 0331        {
 0332            if (UserName.Length == 0 && Password.Length != 0)
 0333            {
 0334                throw new UriFormatException(SR.net_uri_BadUserPassword);
 335            }
 336
 0337            var vsb = new ValueStringBuilder(stackalloc char[Uri.StackallocThreshold]);
 338
 0339            string scheme = Scheme;
 0340            string host = Host;
 341
 0342            if (scheme.Length != 0)
 0343            {
 0344                UriParser? syntax = UriParser.GetSyntax(scheme);
 345                string schemeDelimiter;
 0346                if (syntax is null)
 0347                {
 0348                    schemeDelimiter = host.Length == 0 ? ":" : Uri.SchemeDelimiter;
 0349                }
 350                else
 0351                {
 0352                    schemeDelimiter = syntax.InFact(UriSyntaxFlags.MustHaveAuthority)
 0353                        || (host.Length != 0 && syntax.NotAny(UriSyntaxFlags.MailToLikeUri) && syntax.InFact(UriSyntaxFl
 0354                            ? Uri.SchemeDelimiter
 0355                            : ":";
 0356                }
 357
 0358                vsb.Append(scheme);
 0359                vsb.Append(schemeDelimiter);
 0360            }
 361
 0362            string username = EncodeUserInfo(UserName);
 0363            if (username.Length != 0)
 0364            {
 0365                vsb.Append(username);
 366
 0367                string password = EncodeUserInfo(Password);
 0368                if (password.Length != 0)
 0369                {
 0370                    vsb.Append(':');
 0371                    vsb.Append(password);
 0372                }
 373
 0374                vsb.Append('@');
 0375            }
 376
 0377            if (host.Length != 0)
 0378            {
 0379                vsb.Append(host);
 380
 0381                if (_port != -1)
 0382                {
 0383                    vsb.Append(':');
 384
 385                    const int MaxUshortLength = 5;
 0386                    bool success = _port.TryFormat(vsb.AppendSpan(MaxUshortLength), out int charsWritten);
 0387                    Debug.Assert(success);
 0388                    vsb.Length -= MaxUshortLength - charsWritten;
 0389                }
 0390            }
 391
 0392            string path = Path;
 0393            if (path.Length != 0)
 0394            {
 0395                if (!path.StartsWith('/') && host.Length != 0)
 0396                {
 0397                    vsb.Append('/');
 0398                }
 399
 0400                vsb.Append(path);
 0401            }
 402
 0403            vsb.Append(Query);
 404
 0405            vsb.Append(Fragment);
 406
 0407            return vsb.ToString();
 0408        }
 409    }
 410}
 411