Skip to content

Commit be17ac8

Browse files
committed
Merge branch 'release/3.1.0'
2 parents 214605d + dcc6c94 commit be17ac8

16 files changed

Lines changed: 327 additions & 84 deletions

GitVersion.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,3 @@ branches:
1414
tag: rc
1515
ignore:
1616
sha: []
17-
next-version: 3.0.0

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://httpmultipartparser.mit-license.org/)
44
![Sourcelink](https://img.shields.io/badge/sourcelink-enabled-brightgreen.svg)
55
[![Build status](https://ci.appveyor.com/api/projects/status/t547jmcf10s53h2u?svg=true)](https://ci.appveyor.com/project/Jericho/http-multipart-data-parser)
6-
![tests](https://img.shields.io/appveyor/tests/jericho/http-multipart-data-parser)
7-
6+
[![tests](https://img.shields.io/appveyor/tests/jericho/http-multipart-data-parser)](https://ci.appveyor.com/project/jericho/http-multipart-data-parser/build/tests)
87
[![Coverage Status](https://coveralls.io/repos/github/Http-Multipart-Data-Parser/Http-Multipart-Data-Parser/badge.svg?branch=master)](https://coveralls.io/github/Http-Multipart-Data-Parser/Http-Multipart-Data-Parser?branch=master)
98
[![CodeFactor](https://www.codefactor.io/repository/github/http-multipart-data-parser/http-multipart-data-parser/badge)](https://www.codefactor.io/repository/github/http-multipart-data-parser/http-multipart-data-parser)
109

1110
| Release Notes| NuGet (stable) | MyGet (prerelease) |
1211
|--------------|----------------|--------------------|
13-
| [![GitHub release](https://img.shields.io/github/release/jericho/http-multipart-data-parser.svg)](https://github.com/http-multipart-data-parser/http-multipart-data-parser/releases) | [![NuGet Version](http://img.shields.io/nuget/v/HttpMultipartDataParser.svg)](https://www.nuget.org/packages/HttpMultipartParser/) | [![MyGet Pre Release](https://img.shields.io/myget/jericho/vpre/HttpMultipartParser.svg)](http://myget.org/gallery/jericho) |
12+
| [![GitHub release](https://img.shields.io/github/release/http-multipart-data-parser/http-multipart-data-parser.svg)](https://github.com/http-multipart-data-parser/http-multipart-data-parser/releases) | [![Nuget](https://img.shields.io/nuget/v/HttpMultipartParser.svg)](https://www.nuget.org/packages/HttpMultipartParser/) | [![MyGet Pre Release](https://img.shields.io/myget/jericho/vpre/HttpMultipartParser.svg)](http://myget.org/gallery/jericho) |
1413

1514
## About
1615

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
7+
namespace HttpMultipartParser.Benchmark
8+
{
9+
public class BenchmarkData
10+
{
11+
private readonly IEnumerable<(string Name, string Data)> _parameters;
12+
private readonly IEnumerable<(string Name, string FileName, string Data)> _files;
13+
14+
public BenchmarkData(int numberOfParameters, int sizeOfParameter, int numberOfFiles, int sizeOfFile)
15+
{
16+
_parameters = Enumerable
17+
.Range(1, numberOfParameters)
18+
.Select(index => ($"parameter{index}", new string(Convert.ToChar(index), sizeOfParameter)))
19+
.ToArray();
20+
21+
_files = Enumerable
22+
.Range(1, numberOfFiles)
23+
.Select(index => ($"file{index}", $"fileName{index}", new string(Convert.ToChar(index), sizeOfFile)))
24+
.ToArray();
25+
}
26+
27+
public override string ToString()
28+
{
29+
var sb = new StringBuilder();
30+
foreach (var (Name, Data) in _parameters)
31+
{
32+
sb.Append("--boundary\n");
33+
sb.Append($"Content-Disposition: form-data; name=\"{Name}\"\n");
34+
sb.Append("\n");
35+
sb.Append($"{Data}\n");
36+
}
37+
foreach (var (Name, FileName, Data) in _files)
38+
{
39+
sb.Append("--boundary\n");
40+
sb.Append($"Content-Disposition: form-data; name=\"{Name}\"; filename=\"{FileName}\"\n");
41+
sb.Append("Content-Type: text/plain\n");
42+
sb.Append("\n");
43+
sb.Append($"{Data}\n");
44+
}
45+
sb.Append("--boundary--");
46+
47+
return sb.ToString();
48+
}
49+
50+
public Stream ToStream()
51+
{
52+
var content = this.ToString();
53+
var buffer = Encoding.UTF8.GetBytes(content);
54+
return new MemoryStream(buffer);
55+
}
56+
}
57+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\HttpMultipartParser\HttpMultipartParser.csproj" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using BenchmarkDotNet.Attributes;
2+
using System.IO;
3+
using System.Threading.Tasks;
4+
5+
namespace HttpMultipartParser.Benchmark
6+
{
7+
[MemoryDiagnoser]
8+
[HtmlExporter]
9+
[JsonExporter]
10+
[MarkdownExporter]
11+
public class MultipartFormDataParserBenchmark
12+
{
13+
private readonly Stream small;
14+
private readonly Stream medium;
15+
private readonly Stream large;
16+
17+
public MultipartFormDataParserBenchmark()
18+
{
19+
small = new BenchmarkData(5, 10, 1, 125000).ToStream();
20+
medium = new BenchmarkData(25, 50, 5, 250000).ToStream();
21+
large = new BenchmarkData(100, 500, 50, 500000).ToStream();
22+
}
23+
24+
[Benchmark]
25+
public async Task<MultipartFormDataParser> Small()
26+
{
27+
small.Position = 0;
28+
return await MultipartFormDataParser.ParseAsync(small, "boundary").ConfigureAwait(false);
29+
}
30+
31+
[Benchmark]
32+
public async Task<MultipartFormDataParser> Medium()
33+
{
34+
medium.Position = 0;
35+
return await MultipartFormDataParser.ParseAsync(medium, "boundary").ConfigureAwait(false);
36+
}
37+
38+
[Benchmark]
39+
public async Task<MultipartFormDataParser> Large()
40+
{
41+
large.Position = 0;
42+
return await MultipartFormDataParser.ParseAsync(large, "boundary").ConfigureAwait(false);
43+
}
44+
}
45+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using BenchmarkDotNet.Running;
2+
3+
namespace HttpMultipartParse.Benchmark
4+
{
5+
class Program
6+
{
7+
static void Main()
8+
{
9+
BenchmarkRunner.Run(typeof(Program).Assembly);
10+
11+
// To debug:
12+
// BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig());
13+
}
14+
}
15+
}

Source/HttpMultipartParser.UnitTests/HttpMultipartParser.UnitTests.csproj

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

33
<PropertyGroup>
4-
<TargetFrameworks>net461;net472;netcoreapp2.0</TargetFrameworks>
4+
<TargetFrameworks>net461;net472;netcoreapp2.1</TargetFrameworks>
55
<AssemblyName>HttpMultipartParser.UnitTests</AssemblyName>
66
<RootNamespace>HttpMultipartParser.UnitTests</RootNamespace>
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
1111
<PackageReference Include="xunit" Version="2.4.1" />
1212
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
1313
<PrivateAssets>all</PrivateAssets>

Source/HttpMultipartParser.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D51F3380-641
1616
EndProject
1717
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{5D8E3C6B-9DC8-4D8D-BAA4-6D739906AFE5}"
1818
EndProject
19+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpMultipartParser.Benchmark", "HttpMultipartParser.Benchmark\HttpMultipartParser.Benchmark.csproj", "{38626A16-025B-4B71-A7D7-B963ADB80BC0}"
20+
EndProject
1921
Global
2022
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2123
Debug|Any CPU = Debug|Any CPU
@@ -30,13 +32,18 @@ Global
3032
{5E49EE76-62B3-40E3-BEFC-3F0CB8BEE523}.Debug|Any CPU.Build.0 = Debug|Any CPU
3133
{5E49EE76-62B3-40E3-BEFC-3F0CB8BEE523}.Release|Any CPU.ActiveCfg = Release|Any CPU
3234
{5E49EE76-62B3-40E3-BEFC-3F0CB8BEE523}.Release|Any CPU.Build.0 = Release|Any CPU
35+
{38626A16-025B-4B71-A7D7-B963ADB80BC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36+
{38626A16-025B-4B71-A7D7-B963ADB80BC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
37+
{38626A16-025B-4B71-A7D7-B963ADB80BC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{38626A16-025B-4B71-A7D7-B963ADB80BC0}.Release|Any CPU.Build.0 = Release|Any CPU
3339
EndGlobalSection
3440
GlobalSection(SolutionProperties) = preSolution
3541
HideSolutionNode = FALSE
3642
EndGlobalSection
3743
GlobalSection(NestedProjects) = preSolution
3844
{EA2AE348-5520-4FEE-815B-02D90322804D} = {D51F3380-641A-41EC-9397-D16B2196A24A}
3945
{5E49EE76-62B3-40E3-BEFC-3F0CB8BEE523} = {5D8E3C6B-9DC8-4D8D-BAA4-6D739906AFE5}
46+
{38626A16-025B-4B71-A7D7-B963ADB80BC0} = {5D8E3C6B-9DC8-4D8D-BAA4-6D739906AFE5}
4047
EndGlobalSection
4148
GlobalSection(ExtensibilityGlobals) = postSolution
4249
SolutionGuid = {EC47708C-FE9B-4234-8622-7049B6B01005}

Source/HttpMultipartParser/BinaryStreamStack.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,29 @@ public BinaryReader Pop()
124124
/// </param>
125125
public void Push(byte[] data)
126126
{
127-
streams.Push(new BinaryReader(new MemoryStream(data), CurrentEncoding));
127+
Push(data, 0, data.Length);
128+
}
129+
130+
/// <summary>
131+
/// Pushes data to the front of the stack. The most recently pushed data will
132+
/// be read first.
133+
/// </summary>
134+
/// <param name="data">
135+
/// The data to add to the stack.
136+
/// </param>
137+
/// <param name="offset">
138+
/// The zero-based byte offset in buffer at which to begin copying bytes to the current stream.
139+
/// </param>
140+
/// <param name="count">
141+
/// The maximum number of bytes to write.
142+
/// </param>
143+
public void Push(byte[] data, int offset, int count)
144+
{
145+
var stream = Utilities.MemoryStreamManager.GetStream();
146+
stream.Write(data, offset, count);
147+
stream.Position = 0;
148+
149+
streams.Push(new BinaryReader(stream, CurrentEncoding));
128150
}
129151

130152
/// <summary>
@@ -284,13 +306,13 @@ public byte[] ReadByteLine(out bool hitStreamEnd)
284306
byte[] ignore = CurrentEncoding.GetBytes(new[] { '\r' });
285307
byte[] search = CurrentEncoding.GetBytes(new[] { '\n' });
286308
int searchPos = 0;
287-
using (var builder = new MemoryStream())
309+
using (var builder = Utilities.MemoryStreamManager.GetStream())
288310
{
289311
while (true)
290312
{
291313
// First we need to read a byte from one of the streams
292-
var bytes = new byte[search.Length];
293-
int amountRead = top.Read(bytes, 0, bytes.Length);
314+
var bytes = Utilities.ArrayPool.Rent(search.Length);
315+
int amountRead = top.Read(bytes, 0, search.Length);
294316
while (amountRead == 0)
295317
{
296318
streams.Pop();
@@ -302,12 +324,14 @@ public byte[] ReadByteLine(out bool hitStreamEnd)
302324

303325
top.Dispose();
304326
top = streams.Peek();
305-
amountRead = top.Read(bytes, 0, bytes.Length);
327+
amountRead = top.Read(bytes, 0, search.Length);
306328
}
307329

308330
// Now we've got some bytes, we need to check it against the search array.
309-
foreach (byte b in bytes)
331+
for (int i = 0; i < search.Length; i++)
310332
{
333+
var b = bytes[i];
334+
311335
if (ignore.Contains(b))
312336
{
313337
continue;
@@ -337,6 +361,8 @@ public byte[] ReadByteLine(out bool hitStreamEnd)
337361
return builder.ToArray();
338362
}
339363
}
364+
365+
Utilities.ArrayPool.Return(bytes);
340366
}
341367
}
342368
}

Source/HttpMultipartParser/HttpMultipartParser.csproj

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

33
<PropertyGroup>
44
<TargetFrameworks>net461;net472;netstandard2.0</TargetFrameworks>
@@ -37,10 +37,10 @@
3737
</PropertyGroup>
3838

3939
<ItemGroup>
40-
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
41-
<PrivateAssets>all</PrivateAssets>
42-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
43-
</PackageReference>
40+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
41+
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="1.3.0" />
42+
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
43+
<PackageReference Include="System.Buffers" Version="4.5.0" />
4444
</ItemGroup>
4545

4646
<ItemGroup Condition=" !$(TargetFramework.StartsWith('netstandard')) ">
@@ -56,11 +56,6 @@
5656
<DefineConstants>$(DefineConstants);NETFULL</DefineConstants>
5757
</PropertyGroup>
5858

59-
<ItemGroup>
60-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19351-01" PrivateAssets="All" />
61-
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
62-
</ItemGroup>
63-
6459
<PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
6560
<DefineConstants>$(DefineConstants);NETSTANDARD</DefineConstants>
6661
</PropertyGroup>

0 commit comments

Comments
 (0)