private async Task <Dictionary <string, StringValues> > ReadHeadersAsync(CancellationToken cancellationToken) { int totalSize = 0; var accumulator = new KeyValueAccumulator(); var line = await _stream.ReadLineAsync(HeadersLengthLimit - totalSize, cancellationToken); while (!string.IsNullOrEmpty(line)) { if (HeadersLengthLimit - totalSize < line.Length) { throw new InvalidDataException($"Multipart headers length limit {HeadersLengthLimit} exceeded."); } totalSize += line.Length; int splitIndex = line.IndexOf(':'); if (splitIndex <= 0) { throw new InvalidDataException($"Invalid header line: {line}"); } var name = line.Substring(0, splitIndex); var value = line.Substring(splitIndex + 1, line.Length - splitIndex - 1).Trim(); accumulator.Append(name, value); if (accumulator.KeyCount > HeadersCountLimit) { throw new InvalidDataException($"Multipart headers count limit {HeadersCountLimit} exceeded."); } line = await _stream.ReadLineAsync(HeadersLengthLimit - totalSize, cancellationToken); } return(accumulator.GetResults()); }
private static async Task <Dictionary <string, StringValues> > ReadHeadersAsync(BufferedReadStream stream, CancellationToken cancellationToken = default(CancellationToken)) { var totalSize = 0; var accumulator = default(KeyValueAccumulator); var line = await stream.ReadLineAsync(MultipartReader.DefaultHeadersLengthLimit, cancellationToken).ConfigureAwait(false); while (!string.IsNullOrEmpty(line)) { if (MultipartReader.DefaultHeadersLengthLimit - totalSize < line.Length) { throw new InvalidDataException( $"Multipart headers length limit {MultipartReader.DefaultHeadersLengthLimit} exceeded."); } totalSize += line.Length; var splitIndex = line.IndexOf(':'); if (splitIndex <= 0) { throw new InvalidDataException($"Invalid header line: {line}"); } var name = line.Substring(0, splitIndex); var value = line.Substring(splitIndex + 1, line.Length - splitIndex - 1).Trim(); accumulator.Append(name, value); if (accumulator.KeyCount > MultipartReader.DefaultHeadersCountLimit) { throw new InvalidDataException( $"Multipart headers count limit {MultipartReader.DefaultHeadersCountLimit} exceeded."); } line = await stream.ReadLineAsync(MultipartReader.DefaultHeadersLengthLimit - totalSize, cancellationToken).ConfigureAwait(false); } return(accumulator.GetResults()); }
/// <summary> /// Read the next line of text. /// </summary> /// <param name="stream">The stream to read from.</param> /// <param name="async"> /// Whether to invoke the operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Optional <see cref="CancellationToken"/> to propagate notifications /// that the operation should be cancelled. /// </param> /// <returns>The next line of text.</returns> internal static async Task <string> ReadLineAsync( this BufferedReadStream stream, bool async, CancellationToken cancellationToken) => async ? await stream.ReadLineAsync(BatchConstants.ResponseLineSize, cancellationToken).ConfigureAwait(false) : stream.ReadLine(BatchConstants.ResponseLineSize);
private static async Task <string[]> ReadResponseLineAsync(BufferedReadStream stream, CancellationToken cancellationToken) { var line = await stream.ReadLineAsync(MultipartReader.DefaultHeadersLengthLimit, cancellationToken) .ConfigureAwait(false); return(line.Split(SpaceArray, 3)); }
public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { if (_finished) { return(0); } PositionInnerStream(); if (!await _innerStream.EnsureBufferedAsync(_boundary.FinalBoundaryLength, cancellationToken)) { throw new IOException("Unexpected end of Stream, the content may have already been read by another component. "); } var bufferedData = _innerStream.BufferedData; // scan for a boundary match, full or partial. int matchOffset; int matchCount; int read; if (SubMatch(bufferedData, _boundary.BoundaryBytes, out matchOffset, out matchCount)) { // We found a possible match, return any data before it. if (matchOffset > bufferedData.Offset) { // Sync, it's already buffered read = _innerStream.Read(buffer, offset, Math.Min(count, matchOffset - bufferedData.Offset)); return(UpdatePosition(read)); } var length = _boundary.BoundaryBytes !.Length; Debug.Assert(matchCount == length); // "The boundary may be followed by zero or more characters of // linear whitespace. It is then terminated by either another CRLF" // or -- for the final boundary. var boundary = _bytePool.Rent(length); read = _innerStream.Read(boundary, 0, length); _bytePool.Return(boundary); Debug.Assert(read == length); // It should have all been buffered var remainder = await _innerStream.ReadLineAsync(lengthLimit : 100, cancellationToken : cancellationToken); // Whitespace may exceed the buffer. remainder = remainder.Trim(); if (string.Equals("--", remainder, StringComparison.Ordinal)) { FinalBoundaryFound = true; } Debug.Assert(FinalBoundaryFound || string.Equals(string.Empty, remainder, StringComparison.Ordinal), "Un-expected data found on the boundary line: " + remainder); _finished = true; return(0); } // No possible boundary match within the buffered data, return the data from the buffer. read = _innerStream.Read(buffer, offset, Math.Min(count, bufferedData.Count)); return(UpdatePosition(read)); }
private static async Task <HttpResponseMessage> ReadNextChildResponseAsync(MultipartReader reader, CancellationToken cancellationToken = default(CancellationToken)) { var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); if (section == null) { return(null); } var bufferedStream = new BufferedReadStream(section.Body, 4096); var line = await bufferedStream.ReadLineAsync(MultipartReader.DefaultHeadersLengthLimit, cancellationToken).ConfigureAwait(false); var requestLineParts = line.Split(' '); if (requestLineParts.Length < 2) { throw new InvalidDataException("Invalid response line."); } var headers = await ApiInvoker.ReadHeadersAsync(bufferedStream, cancellationToken).ConfigureAwait(false); var response = new HttpResponseMessage(); HttpStatusCode statusCode; if (Enum.TryParse(requestLineParts[0], out statusCode)) { response.StatusCode = statusCode; } var contentStream = new MemoryStream(); await bufferedStream.CopyToAsync(contentStream); contentStream.Position = 0; response.Content = new StreamContent(contentStream); foreach (var header in headers) { if (!response.Content.Headers.Contains(header.Key)) { response.Content.Headers.Add(header.Key, header.Value.ToArray()); } } return(response); }
/// <summary> /// Parse a multipart/mixed response body into several responses. /// </summary> /// <param name="batchContent">The response content.</param> /// <param name="batchContentType">The response content type.</param> /// <param name="async"> /// Whether to invoke the operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Optional <see cref="CancellationToken"/> to propagate notifications /// that the operation should be cancelled. /// </param> /// <returns>The parsed <see cref="Response"/>s.</returns> public static async Task <Response[]> ParseAsync( Stream batchContent, string batchContentType, bool async, CancellationToken cancellationToken) { // Get the batch boundary if (batchContentType == null || !batchContentType.StartsWith(BatchConstants.MultipartContentTypePrefix, StringComparison.Ordinal)) { throw BatchErrors.InvalidBatchContentType(batchContentType); } string batchBoundary = batchContentType.Substring(BatchConstants.MultipartContentTypePrefix.Length); // Collect the responses in a dictionary (in case the Content-ID // values come back out of order) Dictionary <int, Response> responses = new Dictionary <int, Response>(); // Read through the batch body one section at a time until the // reader returns null MultipartReader reader = new MultipartReader(batchBoundary, batchContent); for (MultipartSection section = await reader.GetNextSectionAsync(async, cancellationToken).ConfigureAwait(false); section != null; section = await reader.GetNextSectionAsync(async, cancellationToken).ConfigureAwait(false)) { // Get the Content-ID header if (!section.Headers.TryGetValue(BatchConstants.ContentIdName, out StringValues contentIdValues) || contentIdValues.Count != 1 || !int.TryParse(contentIdValues[0], out int contentId)) { // If the header wasn't found, this is a failed request // with the details being sent as the first sub-operation // so we default the Content-ID to 0 contentId = 0; } // Build a response MemoryResponse response = new MemoryResponse(); responses[contentId] = response; // We're going to read the section's response body line by line using var body = new BufferedReadStream(section.Body, BatchConstants.ResponseLineSize); // The first line is the status like "HTTP/1.1 202 Accepted" string line = await body.ReadLineAsync(async, cancellationToken).ConfigureAwait(false); string[] status = line.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries); if (status.Length != 3) { throw BatchErrors.InvalidHttpStatusLine(line); } response.SetStatus(int.Parse(status[1], CultureInfo.InvariantCulture)); response.SetReasonPhrase(status[2]); // Continue reading headers until we reach a blank line line = await body.ReadLineAsync(async, cancellationToken).ConfigureAwait(false); while (!string.IsNullOrEmpty(line)) { // Split the header into the name and value int splitIndex = line.IndexOf(':'); if (splitIndex <= 0) { throw BatchErrors.InvalidHttpHeaderLine(line); } var name = line.Substring(0, splitIndex); var value = line.Substring(splitIndex + 1, line.Length - splitIndex - 1).Trim(); response.AddHeader(name, value); line = await body.ReadLineAsync(async, cancellationToken).ConfigureAwait(false); } // Copy the rest of the body as the response content var responseContent = new MemoryStream(); if (async) { await body.CopyToAsync(responseContent).ConfigureAwait(false); } else { body.CopyTo(responseContent); } responseContent.Seek(0, SeekOrigin.Begin); response.ContentStream = responseContent; } // Collect the responses and order by Content-ID Response[] ordered = new Response[responses.Count]; for (int i = 0; i < ordered.Length; i++) { ordered[i] = responses[i]; } return(ordered); }
protected virtual Task <string> GetResponseAsync() { return(_Stream.ReadLineAsync(0, Encoding, null)); }