// 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;
}
}
}
}