// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web.Http; namespace System.Net.Http { /// /// Maintains information about MIME body parts parsed by . /// internal class MimeBodyPart : IDisposable { private static readonly Type _streamType = typeof(Stream); private Stream _outputStream; private MultipartStreamProvider _streamProvider; private HttpContent _parentContent; private HttpContent _content; private HttpContentHeaders _headers; /// /// Initializes a new instance of the class. /// /// The stream provider. /// The max length of the MIME header within each MIME body part. /// The part's parent content public MimeBodyPart(MultipartStreamProvider streamProvider, int maxBodyPartHeaderSize, HttpContent parentContent) { Contract.Assert(streamProvider != null); Contract.Assert(parentContent != null); _streamProvider = streamProvider; _parentContent = parentContent; Segments = new List>(2); _headers = FormattingUtilities.CreateEmptyContentHeaders(); HeaderParser = new InternetMessageFormatHeaderParser( _headers, maxBodyPartHeaderSize, ignoreHeaderValidation: true); } /// /// Gets the header parser. /// /// /// The header parser. /// public InternetMessageFormatHeaderParser HeaderParser { get; private set; } /// /// Gets the part's content as an HttpContent. /// /// /// The part's content, or null if the part had no content. /// public HttpContent GetCompletedHttpContent() { Contract.Assert(IsComplete); if (_content == null) { return null; } _headers.CopyTo(_content.Headers); return _content; } /// /// Gets the set of pointing to the read buffer with /// contents of this body part. /// public List> Segments { get; private set; } /// /// Gets or sets a value indicating whether the body part has been completed. /// /// /// true if this instance is complete; otherwise, false. /// public bool IsComplete { get; set; } /// /// Gets or sets a value indicating whether this is the final body part. /// /// /// true if this instance is complete; otherwise, false. /// public bool IsFinal { get; set; } /// /// Writes the into the part's output stream. /// /// The current segment to be written to the part's output stream. /// The token to monitor for cancellation requests. public async Task WriteSegment(ArraySegment segment, CancellationToken cancellationToken) { var stream = GetOutputStream(); await stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken); } /// /// Gets the output stream. /// /// The output stream to write the body part to. private Stream GetOutputStream() { if (_outputStream == null) { try { _outputStream = _streamProvider.GetStream(_parentContent, _headers); } catch (Exception e) { throw Error.InvalidOperation(e, Properties.Resources.ReadAsMimeMultipartStreamProviderException, _streamProvider.GetType().Name); } if (_outputStream == null) { throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartStreamProviderNull, _streamProvider.GetType().Name, _streamType.Name); } if (!_outputStream.CanWrite) { throw Error.InvalidOperation(Properties.Resources.ReadAsMimeMultipartStreamProviderReadOnly, _streamProvider.GetType().Name, _streamType.Name); } _content = new StreamContent(_outputStream); } return _outputStream; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected void Dispose(bool disposing) { if (disposing) { CleanupOutputStream(); CleanupHttpContent(); _parentContent = null; HeaderParser = null; Segments.Clear(); } } /// /// In the success case, the HttpContent is to be used after this Part has been parsed and disposed of. /// Only if Dispose has been called on a non-completed part, the parsed HttpContent needs to be disposed of as well. /// private void CleanupHttpContent() { if (!IsComplete && _content != null) { _content.Dispose(); } _content = null; } /// /// Resets the output stream by either closing it or, in the case of a resetting /// position to 0 so that it can be read by the caller. /// private void CleanupOutputStream() { if (_outputStream != null) { MemoryStream output = _outputStream as MemoryStream; if (output != null) { output.Position = 0; } else { #if NETFX_CORE _outputStream.Dispose(); #else _outputStream.Close(); #endif } _outputStream = null; } } } }