private async Task<IDictionary<string, string[]>> ReadHeadersAsync(CancellationToken cancellationToken)
        {
            int totalSize = 0;
            var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
            var line = await _stream.ReadLineAsync(HeaderLengthLimit, cancellationToken);
            while (!string.IsNullOrEmpty(line))
            {
                totalSize += line.Length;
                if (totalSize > TotalHeaderSizeLimit)
                {
                    throw new InvalidOperationException("Total header size limit exceeded: " + TotalHeaderSizeLimit);
                }
                int splitIndex = line.IndexOf(':');
                Debug.Assert(splitIndex > 0, "Invalid header line: " + line);
                if (splitIndex >= 0)
                {
                    var name = line.Substring(0, splitIndex);
                    var value = line.Substring(splitIndex + 1, line.Length - splitIndex - 1).Trim();
                    accumulator.Append(name, value);
                }
                line = await _stream.ReadLineAsync(HeaderLengthLimit, cancellationToken);
            }

            return accumulator.GetResults();
        }
        /// <summary>
        /// Parse a query string into its component key and value parts.
        /// </summary>
        /// <param name="text">The raw query string value, with or without the leading '?'.</param>
        /// <returns>A collection of parsed keys and values.</returns>
        public static IDictionary<string, string[]> ParseQuery(string queryString)
        {
            if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?')
            {
                queryString = queryString.Substring(1);
            }
            var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);

            int textLength = queryString.Length;
            int equalIndex = queryString.IndexOf('=');
            if (equalIndex == -1)
            {
                equalIndex = textLength;
            }
            int scanIndex = 0;
            while (scanIndex < textLength)
            {
                int delimiterIndex = queryString.IndexOf('&', scanIndex);
                if (delimiterIndex == -1)
                {
                    delimiterIndex = textLength;
                }
                if (equalIndex < delimiterIndex)
                {
                    while (scanIndex != equalIndex && char.IsWhiteSpace(queryString[scanIndex]))
                    {
                        ++scanIndex;
                    }
                    string name = queryString.Substring(scanIndex, equalIndex - scanIndex);
                    string value = queryString.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1);
                    accumulator.Append(
                        Uri.UnescapeDataString(name.Replace('+', ' ')),
                        Uri.UnescapeDataString(value.Replace('+', ' ')));
                    equalIndex = queryString.IndexOf('=', delimiterIndex);
                    if (equalIndex == -1)
                    {
                        equalIndex = textLength;
                    }
                }
                scanIndex = delimiterIndex + 1;
            }

            return accumulator.GetResults();
        }
        public async Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken)
        {
            if (Form != null)
            {
                return Form;
            }

            if (!HasFormContentType)
            {
                throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType);
            }

            cancellationToken.ThrowIfCancellationRequested();

            _request.EnableRewind();

            IDictionary<string, StringValues> formFields = null;
            var files = new FormFileCollection();

            // Some of these code paths use StreamReader which does not support cancellation tokens.
            using (cancellationToken.Register(_request.HttpContext.Abort))
            {
                var contentType = ContentType;
                // Check the content-type
                if (HasApplicationFormContentType(contentType))
                {
                    var encoding = FilterEncoding(contentType.Encoding);
                    formFields = await FormReader.ReadFormAsync(_request.Body, encoding, cancellationToken);
                }
                else if (HasMultipartFormContentType(contentType))
                {
                    var formAccumulator = new KeyValueAccumulator();

                    var boundary = GetBoundary(contentType);
                    var multipartReader = new MultipartReader(boundary, _request.Body);
                    var section = await multipartReader.ReadNextSectionAsync(cancellationToken);
                    while (section != null)
                    {
                        var headers = new HeaderDictionary(section.Headers);
                        ContentDispositionHeaderValue contentDisposition;
                        ContentDispositionHeaderValue.TryParse(headers[HeaderNames.ContentDisposition], out contentDisposition);
                        if (HasFileContentDisposition(contentDisposition))
                        {
                            // Find the end
                            await section.Body.DrainAsync(cancellationToken);

                            var file = new FormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length)
                            {
                                Headers = headers,
                            };
                            files.Add(file);
                        }
                        else if (HasFormDataContentDisposition(contentDisposition))
                        {
                            // Content-Disposition: form-data; name="key"
                            //
                            // value

                            var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
                            MediaTypeHeaderValue mediaType;
                            MediaTypeHeaderValue.TryParse(headers[HeaderNames.ContentType], out mediaType);
                            var encoding = FilterEncoding(mediaType?.Encoding);
                            using (var reader = new StreamReader(section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true))
                            {
                                var value = await reader.ReadToEndAsync();
                                formAccumulator.Append(key, value);
                            }
                        }
                        else
                        {
                            System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + headers[HeaderNames.ContentDisposition]);
                        }

                        section = await multipartReader.ReadNextSectionAsync(cancellationToken);
                    }

                    formFields = formAccumulator.GetResults();
                }
            }

            // Rewind so later readers don't have to.
            _request.Body.Seek(0, SeekOrigin.Begin);

            Form = new FormCollection(formFields, files);
            return Form;
        }
        /// <summary>
        /// Parses an HTTP form body.
        /// </summary>
        /// <param name="stream">The HTTP form body to parse.</param>
        /// <returns>The collection containing the parsed HTTP form body.</returns>
        public static async Task<IDictionary<string, string[]>> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
        {
            var reader = new FormReader(stream, encoding);

            var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
            var pair = await reader.ReadNextPairAsync(cancellationToken);
            while (pair.HasValue)
            {
                accumulator.Append(pair.Value.Key, pair.Value.Value);
                pair = await reader.ReadNextPairAsync(cancellationToken);
            }

            return accumulator.GetResults();
        }
        /// <summary>
        /// Parses text from an HTTP form body.
        /// </summary>
        /// <param name="text">The HTTP form body to parse.</param>
        /// <returns>The collection containing the parsed HTTP form body.</returns>
        public static IDictionary<string, string[]> ReadForm(string text)
        {
            var reader = new FormReader(text);

            var accumulator = new KeyValueAccumulator<string, string>(StringComparer.OrdinalIgnoreCase);
            var pair = reader.ReadNextPair();
            while (pair.HasValue)
            {
                accumulator.Append(pair.Value.Key, pair.Value.Value);
                pair =  reader.ReadNextPair();
            }

            return accumulator.GetResults();
        }
        /// <summary>
        /// Parse a query string into its component key and value parts.
        /// </summary>
        /// <param name="queryString">The raw query string value, with or without the leading '?'.</param>
        /// <returns>A collection of parsed keys and values, null if there are no entries.</returns>
        public static Dictionary<string, StringValues> ParseNullableQuery(string queryString)
        {
            var accumulator = new KeyValueAccumulator();

            if (string.IsNullOrEmpty(queryString) || queryString == "?")
            {
                return null;
            }

            int scanIndex = 0;
            if (queryString[0] == '?')
            {
                scanIndex = 1;
            }


            int textLength = queryString.Length;
            int equalIndex = queryString.IndexOf('=');
            if (equalIndex == -1)
            {
                equalIndex = textLength;
            }
            while (scanIndex < textLength)
            {
                int delimiterIndex = queryString.IndexOf('&', scanIndex);
                if (delimiterIndex == -1)
                {
                    delimiterIndex = textLength;
                }
                if (equalIndex < delimiterIndex)
                {
                    while (scanIndex != equalIndex && char.IsWhiteSpace(queryString[scanIndex]))
                    {
                        ++scanIndex;
                    }
                    string name = queryString.Substring(scanIndex, equalIndex - scanIndex);
                    string value = queryString.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1);
                    accumulator.Append(
                        Uri.UnescapeDataString(name.Replace('+', ' ')),
                        Uri.UnescapeDataString(value.Replace('+', ' ')));
                    equalIndex = queryString.IndexOf('=', delimiterIndex);
                    if (equalIndex == -1)
                    {
                        equalIndex = textLength;
                    }
                }
                scanIndex = delimiterIndex + 1;
            }

            if (!accumulator.HasValues)
            {
                return null;
            }

            return accumulator.GetResults();
        }
        /// <summary>
        /// Parses text from an HTTP form body.
        /// </summary>
        /// <param name="text">The HTTP form body to parse.</param>
        /// <returns>The collection containing the parsed HTTP form body.</returns>
        public static Dictionary<string, StringValues> ReadForm(string text)
        {
            var reader = new FormReader(text);

            var accumulator = new KeyValueAccumulator();
            var pair = reader.ReadNextPair();
            while (pair.HasValue)
            {
                accumulator.Append(pair.Value.Key, pair.Value.Value);
                pair = reader.ReadNextPair();
            }

            return accumulator.GetResults();
        }