Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,178 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Microsoft.TestCommon;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Net.Http.Formatting.Parsers
{
public class FormUrlEncodedParserTests
{
private const int MinMessageSize = 1;
private const int Iterations = 16;
internal static Collection<KeyValuePair<string, string>> CreateCollection()
{
return new Collection<KeyValuePair<string, string>>();
}
internal static FormUrlEncodedParser CreateParser(int maxMessageSize, out ICollection<KeyValuePair<string, string>> nameValuePairs)
{
nameValuePairs = CreateCollection();
return new FormUrlEncodedParser(nameValuePairs, maxMessageSize);
}
internal static byte[] CreateBuffer(params string[] nameValuePairs)
{
StringBuilder buffer = new StringBuilder();
bool first = true;
foreach (var h in nameValuePairs)
{
if (first)
{
first = false;
}
else
{
buffer.Append('&');
}
buffer.Append(h);
}
return Encoding.UTF8.GetBytes(buffer.ToString());
}
internal static ParserState ParseBufferInSteps(FormUrlEncodedParser parser, byte[] buffer, int readsize, out int totalBytesConsumed)
{
ParserState state = ParserState.Invalid;
totalBytesConsumed = 0;
while (totalBytesConsumed <= buffer.Length)
{
int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
byte[] parseBuffer = new byte[size];
Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
int bytesConsumed = 0;
state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed, totalBytesConsumed == buffer.Length - size);
totalBytesConsumed += bytesConsumed;
if (state != ParserState.NeedMoreData)
{
return state;
}
}
return state;
}
[Fact]
public void TypeIsCorrect()
{
Assert.Type.HasProperties<FormUrlEncodedParser>(TypeAssert.TypeProperties.IsClass);
}
[Fact]
public void FormUrlEncodedParserThrowsOnNull()
{
Assert.ThrowsArgumentNull(() => { new FormUrlEncodedParser(null, ParserData.MinHeaderSize); }, "nameValuePairs");
}
[Fact]
public void FormUrlEncodedParserThrowsOnInvalidSize()
{
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { new FormUrlEncodedParser(CreateCollection(), MinMessageSize - 1); }, "maxMessageSize", MinMessageSize.ToString(), MinMessageSize - 1);
FormUrlEncodedParser parser = new FormUrlEncodedParser(CreateCollection(), MinMessageSize);
Assert.NotNull(parser);
parser = new FormUrlEncodedParser(CreateCollection(), MinMessageSize + 1);
Assert.NotNull(parser);
}
[Fact]
public void ParseBufferThrowsOnNullBuffer()
{
ICollection<KeyValuePair<string, string>> collection;
FormUrlEncodedParser parser = CreateParser(128, out collection);
int bytesConsumed = 0;
Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed, false); }, "buffer");
}
[Fact]
public void ParseBufferHandlesEmptyBuffer()
{
byte[] data = CreateBuffer();
ICollection<KeyValuePair<string, string>> collection;
FormUrlEncodedParser parser = CreateParser(MinMessageSize, out collection);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed, true);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, bytesConsumed);
Assert.Equal(0, collection.Count());
}
public static TheoryDataSet<string, string, string> UriQueryData
{
get
{
return UriQueryTestData.UriQueryData;
}
}
[Theory]
[InlineData("N", "N", "")]
[InlineData("%26", "&", "")]
[PropertyData("UriQueryData")]
public void ParseBufferCorrectly(string segment, string name, string value)
{
for (int index = 1; index < Iterations; index++)
{
List<string> segments = new List<string>();
for (int cnt = 0; cnt < index; cnt++)
{
segments.Add(segment);
}
byte[] data = CreateBuffer(segments.ToArray());
for (var cnt = 1; cnt <= data.Length; cnt++)
{
ICollection<KeyValuePair<string, string>> collection;
FormUrlEncodedParser parser = CreateParser(data.Length + 1, out collection);
Assert.NotNull(parser);
int totalBytesConsumed;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(index, collection.Count());
foreach (KeyValuePair<string, string> element in collection)
{
Assert.Equal(name, element.Key);
Assert.Equal(value, element.Value);
}
}
}
}
[Fact]
public void HeaderParserDataTooBig()
{
byte[] data = CreateBuffer("N=V");
ICollection<KeyValuePair<string, string>> collection;
FormUrlEncodedParser parser = CreateParser(MinMessageSize, out collection);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed, true);
Assert.Equal(ParserState.DataTooBig, state);
Assert.Equal(MinMessageSize, bytesConsumed);
}
}
}

View File

@ -0,0 +1,277 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting.DataSets;
using System.Text;
using Microsoft.TestCommon;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Net.Http.Formatting.Parsers
{
public class HttpRequestHeaderParserTests
{
[Fact]
[Trait("Description", "HttpRequestHeaderParser is internal class")]
public void TypeIsCorrect()
{
Assert.Type.HasProperties<HttpRequestHeaderParser>(TypeAssert.TypeProperties.IsClass);
}
private static byte[] CreateBuffer(string method, string address, string version, Dictionary<string, string> headers)
{
const string SP = " ";
const string CRLF = "\r\n";
string lws = SP;
StringBuilder request = new StringBuilder();
request.AppendFormat("{0}{1}{2}{3}{4}{5}", method, lws, address, lws, version, CRLF);
if (headers != null)
{
foreach (var h in headers)
{
request.AppendFormat("{0}: {1}{2}", h.Key, h.Value, CRLF);
}
}
request.Append(CRLF);
return Encoding.UTF8.GetBytes(request.ToString());
}
private static ParserState ParseBufferInSteps(HttpRequestHeaderParser parser, byte[] buffer, int readsize, out int totalBytesConsumed)
{
ParserState state = ParserState.Invalid;
totalBytesConsumed = 0;
while (totalBytesConsumed <= buffer.Length)
{
int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
byte[] parseBuffer = new byte[size];
Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
int bytesConsumed = 0;
state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed);
totalBytesConsumed += bytesConsumed;
if (state != ParserState.NeedMoreData)
{
return state;
}
}
return state;
}
private static void ValidateResult(
HttpUnsortedRequest requestLine,
string method,
string requestUri,
Version version,
Dictionary<string, string> headers)
{
Assert.Equal(new HttpMethod(method), requestLine.Method);
Assert.Equal(requestUri, requestLine.RequestUri);
Assert.Equal(version, requestLine.Version);
if (headers != null)
{
Assert.Equal(headers.Count, requestLine.HttpHeaders.Count());
foreach (var header in headers)
{
Assert.True(requestLine.HttpHeaders.Contains(header.Key), "Parsed header did not contain expected key " + header.Key);
Assert.Equal(header.Value, requestLine.HttpHeaders.GetValues(header.Key).ElementAt(0));
}
}
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser constructor throws on invalid arguments")]
public void HttpRequestHeaderParserConstructorTest()
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
Assert.NotNull(result);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize - 1, ParserData.MinHeaderSize),
"maxRequestLineSize", ParserData.MinRequestLineSize.ToString(), ParserData.MinRequestLineSize - 1);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize - 1),
"maxHeaderSize", ParserData.MinHeaderSize.ToString(), ParserData.MinHeaderSize - 1);
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize);
Assert.NotNull(parser);
Assert.ThrowsArgumentNull(() => { new HttpRequestHeaderParser(null); }, "httpRequest");
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer throws on null buffer.")]
public void RequestHeaderParserNullBuffer()
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer");
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer parses minimum requestline.")]
public void RequestHeaderParserMinimumBuffer()
{
byte[] data = CreateBuffer("G", "/", "HTTP/1.1", null);
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result, ParserData.MinRequestLineSize, ParserData.MinHeaderSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, bytesConsumed);
ValidateResult(result, "G", "/", new Version("1.1"), null);
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer parses standard methods.")]
public void RequestHeaderParserAcceptsStandardMethods()
{
foreach (HttpMethod method in HttpUnitTestDataSets.AllHttpMethods)
{
byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(result, method.ToString(), "/", new Version("1.1"), ParserData.ValidHeaders);
}
}
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer parses custom methods.")]
public void RequestHeaderParserAcceptsCustomMethods()
{
foreach (HttpMethod method in HttpUnitTestDataSets.CustomHttpMethods)
{
byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(result, method.ToString(), "/", new Version("1.1"), ParserData.ValidHeaders);
}
}
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer rejects invalid method")]
public void RequestHeaderParserRejectsInvalidMethod()
{
foreach (string invalidMethod in ParserData.InvalidMethods)
{
byte[] data = CreateBuffer(invalidMethod, "/", "HTTP/1.1", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
[Fact]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer rejects invalid URI.")]
public void RequestHeaderParserRejectsInvalidUri()
{
foreach (string invalidRequestUri in ParserData.InvalidRequestUris)
{
byte[] data = CreateBuffer("GET", invalidRequestUri, "HTTP/1.1", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
public static IEnumerable<object[]> Versions
{
get { return ParserData.Versions; }
}
[Theory]
[PropertyData("Versions")]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer accepts valid versions.")]
public void RequestHeaderParserAcceptsValidVersion(Version version)
{
byte[] data = CreateBuffer("GET", "/", String.Format("HTTP/{0}", version.ToString(2)), ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(result, "GET", "/", version, ParserData.ValidHeaders);
}
}
public static IEnumerable<object[]> InvalidVersions
{
get { return ParserData.InvalidVersions; }
}
[Theory]
[PropertyData("InvalidVersions")]
[Trait("Description", "HttpRequestHeaderParser.ParseBuffer rejects lower case protocol version.")]
public void RequestHeaderParserRejectsInvalidVersion(string invalidVersion)
{
byte[] data = CreateBuffer("GET", "/", invalidVersion, ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest result = new HttpUnsortedRequest();
HttpRequestHeaderParser parser = new HttpRequestHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
}

View File

@ -0,0 +1,276 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http.Formatting.DataSets;
using System.Text;
using Microsoft.TestCommon;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Net.Http.Formatting.Parsers
{
public class HttpRequestLineParserTests
{
[Fact]
[Trait("Description", "HttpRequestLineParser is internal class")]
public void TypeIsCorrect()
{
Assert.Type.HasProperties<HttpRequestLineParser>(TypeAssert.TypeProperties.IsClass);
}
internal static byte[] CreateBuffer(string method, string address, string version)
{
return CreateBuffer(method, address, version, false);
}
private static byte[] CreateBuffer(string method, string address, string version, bool withLws)
{
const string SP = " ";
const string HTAB = "\t";
const string CRLF = "\r\n";
string lws = SP;
if (withLws)
{
lws = SP + SP + HTAB + SP;
}
string requestLine = String.Format("{0}{1}{2}{3}{4}{5}", method, lws, address, lws, version, CRLF);
return Encoding.UTF8.GetBytes(requestLine);
}
private static ParserState ParseBufferInSteps(HttpRequestLineParser parser, byte[] buffer, int readsize, out int totalBytesConsumed)
{
ParserState state = ParserState.Invalid;
totalBytesConsumed = 0;
while (totalBytesConsumed <= buffer.Length)
{
int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
byte[] parseBuffer = new byte[size];
Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
int bytesConsumed = 0;
state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed);
totalBytesConsumed += bytesConsumed;
if (state != ParserState.NeedMoreData)
{
return state;
}
}
return state;
}
private static void ValidateResult(HttpUnsortedRequest requestLine, string method, string requestUri, Version version)
{
Assert.Equal(new HttpMethod(method), requestLine.Method);
Assert.Equal(requestUri, requestLine.RequestUri);
Assert.Equal(version, requestLine.Version);
}
[Fact]
[Trait("Description", "HttpRequestLineParser constructor throws on invalid arguments")]
public void HttpRequestLineParserConstructorTest()
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
Assert.NotNull(requestLine);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize - 1),
"maxRequestLineSize", ParserData.MinRequestLineSize.ToString(), ParserData.MinRequestLineSize - 1);
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize);
Assert.NotNull(parser);
Assert.ThrowsArgumentNull(() => { new HttpRequestLineParser(null, ParserData.MinRequestLineSize); }, "httpRequest");
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer throws on null buffer.")]
public void RequestLineParserNullBuffer()
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer");
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer parses minimum requestline.")]
public void RequestLineParserMinimumBuffer()
{
byte[] data = CreateBuffer("G", "/", "HTTP/1.1");
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, ParserData.MinRequestLineSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, bytesConsumed);
ValidateResult(requestLine, "G", "/", new Version("1.1"));
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer rejects LWS requestline.")]
public void RequestLineParserRejectsLws()
{
byte[] data = CreateBuffer("GET", "/", "HTTP/1.1", true);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, data.Length);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer parses standard methods.")]
public void RequestLineParserAcceptsStandardMethods()
{
foreach (HttpMethod method in HttpUnitTestDataSets.AllHttpMethods)
{
byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, data.Length);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(requestLine, method.ToString(), "/", new Version("1.1"));
}
}
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer parses custom methods.")]
public void RequestLineParserAcceptsCustomMethods()
{
foreach (HttpMethod method in HttpUnitTestDataSets.CustomHttpMethods)
{
byte[] data = CreateBuffer(method.ToString(), "/", "HTTP/1.1");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, data.Length);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(requestLine, method.ToString(), "/", new Version("1.1"));
}
}
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer rejects invalid method")]
public void RequestLineParserRejectsInvalidMethod()
{
foreach (string invalidMethod in ParserData.InvalidMethods)
{
byte[] data = CreateBuffer(invalidMethod, "/", "HTTP/1.1");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
[Fact]
[Trait("Description", "HttpRequestLineParser.ParseBuffer rejects invalid URI.")]
public void RequestLineParserRejectsInvalidUri()
{
foreach (string invalidRequestUri in ParserData.InvalidRequestUris)
{
byte[] data = CreateBuffer("GET", invalidRequestUri, "HTTP/1.1");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
public static IEnumerable<object[]> Versions
{
get { return ParserData.Versions; }
}
[Theory]
[PropertyData("Versions")]
[Trait("Description", "HttpRequestLineParser.ParseBuffer accepts valid versions.")]
public void RequestLineParserAcceptsValidVersion(Version version)
{
byte[] data = CreateBuffer("GET", "/", String.Format("HTTP/{0}", version.ToString(2)));
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(requestLine, "GET", "/", version);
}
}
public static IEnumerable<object[]> InvalidVersions
{
get { return ParserData.InvalidVersions; }
}
[Theory]
[PropertyData("InvalidVersions")]
[Trait("Description", "HttpRequestLineParser.ParseBuffer rejects invalid protocol version.")]
public void RequestLineParserRejectsInvalidVersion(string invalidVersion)
{
byte[] data = CreateBuffer("GET", "/", invalidVersion);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedRequest requestLine = new HttpUnsortedRequest();
HttpRequestLineParser parser = new HttpRequestLineParser(requestLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
}

View File

@ -0,0 +1,276 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting.DataSets;
using System.Text;
using Microsoft.TestCommon;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Net.Http.Formatting.Parsers
{
public class HttpResponseHeaderParserTests
{
[Fact]
[Trait("Description", "HttpResponseHeaderParser is internal class")]
public void TypeIsCorrect()
{
Assert.Type.HasProperties<HttpResponseHeaderParser>(TypeAssert.TypeProperties.IsClass);
}
private static byte[] CreateBuffer(string version, string statusCode, string reasonPhrase, Dictionary<string, string> headers)
{
const string SP = " ";
const string CRLF = "\r\n";
string lws = SP;
StringBuilder response = new StringBuilder();
response.AppendFormat("{0}{1}{2}{3}{4}{5}", version, lws, statusCode, lws, reasonPhrase, CRLF);
if (headers != null)
{
foreach (var h in headers)
{
response.AppendFormat("{0}: {1}{2}", h.Key, h.Value, CRLF);
}
}
response.Append(CRLF);
return Encoding.UTF8.GetBytes(response.ToString());
}
private static ParserState ParseBufferInSteps(HttpResponseHeaderParser parser, byte[] buffer, int readsize, out int totalBytesConsumed)
{
ParserState state = ParserState.Invalid;
totalBytesConsumed = 0;
while (totalBytesConsumed <= buffer.Length)
{
int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
byte[] parseBuffer = new byte[size];
Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
int bytesConsumed = 0;
state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed);
totalBytesConsumed += bytesConsumed;
if (state != ParserState.NeedMoreData)
{
return state;
}
}
return state;
}
private static void ValidateResult(
HttpUnsortedResponse statusLine,
Version version,
HttpStatusCode statusCode,
string reasonPhrase,
Dictionary<string, string> headers)
{
Assert.Equal(version, statusLine.Version);
Assert.Equal(statusCode, statusLine.StatusCode);
Assert.Equal(reasonPhrase, statusLine.ReasonPhrase);
if (headers != null)
{
Assert.Equal(headers.Count, statusLine.HttpHeaders.Count());
foreach (var header in headers)
{
Assert.True(statusLine.HttpHeaders.Contains(header.Key), "Parsed header did not contain expected key " + header.Key);
Assert.Equal(header.Value, statusLine.HttpHeaders.GetValues(header.Key).ElementAt(0));
}
}
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser constructor throws on invalid arguments")]
public void HttpResponseHeaderParserConstructorTest()
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
Assert.NotNull(result);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize - 1, ParserData.MinHeaderSize),
"maxStatusLineSize", ParserData.MinStatusLineSize.ToString(), ParserData.MinStatusLineSize - 1);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize - 1),
"maxHeaderSize", ParserData.MinHeaderSize.ToString(), ParserData.MinHeaderSize - 1);
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize);
Assert.NotNull(parser);
Assert.ThrowsArgumentNull(() => { new HttpResponseHeaderParser(null); }, "httpResponse");
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer throws on null buffer.")]
public void ResponseHeaderParserNullBuffer()
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer");
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer parses minimum statusLine.")]
public void ResponseHeaderParserMinimumBuffer()
{
byte[] data = CreateBuffer("HTTP/1.1", "200", "", null);
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result, ParserData.MinStatusLineSize, ParserData.MinHeaderSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, bytesConsumed);
ValidateResult(result, new Version("1.1"), HttpStatusCode.OK, "", null);
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer parses standard status codes.")]
public void ResponseHeaderParserAcceptsStandardStatusCodes()
{
foreach (HttpStatusCode status in HttpUnitTestDataSets.AllHttpStatusCodes)
{
byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(result, new Version("1.1"), status, "Reason", ParserData.ValidHeaders);
}
}
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer parses custom status codes.")]
public void ResponseHeaderParserAcceptsCustomStatusCodes()
{
foreach (HttpStatusCode status in HttpUnitTestDataSets.CustomHttpStatusCodes)
{
byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(result, new Version("1.1"), status, "Reason", ParserData.ValidHeaders);
}
}
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer rejects invalid status codes")]
public void ResponseHeaderParserRejectsInvalidStatusCodes()
{
foreach (string invalidStatus in ParserData.InvalidStatusCodes)
{
byte[] data = CreateBuffer("HTTP/1.1", invalidStatus, "Reason", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
[Fact]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer rejects invalid reason phrase.")]
public void ResponseHeaderParserRejectsInvalidReasonPhrase()
{
foreach (string invalidReason in ParserData.InvalidReasonPhrases)
{
byte[] data = CreateBuffer("HTTP/1.1", "200", invalidReason, ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
public static IEnumerable<object[]> Versions
{
get { return ParserData.Versions; }
}
[Theory]
[PropertyData("Versions")]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer accepts valid versions.")]
public void ResponseHeaderParserAcceptsValidVersion(Version version)
{
byte[] data = CreateBuffer(String.Format("HTTP/{0}", version.ToString(2)), "200", "Reason", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(result, version, HttpStatusCode.OK, "Reason", ParserData.ValidHeaders);
}
}
public static IEnumerable<object[]> InvalidVersions
{
get { return ParserData.InvalidVersions; }
}
[Theory]
[PropertyData("InvalidVersions")]
[Trait("Description", "HttpResponseHeaderParser.ParseBuffer rejects invalid protocol version.")]
public void ResponseHeaderParserRejectsInvalidVersion(string invalid)
{
byte[] data = CreateBuffer(invalid, "200", "Reason", ParserData.ValidHeaders);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse result = new HttpUnsortedResponse();
HttpResponseHeaderParser parser = new HttpResponseHeaderParser(result);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
}

View File

@ -0,0 +1,287 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http.Formatting.DataSets;
using System.Text;
using Microsoft.TestCommon;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Net.Http.Formatting.Parsers
{
public class HttpStatusLineParserTests
{
[Fact]
[Trait("Description", "HttpStatusLineParser is internal class")]
public void TypeIsCorrect()
{
Assert.Type.HasProperties<HttpStatusLineParser>(TypeAssert.TypeProperties.IsClass);
}
internal static byte[] CreateBuffer(string version, string statusCode, string reasonPhrase)
{
return CreateBuffer(version, statusCode, reasonPhrase, false);
}
private static byte[] CreateBuffer(string version, string statusCode, string reasonPhrase, bool withLws)
{
const string SP = " ";
const string HTAB = "\t";
const string CRLF = "\r\n";
string lws = SP;
if (withLws)
{
lws = SP + SP + HTAB + SP;
}
string statusLine = String.Format("{0}{1}{2}{3}{4}{5}", version, lws, statusCode, lws, reasonPhrase, CRLF);
return Encoding.UTF8.GetBytes(statusLine);
}
private static ParserState ParseBufferInSteps(HttpStatusLineParser parser, byte[] buffer, int readsize, out int totalBytesConsumed)
{
ParserState state = ParserState.Invalid;
totalBytesConsumed = 0;
while (totalBytesConsumed <= buffer.Length)
{
int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
byte[] parseBuffer = new byte[size];
Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
int bytesConsumed = 0;
state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed);
totalBytesConsumed += bytesConsumed;
if (state != ParserState.NeedMoreData)
{
return state;
}
}
return state;
}
private static void ValidateResult(HttpUnsortedResponse statusLine, Version version, HttpStatusCode statusCode, string reasonPhrase)
{
Assert.Equal(version, statusLine.Version);
Assert.Equal(statusCode, statusLine.StatusCode);
Assert.Equal(reasonPhrase, statusLine.ReasonPhrase);
}
[Fact]
[Trait("Description", "HttpStatusLineParser constructor throws on invalid arguments")]
public void HttpStatusLineParserConstructorTest()
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
Assert.NotNull(statusLine);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize - 1),
"maxStatusLineSize", ParserData.MinStatusLineSize.ToString(), ParserData.MinStatusLineSize - 1);
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize);
Assert.NotNull(parser);
Assert.ThrowsArgumentNull(() => { new HttpStatusLineParser(null, ParserData.MinStatusLineSize); }, "httpResponse");
}
[Fact]
[Trait("Description", "HttpStatusLineParser.ParseBuffer throws on null buffer.")]
public void StatusLineParserNullBuffer()
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed); }, "buffer");
}
[Fact]
[Trait("Description", "HttpStatusLineParser.ParseBuffer parses minimum requestline.")]
public void StatusLineParserMinimumBuffer()
{
byte[] data = CreateBuffer("HTTP/1.1", "200", "");
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, ParserData.MinStatusLineSize);
Assert.NotNull(parser);
int bytesConsumed = 0;
ParserState state = parser.ParseBuffer(data, data.Length, ref bytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, bytesConsumed);
ValidateResult(statusLine, new Version("1.1"), HttpStatusCode.OK, "");
}
[Fact]
[Trait("Description", "HttpStatusLineParser.ParseBuffer rejects LWS requestline.")]
public void StatusLineParserRejectsLws()
{
byte[] data = CreateBuffer("HTTP/1.1", "200", "Reason", true);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, data.Length);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
[Fact]
[Trait("Description", "HttpStatusLineParser.ParseBuffer parses standard status codes.")]
public void StatusLineParserAcceptsStandardStatusCodes()
{
foreach (HttpStatusCode status in HttpUnitTestDataSets.AllHttpStatusCodes)
{
byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, data.Length);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(statusLine, new Version("1.1"), status, "Reason");
}
}
}
[Fact]
[Trait("Description", "HttpStatusLineParser.ParseBuffer parses custom status codes.")]
public void StatusLineParserAcceptsCustomStatusCodes()
{
foreach (HttpStatusCode status in HttpUnitTestDataSets.CustomHttpStatusCodes)
{
byte[] data = CreateBuffer("HTTP/1.1", ((int)status).ToString(), "Reason");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, data.Length);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Done, state);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(statusLine, new Version("1.1"), status, "Reason");
}
}
}
[Fact]
[Trait("Description", "HttpStatusLineParser.ParseBuffer rejects invalid status codes")]
public void StatusLineParserRejectsInvalidStatusCodes()
{
foreach (string invalidStatus in ParserData.InvalidStatusCodes)
{
byte[] data = CreateBuffer("HTTP/1.1", invalidStatus, "Reason");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
public static IEnumerable<object[]> ValidReasonPhrases
{
get
{
yield return new object[] { "" };
yield return new object[] { "Ok" };
yield return new object[] { "public Server Error" };
yield return new object[] { "r e a s o n" };
yield return new object[] { "reason " };
yield return new object[] { " reason " };
}
}
[Theory]
[PropertyData("ValidReasonPhrases")]
[Trait("Description", "HttpStatusLineParser.ParseBuffer accepts valid reason phrase.")]
public void StatusLineParserAcceptsValidReasonPhrase(string validReasonPhrase)
{
byte[] data = CreateBuffer("HTTP/1.1", "200", validReasonPhrase);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
ValidateResult(statusLine, new Version("1.1"), HttpStatusCode.OK, validReasonPhrase);
}
}
public static IEnumerable<object[]> Versions
{
get { return ParserData.Versions; }
}
[Theory]
[PropertyData("Versions")]
[Trait("Description", "HttpStatusLineParser.ParseBuffer accepts valid versions.")]
public void StatusLineParserAcceptsValidVersion(Version version)
{
byte[] data = CreateBuffer(String.Format("HTTP/{0}", version.ToString(2)), "200", "Reason");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(data.Length, totalBytesConsumed);
ValidateResult(statusLine, version, HttpStatusCode.OK, "Reason");
}
}
public static IEnumerable<object[]> InvalidVersions
{
get { return ParserData.InvalidVersions; }
}
[Theory]
[PropertyData("InvalidVersions")]
[Trait("Description", "HttpStatusLineParser.ParseBuffer rejects invalid protocol version.")]
public void StatusLineParserRejectsInvalidVersion(string invalidVersion)
{
byte[] data = CreateBuffer(invalidVersion, "200", "Reason");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
HttpUnsortedResponse statusLine = new HttpUnsortedResponse();
HttpStatusLineParser parser = new HttpStatusLineParser(statusLine, 256);
Assert.NotNull(parser);
int totalBytesConsumed = 0;
ParserState state = ParseBufferInSteps(parser, data, cnt, out totalBytesConsumed);
Assert.Equal(ParserState.Invalid, state);
}
}
}
}

View File

@ -0,0 +1,485 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.TestCommon;
using Xunit;
using Xunit.Extensions;
using Assert = Microsoft.TestCommon.AssertEx;
namespace System.Net.Http.Formatting.Parsers
{
public class MimeMultipartParserTests
{
[Fact]
public void MimeMultipartParserTypeIsCorrect()
{
Assert.Type.HasProperties<InternetMessageFormatHeaderParser>(TypeAssert.TypeProperties.IsClass);
}
private static MimeMultipartParser CreateMimeMultipartParser(int maximumHeaderLength, string boundary)
{
return new MimeMultipartParser(boundary, maximumHeaderLength);
}
internal static byte[] CreateBuffer(string boundary, params string[] bodyparts)
{
return CreateBuffer(boundary, false, bodyparts);
}
internal static string CreateNestedBuffer(int count)
{
StringBuilder buffer = new StringBuilder("content");
for (var cnt = 0; cnt < count; cnt++)
{
byte[] nested = CreateBuffer("N" + cnt.ToString(), buffer.ToString());
var message = Encoding.UTF8.GetString(nested);
buffer.Length = 0;
buffer.AppendLine(message);
}
return buffer.ToString();
}
private static byte[] CreateBuffer(string boundary, bool withLws, params string[] bodyparts)
{
const string SP = " ";
const string HTAB = "\t";
const string CRLF = "\r\n";
const string DashDash = "--";
string lws = String.Empty;
if (withLws)
{
lws = SP + SP + HTAB + SP;
}
StringBuilder message = new StringBuilder();
message.Append(DashDash + boundary + lws + CRLF);
for (var cnt = 0; cnt < bodyparts.Length; cnt++)
{
message.Append(bodyparts[cnt]);
if (cnt < bodyparts.Length - 1)
{
message.Append(CRLF + DashDash + boundary + lws + CRLF);
}
}
// Note: We rely on a final CRLF even though it is not required by the BNF existing application do send it
message.Append(CRLF + DashDash + boundary + DashDash + lws + CRLF);
return Encoding.UTF8.GetBytes(message.ToString());
}
private static MimeMultipartParser.State ParseBufferInSteps(MimeMultipartParser parser, byte[] buffer, int readsize, out List<string> bodyParts, out int totalBytesConsumed)
{
MimeMultipartParser.State state = MimeMultipartParser.State.Invalid;
totalBytesConsumed = 0;
bodyParts = new List<string>();
bool isFinal = false;
byte[] currentBodyPart = new byte[32 * 1024];
int currentBodyLength = 0;
while (totalBytesConsumed <= buffer.Length)
{
int size = Math.Min(buffer.Length - totalBytesConsumed, readsize);
byte[] parseBuffer = new byte[size];
Buffer.BlockCopy(buffer, totalBytesConsumed, parseBuffer, 0, size);
int bytesConsumed = 0;
ArraySegment<byte> out1;
ArraySegment<byte> out2;
state = parser.ParseBuffer(parseBuffer, parseBuffer.Length, ref bytesConsumed, out out1, out out2, out isFinal);
totalBytesConsumed += bytesConsumed;
Buffer.BlockCopy(out1.Array, out1.Offset, currentBodyPart, currentBodyLength, out1.Count);
currentBodyLength += out1.Count;
Buffer.BlockCopy(out2.Array, out2.Offset, currentBodyPart, currentBodyLength, out2.Count);
currentBodyLength += out2.Count;
if (state == MimeMultipartParser.State.BodyPartCompleted)
{
var bPart = new byte[currentBodyLength];
Buffer.BlockCopy(currentBodyPart, 0, bPart, 0, currentBodyLength);
bodyParts.Add(Encoding.UTF8.GetString(bPart));
currentBodyLength = 0;
if (isFinal)
{
break;
}
}
else if (state != MimeMultipartParser.State.NeedMoreData)
{
return state;
}
}
Assert.True(isFinal);
return state;
}
public static IEnumerable<object[]> Boundaries
{
get { return ParserData.Boundaries; }
}
[Theory]
[PropertyData("Boundaries")]
public void MimeMultipartParserConstructorTest(string boundary)
{
MimeMultipartParser parser = new MimeMultipartParser(boundary, ParserData.MinMessageSize);
Assert.NotNull(parser);
Assert.ThrowsArgumentGreaterThanOrEqualTo(() => new MimeMultipartParser("-", ParserData.MinMessageSize - 1),
"maxMessageSize", ParserData.MinMessageSize.ToString(), ParserData.MinMessageSize - 1);
foreach (string empty in TestData.EmptyStrings)
{
Assert.ThrowsArgument(() => { new MimeMultipartParser(empty, ParserData.MinMessageSize); }, "boundary", allowDerivedExceptions: true);
}
Assert.ThrowsArgument(() => { new MimeMultipartParser("trailingspace ", ParserData.MinMessageSize); }, "boundary");
Assert.ThrowsArgumentNull(() => { new MimeMultipartParser(null, ParserData.MinMessageSize); }, "boundary");
}
[Fact]
public void MultipartParserNullBuffer()
{
MimeMultipartParser parser = CreateMimeMultipartParser(128, "-");
Assert.NotNull(parser);
int bytesConsumed = 0;
ArraySegment<byte> out1;
ArraySegment<byte> out2;
bool isFinal;
Assert.ThrowsArgumentNull(() => { parser.ParseBuffer(null, 0, ref bytesConsumed, out out1, out out2, out isFinal); }, "buffer");
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserEmptyBuffer(string boundary)
{
byte[] data = CreateBuffer(boundary);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(2, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
Assert.Equal(0, bodyParts[1].Length);
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserSingleShortBodyPart(string boundary)
{
byte[] data = CreateBuffer(boundary, "A");
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(2, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
Assert.Equal(1, bodyParts[1].Length);
Assert.Equal("A", bodyParts[1]);
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserMultipleShortBodyParts(string boundary)
{
string[] text = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
byte[] data = CreateBuffer(boundary, text);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(text.Length + 1, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
for (var check = 0; check < text.Length; check++)
{
Assert.Equal(1, bodyParts[check + 1].Length);
Assert.Equal(text[check], bodyParts[check + 1]);
}
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserMultipleShortBodyPartsWithLws(string boundary)
{
string[] text = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
byte[] data = CreateBuffer(boundary, true, text);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(text.Length + 1, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
for (var check = 0; check < text.Length; check++)
{
Assert.Equal(1, bodyParts[check + 1].Length);
Assert.Equal(text[check], bodyParts[check + 1]);
}
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserSingleLongBodyPart(string boundary)
{
const string text = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
byte[] data = CreateBuffer(boundary, text);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(2, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
Assert.Equal(text.Length, bodyParts[1].Length);
Assert.Equal(text, bodyParts[1]);
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserMultipleLongBodyParts(string boundary)
{
const string middleText = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
string[] text = new string[] {
"A" + middleText + "A",
"B" + middleText + "B",
"C" + middleText + "C",
"D" + middleText + "D",
"E" + middleText + "E",
"F" + middleText + "F",
"G" + middleText + "G",
"H" + middleText + "H",
"I" + middleText + "I",
"J" + middleText + "J",
"K" + middleText + "K",
"L" + middleText + "L",
"M" + middleText + "M",
"N" + middleText + "N",
"O" + middleText + "O",
"P" + middleText + "P",
"Q" + middleText + "Q",
"R" + middleText + "R",
"S" + middleText + "S",
"T" + middleText + "T",
"U" + middleText + "U",
"V" + middleText + "V",
"W" + middleText + "W",
"X" + middleText + "X",
"Y" + middleText + "Y",
"Z" + middleText + "Z"};
byte[] data = CreateBuffer(boundary, text);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(text.Length + 1, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
for (var check = 0; check < text.Length; check++)
{
Assert.Equal(text[check].Length, bodyParts[check + 1].Length);
Assert.Equal(text[check], bodyParts[check + 1]);
}
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserNearMatches(string boundary)
{
const string CR = "\r";
const string CRLF = "\r\n";
const string Dash = "-";
const string DashDash = "--";
string[] text = new string[] {
CR + Dash + "AAA",
CRLF + Dash + "AAA",
CRLF + DashDash + "AAA" + CR + "AAA",
CRLF,
"AAA",
"AAA" + CRLF,
CRLF + CRLF,
CRLF + CRLF + CRLF,
"AAA" + DashDash + "AAA",
CRLF + "AAA" + DashDash + "AAA" + DashDash,
CRLF + DashDash + "AAA" + CRLF,
CRLF + DashDash + "AAA" + CRLF + CRLF,
CRLF + DashDash + "AAA" + DashDash + CRLF,
CRLF + DashDash + "AAA" + DashDash + CRLF + CRLF
};
byte[] data = CreateBuffer(boundary, text);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(text.Length + 1, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
for (var check = 0; check < text.Length; check++)
{
Assert.Equal(text[check].Length, bodyParts[check + 1].Length);
Assert.Equal(text[check], bodyParts[check + 1]);
}
}
}
[Theory]
[PropertyData("Boundaries")]
public void MultipartParserNesting(string boundary)
{
for (var nesting = 0; nesting < 16; nesting++)
{
string nested = CreateNestedBuffer(nesting);
byte[] data = CreateBuffer(boundary, nested);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(2, bodyParts.Count);
Assert.Equal(0, bodyParts[0].Length);
Assert.Equal(nested.Length, bodyParts[1].Length);
}
}
}
[Theory]
[PropertyData("Boundaries")]
public void MimeMultipartParserTestDataTooBig(string boundary)
{
byte[] data = CreateBuffer(boundary);
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(ParserData.MinMessageSize, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.DataTooBig, state);
Assert.Equal(ParserData.MinMessageSize, totalBytesConsumed);
}
}
[Theory]
[PropertyData("Boundaries")]
public void MimeMultipartParserTestMultipartContent(string boundary)
{
MultipartContent content = new MultipartContent("mixed", boundary);
content.Add(new StringContent("A"));
content.Add(new StringContent("B"));
content.Add(new StringContent("C"));
MemoryStream memStream = new MemoryStream();
content.CopyToAsync(memStream).Wait();
memStream.Position = 0;
byte[] data = memStream.ToArray();
for (var cnt = 1; cnt <= data.Length; cnt++)
{
MimeMultipartParser parser = CreateMimeMultipartParser(data.Length, boundary);
Assert.NotNull(parser);
int totalBytesConsumed;
List<string> bodyParts;
MimeMultipartParser.State state = ParseBufferInSteps(parser, data, cnt, out bodyParts, out totalBytesConsumed);
Assert.Equal(MimeMultipartParser.State.BodyPartCompleted, state);
Assert.Equal(data.Length, totalBytesConsumed);
Assert.Equal(4, bodyParts.Count);
Assert.Empty(bodyParts[0]);
Assert.True(bodyParts[1].EndsWith("A"));
Assert.True(bodyParts[2].EndsWith("B"));
Assert.True(bodyParts[3].EndsWith("C"));
}
}
}
}