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(); }
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="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(); }
/// <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(); }