// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections; using System.Diagnostics.Contracts; using System.IO; using System.Net.Http.Formatting.Parsers; using System.Net.Http.Headers; 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 IMultipartStreamProvider _streamProvider; 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. public MimeBodyPart(IMultipartStreamProvider streamProvider, int maxBodyPartHeaderSize) { Contract.Assert(streamProvider != null, "Stream provider cannot be null."); _streamProvider = streamProvider; Segments = new ArrayList(2); _headers = FormattingUtilities.CreateEmptyContentHeaders(); HeaderParser = new InternetMessageFormatHeaderParser(_headers, maxBodyPartHeaderSize); } /// /// Gets the header parser. /// /// /// The header parser. /// public InternetMessageFormatHeaderParser HeaderParser { get; private set; } /// /// Gets the content of the HTTP. /// /// /// The content of the HTTP. /// public HttpContent HttpContent { get; private set; } /// /// Gets the set of pointing to the read buffer with /// contents of this body part. /// /// We deliberately use as List{ArraySegment{byte}} and Collection{ArraySegment{byte}} trip FxCop rule CA908 /// which states the following: "Assemblies that are precompiled (using ngen.exe) should only instantiate generic types that will not cause JIT /// compilation at runtime. Generic types with value type type parameters (outside of a special set of supported runtime generic types) will /// always cause JIT compilation, even if the encapsulating assembly has been precompiled". As we don't want to force JIT'ing we use the old /// non-generic form which doesn't have this problem (ArraySegment{byte} of course is a value type). public ArrayList 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; } /// /// Gets the output stream. /// /// The output stream to write the body part to. public Stream GetOutputStream() { if (_outputStream == null) { try { _outputStream = _streamProvider.GetStream(_headers); } catch (Exception e) { throw new InvalidOperationException(RS.Format(Properties.Resources.ReadAsMimeMultipartStreamProviderException, _streamProvider.GetType().Name), e); } if (_outputStream == null) { throw new InvalidOperationException( RS.Format(Properties.Resources.ReadAsMimeMultipartStreamProviderNull, _streamProvider.GetType().Name, _streamType.Name)); } if (!_outputStream.CanWrite) { throw new InvalidOperationException( RS.Format(Properties.Resources.ReadAsMimeMultipartStreamProviderReadOnly, _streamProvider.GetType().Name, _streamType.Name)); } HttpContent = new StreamContent(_outputStream); _headers.CopyTo(HttpContent.Headers); } 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(); HttpContent = null; HeaderParser = null; Segments.Clear(); } } /// /// 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 { _outputStream.Close(); } _outputStream = null; } } } }