Beispiel #1
0
    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());
    }
Beispiel #2
0
        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));
        }
Beispiel #5
0
    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));
    }
Beispiel #6
0
        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);
        }
Beispiel #8
0
 protected virtual Task <string> GetResponseAsync()
 {
     return(_Stream.ReadLineAsync(0, Encoding, null));
 }