Пример #1
0
        /// <summary>
        /// Appends a new response cookie to the Set-Cookie header. If the cookie is larger than the given size limit
        /// then it will be broken down into multiple cookies as follows:
        /// Set-Cookie: CookieName=chunks:3; path=/
        /// Set-Cookie: CookieNameC1=Segment1; path=/
        /// Set-Cookie: CookieNameC2=Segment2; path=/
        /// Set-Cookie: CookieNameC3=Segment3; path=/
        /// </summary>
        /// <param name="context"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="options"></param>
        public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            var webContext = context.Get <HttpContextBase>(typeof(HttpContextBase).FullName);

            if (webContext == null)
            {
                Fallback.AppendResponseCookie(context, key, value, options);
                return;
            }

            bool domainHasValue   = !string.IsNullOrEmpty(options.Domain);
            bool pathHasValue     = !string.IsNullOrEmpty(options.Path);
            bool expiresHasValue  = options.Expires.HasValue;
            bool sameSiteHasValue = options.SameSite.HasValue && SystemWebCookieManager.IsSameSiteAvailable;

            string escapedKey = Uri.EscapeDataString(key);
            string prefix     = escapedKey + "=";

            string suffix = string.Concat(
                !domainHasValue ? null : "; domain=",
                !domainHasValue ? null : options.Domain,
                !pathHasValue ? null : "; path=",
                !pathHasValue ? null : options.Path,
                !expiresHasValue ? null : "; expires=",
                !expiresHasValue ? null : options.Expires.Value.ToString("ddd, dd-MMM-yyyy HH:mm:ss \\G\\M\\T", CultureInfo.InvariantCulture),
                !options.Secure ? null : "; secure",
                !options.HttpOnly ? null : "; HttpOnly",
                !sameSiteHasValue ? null : "; SameSite=",
                !sameSiteHasValue ? null : GetStringRepresentationOfSameSite(options.SameSite.Value)
                );

            value = value ?? string.Empty;
            bool quoted = false;

            if (IsQuoted(value))
            {
                quoted = true;
                value  = RemoveQuotes(value);
            }
            string escapedValue = Uri.EscapeDataString(value);

            // Normal cookie
            if (!ChunkSize.HasValue || ChunkSize.Value > prefix.Length + escapedValue.Length + suffix.Length + (quoted ? 2 : 0))
            {
                var cookie = new HttpCookie(escapedKey, escapedValue);
                SetOptions(cookie, options, domainHasValue, pathHasValue, expiresHasValue);

                webContext.Response.AppendCookie(cookie);
            }
            else if (ChunkSize.Value < prefix.Length + suffix.Length + (quoted ? 2 : 0) + 10)
            {
                // 10 is the minimum data we want to put in an individual cookie, including the cookie chunk identifier "CXX".
                // No room for data, we can't chunk the options and name
                throw new InvalidOperationException(Resources.Exception_CookieLimitTooSmall);
            }
            else
            {
                // Break the cookie down into multiple cookies.
                // Key = CookieName, value = "Segment1Segment2Segment2"
                // Set-Cookie: CookieName=chunks:3; path=/
                // Set-Cookie: CookieNameC1="Segment1"; path=/
                // Set-Cookie: CookieNameC2="Segment2"; path=/
                // Set-Cookie: CookieNameC3="Segment3"; path=/
                int dataSizePerCookie = ChunkSize.Value - prefix.Length - suffix.Length - (quoted ? 2 : 0) - 3; // Budget 3 chars for the chunkid.
                int cookieChunkCount  = (int)Math.Ceiling(escapedValue.Length * 1.0 / dataSizePerCookie);

                var cookie = new HttpCookie(escapedKey, "chunks:" + cookieChunkCount.ToString(CultureInfo.InvariantCulture));
                SetOptions(cookie, options, domainHasValue, pathHasValue, expiresHasValue);

                webContext.Response.AppendCookie(cookie);

                int offset = 0;
                for (int chunkId = 1; chunkId <= cookieChunkCount; chunkId++)
                {
                    int    remainingLength = escapedValue.Length - offset;
                    int    length          = Math.Min(dataSizePerCookie, remainingLength);
                    string segment         = escapedValue.Substring(offset, length);
                    offset += length;

                    cookie = new HttpCookie(escapedKey + "C" + chunkId.ToString(CultureInfo.InvariantCulture), quoted ? Quote(segment) : segment);
                    SetOptions(cookie, options, domainHasValue, pathHasValue, expiresHasValue);

                    webContext.Response.AppendCookie(cookie);
                }
            }
        }