Exemplo n.º 1
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);
        }
Exemplo n.º 2
0
        /// <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);
        }