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