Azure.Core.Http.Multipart.MultipartReader - Coverage Report
1// Copyright (c) Microsoft Corporation. All rights reserved.2// Licensed under the MIT License.34// Copied from https://github.com/aspnet/AspNetCore/tree/master/src/Http/WebUtilities/src56using System;7using System.Collections.Generic;8using System.Diagnostics;9using System.IO;10using System.Threading;11using System.Threading.Tasks;1213#pragma warning disable CA1001 // disposable fields14#pragma warning disable IDE0008 // Use explicit type1516namespace Azure.Core.Http.Multipart17{18 // https://www.ietf.org/rfc/rfc2046.txt19 internal class MultipartReader20 {21 public const int DefaultHeadersCountLimit = 16;22 public const int DefaultHeadersLengthLimit = 1024 * 16;23 private const int DefaultBufferSize = 1024 * 4;2425 private readonly BufferedReadStream _stream;26 private readonly MultipartBoundary _boundary;27 private MultipartReaderStream _currentStream;2829 public MultipartReader(string boundary, Stream stream)30 : this(boundary, stream, DefaultBufferSize)31 {32 }3334 public MultipartReader(string boundary, Stream stream, int bufferSize)35 {36 if (boundary == null)37 {38 throw new ArgumentNullException(nameof(boundary));39 }4041 if (stream == null)42 {43 throw new ArgumentNullException(nameof(stream));44 }4546 if (bufferSize < boundary.Length + 8) // Size of the boundary + leading and trailing CRLF + leading and trai47 {48 throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, "Insufficient buffer space, the bu49 }50 _stream = new BufferedReadStream(stream, bufferSize);51 _boundary = new MultipartBoundary(boundary, false);52 // This stream will drain any preamble data and remove the first boundary marker.53 // TODO: HeadersLengthLimit can't be modified until after the constructor.54 _currentStream = new MultipartReaderStream(_stream, _boundary) { LengthLimit = HeadersLengthLimit };55 }5657 /// <summary>58 /// The limit for the number of headers to read.59 /// </summary>60 public int HeadersCountLimit { get; set; } = DefaultHeadersCountLimit;6162 /// <summary>63 /// The combined size limit for headers per multipart section.64 /// </summary>65 public int HeadersLengthLimit { get; set; } = DefaultHeadersLengthLimit;6667 /// <summary>68 /// The optional limit for the total response body length.69 /// </summary>70 public long? BodyLengthLimit { get; set; }7172 public async Task<MultipartSection> ReadNextSectionAsync(CancellationToken cancellationToken = new CancellationT73 {74 // Drain the prior section.75 await _currentStream.DrainAsync(cancellationToken).ConfigureAwait(false);76 // If we're at the end return null77 if (_currentStream.FinalBoundaryFound)78 {79 // There may be trailer data after the last boundary.80 await _stream.DrainAsync(HeadersLengthLimit, cancellationToken).ConfigureAwait(false);81 return null;82 }83 var headers = await ReadHeadersAsync(cancellationToken).ConfigureAwait(false);84 _boundary.ExpectLeadingCrlf = true;85 _currentStream = new MultipartReaderStream(_stream, _boundary) { LengthLimit = BodyLengthLimit };86 long? baseStreamOffset = _stream.CanSeek ? (long?)_stream.Position : null;87 return new MultipartSection() { Headers = headers, Body = _currentStream, BaseStreamOffset = baseStreamOffse88 }8990 private async Task<Dictionary<string, StringValues>> ReadHeadersAsync(CancellationToken cancellationToken)91 {92 int totalSize = 0;93 var accumulator = new KeyValueAccumulator();94 var line = await _stream.ReadLineAsync(HeadersLengthLimit - totalSize, cancellationToken).ConfigureAwait(fal95 while (!string.IsNullOrEmpty(line))96 {97 if (HeadersLengthLimit - totalSize < line.Length)98 {99 throw new InvalidDataException($"Multipart headers length limit {HeadersLengthLimit} exceeded.");100 }101 totalSize += line.Length;102 int splitIndex = line.IndexOf(':');103 if (splitIndex <= 0)104 {105 throw new InvalidDataException($"Invalid header line: {line}");106 }107108 var name = line.Substring(0, splitIndex);109 var value = line.Substring(splitIndex + 1, line.Length - splitIndex - 1).Trim();110 accumulator.Append(name, value);111 if (accumulator.KeyCount > HeadersCountLimit)112 {113 throw new InvalidDataException($"Multipart headers count limit {HeadersCountLimit} exceeded.");114 }115116 line = await _stream.ReadLineAsync(HeadersLengthLimit - totalSize, cancellationToken).ConfigureAwait(fal117 }118119 return accumulator.GetResults();120 }121 }122}ncG1vNJzZmiZqqq%2Fpr%2FDpJirrJmbrqTA0meZpaeSY7CwvsRnrqKmlKTEtHrNnqtomaqqv6Z50p2iZp6fp3qvsdNoeqiclVp%2FcY%2FOr5yrmZeafILG1KucZ4ukpL%2Bis8RneaWnkqh7g63TnJ%2BYhaWhwaq8wKuri52RmbKzesetpKU%3D