Skip to content

Commit 074d608

Browse files
committed
Merge branch 'release/8.0.0'
2 parents f95dc3e + c321333 commit 074d608

19 files changed

+1961
-1314
lines changed

.config/dotnet-tools.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"isRoot": true,
44
"tools": {
55
"cake.tool": {
6-
"version": "2.3.0",
6+
"version": "3.0.0",
77
"commands": [
88
"dotnet-cake"
99
]

README.md

Lines changed: 122 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -47,133 +47,142 @@ PM> Install-Package HttpMultipartParser
4747

4848
### Single file
4949

50-
// stream:
51-
-----------------------------41952539122868
52-
Content-Disposition: form-data; name="username"
53-
54-
example
55-
-----------------------------41952539122868
56-
Content-Disposition: form-data; name="email"
57-
58-
59-
-----------------------------41952539122868
60-
Content-Disposition: form-data; name="files[]"; filename="photo1.jpg"
61-
Content-Type: image/jpeg
62-
63-
ExampleBinaryData012031203
64-
-----------------------------41952539122868--
65-
66-
// ===== Simple Parsing ====
67-
// You can parse synchronously:
68-
var parser = MultipartFormDataParser.Parse(stream);
69-
70-
// Or you can parse asynchronously:
71-
var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false);
72-
73-
// From this point the data is parsed, we can retrieve the
74-
// form data using the GetParameterValue method.
75-
var username = parser.GetParameterValue("username");
76-
var email = parser.GetParameterValue("email")
50+
```
51+
// stream:
52+
-----------------------------41952539122868
53+
Content-Disposition: form-data; name="username"
7754
78-
// Files are stored in a list:
79-
var file = parser.Files.First();
80-
string filename = file.FileName;
81-
Stream data = file.Data;
55+
example
56+
-----------------------------41952539122868
57+
Content-Disposition: form-data; name="email"
8258
83-
// ==== Advanced Parsing ====
84-
var parser = new StreamingMultipartFormDataParser(stream);
85-
parser.ParameterHandler += parameter => DoSomethingWithParameter(parameter);
86-
parser.FileHandler += (name, fileName, type, disposition, buffer, bytes, partNumber, additionalProperties) =>
87-
{
88-
// Write the part of the file we've received to a file stream. (Or do something else)
89-
filestream.Write(buffer, 0, bytes);
90-
}
59+
60+
-----------------------------41952539122868
61+
Content-Disposition: form-data; name="files[]"; filename="photo1.jpg"
62+
Content-Type: image/jpeg
9163
92-
// You can parse synchronously:
93-
parser.Run();
64+
ExampleBinaryData012031203
65+
-----------------------------41952539122868--
66+
```
9467

95-
// Or you can parse asynchronously:
96-
await parser.RunAsync().ConfigureAwait(false);
68+
```csharp
69+
// ===== Simple Parsing ====
70+
// You can parse synchronously:
71+
var parser = MultipartFormDataParser.Parse(stream);
72+
73+
// Or you can parse asynchronously:
74+
var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false);
75+
76+
// From this point the data is parsed, we can retrieve the
77+
// form data using the GetParameterValue method.
78+
var username = parser.GetParameterValue("username");
79+
var email = parser.GetParameterValue("email")
80+
81+
// Files are stored in a list:
82+
var file = parser.Files.First();
83+
string filename = file.FileName;
84+
Stream data = file.Data;
85+
86+
// ==== Advanced Parsing ====
87+
var parser = new StreamingMultipartFormDataParser(stream);
88+
parser.ParameterHandler += parameter => DoSomethingWithParameter(parameter);
89+
parser.FileHandler += (name, fileName, type, disposition, buffer, bytes, partNumber, additionalProperties) =>
90+
{
91+
// Write the part of the file we've received to a file stream. (Or do something else)
92+
filestream.Write(buffer, 0, bytes);
93+
}
94+
95+
// You can parse synchronously:
96+
parser.Run();
97+
98+
// Or you can parse asynchronously:
99+
await parser.RunAsync().ConfigureAwait(false);
100+
```
97101

98102
### Multiple Parameters
99103

100-
// stream:
101-
-----------------------------41952539122868
102-
Content-Disposition: form-data; name="checkbox"
103-
104-
likes_cake
105-
-----------------------------41952539122868
106-
Content-Disposition: form-data; name="checkbox"
104+
```
105+
// stream:
106+
-----------------------------41952539122868
107+
Content-Disposition: form-data; name="checkbox"
107108
108-
likes_cookies
109-
-----------------------------41952539122868--
109+
likes_cake
110+
-----------------------------41952539122868
111+
Content-Disposition: form-data; name="checkbox"
110112
111-
// ===== Simple Parsing ====
112-
// You can parse synchronously:
113-
var parser = MultipartFormDataParser.Parse(stream);
113+
likes_cookies
114+
-----------------------------41952539122868--
115+
```
116+
```csharp
117+
// ===== Simple Parsing ====
118+
// You can parse synchronously:
119+
var parser = MultipartFormDataParser.Parse(stream);
120+
121+
// Or you can parse asynchronously:
122+
var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false);
123+
124+
// From this point the data is parsed, we can retrieve the
125+
// form data from the GetParameterValues method
126+
var checkboxResponses = parser.GetParameterValues("checkbox");
127+
foreach(var parameter in checkboxResponses)
128+
{
129+
Console.WriteLine("Parameter {0} is {1}", parameter.Name, parameter.Data)
130+
}
131+
```
114132

115-
// Or you can parse asynchronously:
116-
var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false);
133+
### Multiple Files
117134

118-
// From this point the data is parsed, we can retrieve the
119-
// form data from the GetParameterValues method
120-
var checkboxResponses = parser.GetParameterValues("checkbox");
121-
foreach(var parameter in checkboxResponses)
122-
{
123-
Console.WriteLine("Parameter {0} is {1}", parameter.Name, parameter.Data)
124-
}
135+
```
136+
// stream:
137+
-----------------------------41111539122868
138+
Content-Disposition: form-data; name="files[]"; filename="photo1.jpg"
139+
Content-Type: image/jpeg
140+
141+
MoreBinaryData
142+
-----------------------------41111539122868
143+
Content-Disposition: form-data; name="files[]"; filename="photo2.jpg"
144+
Content-Type: image/jpeg
145+
146+
ImagineLotsOfBinaryData
147+
-----------------------------41111539122868--
148+
```
149+
```csharp
150+
// ===== Simple Parsing ====
151+
// You can parse synchronously:
152+
var parser = MultipartFormDataParser.Parse(stream);
125153

126-
### Multiple Files
154+
// Or you can parse asynchronously:
155+
var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false);
127156

128-
// stream:
129-
-----------------------------41111539122868
130-
Content-Disposition: form-data; name="files[]"; filename="photo1.jpg"
131-
Content-Type: image/jpeg
132-
133-
MoreBinaryData
134-
-----------------------------41111539122868
135-
Content-Disposition: form-data; name="files[]"; filename="photo2.jpg"
136-
Content-Type: image/jpeg
137-
138-
ImagineLotsOfBinaryData
139-
-----------------------------41111539122868--
140-
141-
// ===== Simple Parsing ====
142-
// You can parse synchronously:
143-
var parser = MultipartFormDataParser.Parse(stream);
144-
145-
// Or you can parse asynchronously:
146-
var parser = await MultipartFormDataParser.ParseAsync(stream).ConfigureAwait(false);
147-
148-
// Loop through all the files
149-
foreach(var file in parser.Files)
150-
{
151-
Stream data = file.Data;
152-
153-
// Do stuff with the data.
154-
}
155-
156-
// ==== Advanced Parsing ====
157-
var parser = new StreamingMultipartFormDataParser(stream);
158-
parser.ParameterHandler += parameter => DoSomethingWithParameter(parameter);
159-
parser.FileHandler += (name, fileName, type, disposition, buffer, bytes, partNumber, additionalProperties) =>
160-
{
161-
// Write the part of the file we've received to a file stream. (Or do something else)
162-
// Assume that filesreamsByName is a Dictionary<string, FileStream> of all the files
163-
// we are writing.
164-
filestreamsByName[name].Write(buffer, 0, bytes);
165-
};
166-
parser.StreamClosedHandler += ()
167-
{
168-
// Do things when my input stream is closed
169-
};
170-
171-
// You can parse synchronously:
172-
parser.Run();
173-
174-
// Or you can parse asynchronously:
175-
await parser.RunAsync().ConfigureAwait(false);
157+
// Loop through all the files
158+
foreach(var file in parser.Files)
159+
{
160+
Stream data = file.Data;
176161

162+
// Do stuff with the data.
163+
}
164+
165+
// ==== Advanced Parsing ====
166+
var parser = new StreamingMultipartFormDataParser(stream);
167+
parser.ParameterHandler += parameter => DoSomethingWithParameter(parameter);
168+
parser.FileHandler += (name, fileName, type, disposition, buffer, bytes, partNumber, additionalProperties) =>
169+
{
170+
// Write the part of the file we've received to a file stream. (Or do something else)
171+
// Assume that filesreamsByName is a Dictionary<string, FileStream> of all the files
172+
// we are writing.
173+
filestreamsByName[name].Write(buffer, 0, bytes);
174+
};
175+
parser.StreamClosedHandler += ()
176+
{
177+
// Do things when my input stream is closed
178+
};
179+
180+
// You can parse synchronously:
181+
parser.Run();
182+
183+
// Or you can parse asynchronously:
184+
await parser.RunAsync().ConfigureAwait(false);
185+
```
177186
## Licensing
178187

179188
This project is licensed under MIT.

Source/HttpMultipartParser.Benchmark/HttpMultipartParser.Benchmark.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net7.0</TargetFramework>
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
9+
<PackageReference Include="BenchmarkDotNet" Version="0.13.3" />
1010
</ItemGroup>
1111

1212
<ItemGroup>

Source/HttpMultipartParser.UnitTests/HttpMultipartParser.UnitTests.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net48;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
4+
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
55
<AssemblyName>HttpMultipartParser.UnitTests</AssemblyName>
66
<RootNamespace>HttpMultipartParser.UnitTests</RootNamespace>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="coverlet.msbuild" Version="3.1.2">
10+
<PackageReference Include="coverlet.msbuild" Version="3.2.0">
1111
<PrivateAssets>all</PrivateAssets>
1212
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1313
</PackageReference>
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
15-
<PackageReference Include="xunit" Version="2.4.1" />
16-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
15+
<PackageReference Include="xunit" Version="2.4.2" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
1717
<PrivateAssets>all</PrivateAssets>
1818
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1919
</PackageReference>

Source/HttpMultipartParser.UnitTests/ParserScenarios/SmallData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public void SmallDataTest()
6060
{
6161
// The boundry is missing the first two -- in accordance with the multipart
6262
// spec. (A -- is added by the parser, this boundry is what would be sent in the
63-
// requset header)
63+
// request header)
6464
var parser = MultipartFormDataParser.Parse(stream, "---------------------------265001916915724");
6565
Assert.True(_testCase.Validate(parser));
6666
}

Source/HttpMultipartParser/BinaryStreamStack.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class BinaryStreamStack
5454
/// encoding of UTF8.
5555
/// </summary>
5656
public BinaryStreamStack()
57-
: this(Encoding.UTF8)
57+
: this(Constants.DefaultEncoding)
5858
{
5959
}
6060

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Text;
2+
3+
namespace HttpMultipartParser
4+
{
5+
internal static class Constants
6+
{
7+
/// <summary>
8+
/// The default buffer size.
9+
/// </summary>
10+
/// <remarks>
11+
/// 4096 is the optimal buffer size as it matches the internal buffer of a StreamReader
12+
/// See: http://stackoverflow.com/a/129318/203133
13+
/// See: http://msdn.microsoft.com/en-us/library/9kstw824.aspx (under remarks).
14+
/// </remarks>
15+
internal const int DefaultBufferSize = 4096;
16+
17+
/// <summary>
18+
/// The mimetypes that are considered a file by default.
19+
/// </summary>
20+
internal static readonly string[] DefaultBinaryMimeTypes = { "application/octet-stream" };
21+
22+
/// <summary>
23+
/// The default encoding used by the parser when developer does not specify the encoding.
24+
/// </summary>
25+
internal static readonly Encoding DefaultEncoding = Encoding.UTF8;
26+
}
27+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Collections.Generic;
2+
3+
namespace HttpMultipartParser
4+
{
5+
/// <summary>
6+
/// The FileStreamDelegate defining functions that can handle file stream data from this parser.
7+
///
8+
/// Delegates can assume that the data is sequential i.e. the data received by any delegates will be
9+
/// the data immediately following any previously received data.
10+
/// </summary>
11+
/// <param name="name">The name of the multipart data.</param>
12+
/// <param name="fileName">The name of the file.</param>
13+
/// <param name="contentType">The content type of the multipart data.</param>
14+
/// <param name="contentDisposition">The content disposition of the multipart data.</param>
15+
/// <param name="buffer">Some of the data from the file (not necessarily all of the data).</param>
16+
/// <param name="bytes">The length of data in buffer.</param>
17+
/// <param name="partNumber">Each chunk (or "part") in a given file is sequentially numbered, starting at zero.</param>
18+
/// <param name="additionalProperties">Properties other than the "well known" ones (such as name, filename, content-type, etc.) associated with a file stream.</param>
19+
public delegate void FileStreamDelegate(string name, string fileName, string contentType, string contentDisposition, byte[] buffer, int bytes, int partNumber, IDictionary<string, string> additionalProperties);
20+
21+
/// <summary>
22+
/// The StreamClosedDelegate defining functions that can handle stream being closed.
23+
/// </summary>
24+
public delegate void StreamClosedDelegate();
25+
26+
/// <summary>
27+
/// The ParameterDelegate defining functions that can handle multipart parameter data.
28+
/// </summary>
29+
/// <param name="part">The parsed parameter part.</param>
30+
public delegate void ParameterDelegate(ParameterPart part);
31+
32+
/// <summary>
33+
/// The BinaryParameterDelegate defining functions that can handle multipart parameter data.
34+
/// </summary>
35+
/// <param name="binaryPart">The parsed parameter part.</param>
36+
public delegate void BinaryParameterDelegate(ParameterPartBinary binaryPart);
37+
}

0 commit comments

Comments
 (0)