public override bool TryFormat(Span <char> span) { var isCommentNull = string.IsNullOrWhiteSpace(Comment); var len = NameSize + RawValue.AsSpan().StringSizeWithQuoteReplacement(0) + 2; if (span.Length < EntrySizeInBytes) { return(false); } span.Slice(0, EntrySizeInBytes).Fill(' '); Name.AsSpan().CopyTo(span); //span[EqualsPos] = '='; if (!RawValue.AsSpan().TryGetCompatibleString(span.Slice(ValueStart), 0)) { span.Slice(0, EntrySizeInBytes).Fill(' '); return(false); } if (isCommentNull) { return(true); } Comment.AsSpan().CopyTo(span.Slice(len + 2)); span[len + 1] = '/'; return(true); }
public override bool TryFormat(Span <char> span) { if (span.Length < EntrySizeInBytes) { return(false); } var isCommentNull = string.IsNullOrWhiteSpace(Comment); span.Slice(0, EntrySizeInBytes).Fill(' '); Name.AsSpan().CopyTo(span); if (isCommentNull) { return(true); } //Comment.AsSpan().CopyTo(span.Slice(ValueStart)); // This accounts for the absence of `= ` in special keys Comment.AsSpan().CopyTo(span.Slice(ValueStart - 1)); return(true); }
// According to spec we must assume default values for attributes but still // keep in mind that we must not include them into the requests. // We also check the validity of all attributes based on the version and variant (read RFC) // // To work properly this function must be called after cookie construction with // default (response) URI AND setDefault == true // // Afterwards, the function can be called many times with other URIs and // setDefault == false to check whether this cookie matches given uri internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDomain, string localDomain, bool setDefault, bool shouldThrow) { string host = uri.Host; int port = uri.Port; string path = uri.AbsolutePath; bool valid = true; if (setDefault) { // Set Variant. If version is zero => reset cookie to Version0 style if (Version == 0) { variant = CookieVariant.Plain; } else if (Version == 1 && variant == CookieVariant.Unknown) { // Since we don't expose Variant to an app, set it to Default variant = CookieVariant.Default; } m_cookieVariant = variant; } // Check the name if (string.IsNullOrEmpty(m_name) || m_name.StartsWith('$') || m_name.StartsWith(' ') || m_name.EndsWith(' ') || m_name.AsSpan().IndexOfAny(ReservedToName) >= 0) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, "Name", m_name ?? "<null>")); } return(false); } // Check the value if (m_value == null || (!(m_value.Length > 2 && m_value.StartsWith('\"') && m_value.EndsWith('\"')) && m_value.AsSpan().IndexOfAny(';', ',') >= 0)) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, "Value", m_value ?? "<null>")); } return(false); } // Check Comment syntax if (Comment != null && !(Comment.Length > 2 && Comment.StartsWith('\"') && Comment.EndsWith('\"')) && (Comment.AsSpan().IndexOfAny(';', ',') >= 0)) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.CommentAttributeName, Comment)); } return(false); } // Check Path syntax if (Path != null && !(Path.Length > 2 && Path.StartsWith('\"') && Path.EndsWith('\"')) && (Path.AsSpan().IndexOfAny(';', ',') != -1)) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PathAttributeName, Path)); } return(false); } // Check/set domain // // If domain is implicit => assume a) uri is valid, b) just set domain to uri hostname. if (setDefault && m_domain_implicit) { m_domain = host; } else { if (!m_domain_implicit) { // Forwarding note: If Uri.Host is of IP address form then the only supported case // is for IMPLICIT domain property of a cookie. // The code below (explicit cookie.Domain value) will try to parse Uri.Host IP string // as a fqdn and reject the cookie. // Aliasing since we might need the KeyValue (but not the original one). string domain = m_domain; // Syntax check for Domain charset plus empty string. if (!DomainCharsTest(domain)) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.DomainAttributeName, domain ?? "<null>")); } return(false); } // Domain must start with '.' if set explicitly. if (domain[0] != '.') { domain = '.' + domain; } int host_dot = host.IndexOf('.'); // First quick check is for pushing a cookie into the local domain. if (isLocalDomain && string.Equals(localDomain, domain, StringComparison.OrdinalIgnoreCase)) { valid = true; } else if (domain.IndexOf('.', 1, domain.Length - 2) == -1) { // A single label domain is valid only if the domain is exactly the same as the host specified in the URI. if (!IsDomainEqualToHost(domain, host)) { valid = false; } } else if (variant == CookieVariant.Plain) { // We distinguish between Version0 cookie and other versions on domain issue. // According to Version0 spec a domain must be just a substring of the hostname. if (!IsDomainEqualToHost(domain, host)) { if (host.Length <= domain.Length || (string.Compare(host, host.Length - domain.Length, domain, 0, domain.Length, StringComparison.OrdinalIgnoreCase) != 0)) { valid = false; } } } else if (host_dot == -1 || domain.Length != host.Length - host_dot || (string.Compare(host, host_dot, domain, 0, domain.Length, StringComparison.OrdinalIgnoreCase) != 0)) { // Starting from the first dot, the host must match the domain. // // For null hosts, the host must match the domain exactly. if (!IsDomainEqualToHost(domain, host)) { valid = false; } } if (valid) { m_domainKey = domain.ToLowerInvariant(); } } else { // For implicitly set domain AND at the set_default == false time // we simply need to match uri.Host against m_domain. if (!string.Equals(host, m_domain, StringComparison.OrdinalIgnoreCase)) { valid = false; } } if (!valid) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.DomainAttributeName, m_domain)); } return(false); } } // Check/Set Path if (setDefault && m_path_implicit) { // This code assumes that the URI path is always valid and contains at least one '/'. switch (m_cookieVariant) { case CookieVariant.Plain: // As per RFC6265 5.1.4. (https://tools.ietf.org/html/rfc6265#section-5.1.4): // | 2. If the uri-path is empty or if the first character of the uri- // | path is not a %x2F ("/") character, output %x2F ("/") and skip // | the remaining steps. // | 3. If the uri-path contains no more than one %x2F ("/") character, // | output %x2F ("/") and skip the remaining step. // Note: Normally Uri.AbsolutePath contains at least one "/" after parsing, // but it's possible construct Uri with an empty path using a custom UriParser int lastSlash; if (!path.StartsWith('/') || (lastSlash = path.LastIndexOf('/')) == 0) { m_path = "/"; break; } // | 4. Output the characters of the uri-path from the first character up // | to, but not including, the right-most %x2F ("/"). m_path = path.Substring(0, lastSlash); break; case CookieVariant.Rfc2109: m_path = path.Substring(0, path.LastIndexOf('/')); // May be empty break; case CookieVariant.Rfc2965: default: // NOTE: this code is not resilient against future versions with different 'Path' semantics. m_path = path.Substring(0, path.LastIndexOf('/') + 1); break; } } // Set the default port if Port attribute was present but had no value. if (setDefault && (m_port_implicit == false && m_port.Length == 0)) { m_port_list = new int[1] { port }; } if (m_port_implicit == false) { // Port must match against the one from the uri. valid = false; foreach (int p in m_port_list !) { if (p == port) { valid = true; break; } } if (!valid) { if (shouldThrow) { throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PortAttributeName, m_port)); } return(false); } } return(true); }