forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMimeBodyPart.cs
More file actions
204 lines (183 loc) · 7.28 KB
/
Copy pathMimeBodyPart.cs
File metadata and controls
204 lines (183 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// 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
{
/// <summary>
/// Maintains information about MIME body parts parsed by <see cref="MimeMultipartBodyPartParser"/>.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="MimeBodyPart"/> class.
/// </summary>
/// <param name="streamProvider">The stream provider.</param>
/// <param name="maxBodyPartHeaderSize">The max length of the MIME header within each MIME body part.</param>
/// <param name="parentContent">The part's parent content</param>
public MimeBodyPart(MultipartStreamProvider streamProvider, int maxBodyPartHeaderSize, HttpContent parentContent)
{
Contract.Assert(streamProvider != null);
Contract.Assert(parentContent != null);
_streamProvider = streamProvider;
_parentContent = parentContent;
Segments = new List<ArraySegment<byte>>(2);
_headers = FormattingUtilities.CreateEmptyContentHeaders();
HeaderParser = new InternetMessageFormatHeaderParser(
_headers,
maxBodyPartHeaderSize,
ignoreHeaderValidation: true);
}
/// <summary>
/// Gets the header parser.
/// </summary>
/// <value>
/// The header parser.
/// </value>
public InternetMessageFormatHeaderParser HeaderParser { get; private set; }
/// <summary>
/// Gets the part's content as an HttpContent.
/// </summary>
/// <value>
/// The part's content, or null if the part had no content.
/// </value>
public HttpContent GetCompletedHttpContent()
{
Contract.Assert(IsComplete);
if (_content == null)
{
return null;
}
_headers.CopyTo(_content.Headers);
return _content;
}
/// <summary>
/// Gets the set of <see cref="ArraySegment{T}"/> pointing to the read buffer with
/// contents of this body part.
/// </summary>
public List<ArraySegment<byte>> Segments { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether the body part has been completed.
/// </summary>
/// <value>
/// <c>true</c> if this instance is complete; otherwise, <c>false</c>.
/// </value>
public bool IsComplete { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this is the final body part.
/// </summary>
/// <value>
/// <c>true</c> if this instance is complete; otherwise, <c>false</c>.
/// </value>
public bool IsFinal { get; set; }
/// <summary>
/// Writes the <paramref name="segment"/> into the part's output stream.
/// </summary>
/// <param name="segment">The current segment to be written to the part's output stream.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
public async Task WriteSegment(ArraySegment<byte> segment, CancellationToken cancellationToken)
{
var stream = GetOutputStream();
await stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken);
}
/// <summary>
/// Gets the output stream.
/// </summary>
/// <returns>The output stream to write the body part to.</returns>
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;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected void Dispose(bool disposing)
{
if (disposing)
{
CleanupOutputStream();
CleanupHttpContent();
_parentContent = null;
HeaderParser = null;
Segments.Clear();
}
}
/// <summary>
/// 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.
/// </summary>
private void CleanupHttpContent()
{
if (!IsComplete && _content != null)
{
_content.Dispose();
}
_content = null;
}
/// <summary>
/// Resets the output stream by either closing it or, in the case of a <see cref="MemoryStream"/> resetting
/// position to 0 so that it can be read by the caller.
/// </summary>
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;
}
}
}
}