Http Abstractions for parsing Http headers needed for conneg.

This commit is contained in:
harshgMSFT 2014-07-15 17:51:24 -07:00
parent d13f6474d8
commit b8ab5c5063
13 changed files with 599 additions and 0 deletions

26
Mvc.sln
View File

@ -2,6 +2,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.21901.1
VisualStudioVersion = 14.0.21806.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -54,6 +55,9 @@ EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorWebSite", "test\WebSites\RazorWebSite\RazorWebSite.kproj", "{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValueProvidersSite", "test\WebSites\ValueProvidersSite\ValueProvidersSite.kproj", "{14F79E79-AE79-48FA-95DE-D794EF4EABB3}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.HeaderValueAbstractions", "src\Microsoft.AspNet.Mvc.HeaderValueAbstractions\Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj", "{98335B23-E4B9-4CAD-9749-0DED32A659A1}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.HeaderValueAbstractions.Tests", "test\Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test\Microsoft.AspNet.Mvc.HeaderValueAbstractions.Tests.kproj", "{E69FD235-2042-43A4-9970-59CB29955B4E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -285,6 +289,26 @@ Global
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|x86.ActiveCfg = Release|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Debug|x86.ActiveCfg = Debug|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Release|Any CPU.Build.0 = Release|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{98335B23-E4B9-4CAD-9749-0DED32A659A1}.Release|x86.ActiveCfg = Release|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Release|Any CPU.Build.0 = Release|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E69FD235-2042-43A4-9970-59CB29955B4E}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -313,5 +337,7 @@ Global
{A853B2BA-4449-4908-A416-5A3C027FC22B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{B07CAF59-11ED-40E3-A5DB-E1178F84FA78} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{14F79E79-AE79-48FA-95DE-D794EF4EABB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{98335B23-E4B9-4CAD-9749-0DED32A659A1} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{E69FD235-2042-43A4-9970-59CB29955B4E} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public static class FormattingUtilities
{
/// <summary>
/// Quality factor to indicate a perfect match.
/// </summary>
public const double Match = 1.0;
/// <summary>
/// Quality factor to indicate no match.
/// </summary>
public const double NoMatch = 0.0;
}
}

View File

@ -0,0 +1,187 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public class MediaTypeHeaderValue
{
public string Charset { get; set; }
public string MediaType { get; set; }
public string MediaSubType { get; set; }
public MediaTypeHeaderValueRange MediaTypeRange { get; set; }
public string RawValue
{
get
{
var stringBuilder = new StringBuilder();
stringBuilder.Append(MediaType);
stringBuilder.Append('/');
stringBuilder.Append(MediaSubType);
if (!string.IsNullOrEmpty(Charset))
{
stringBuilder.Append(";charset=");
stringBuilder.Append(Charset);
}
foreach (var parameter in Parameters)
{
if (string.Equals(parameter.Key, "charset", System.StringComparison.OrdinalIgnoreCase))
{
continue;
}
stringBuilder.Append(";");
stringBuilder.Append(parameter.Key);
stringBuilder.Append("=");
stringBuilder.Append(parameter.Value);
}
return stringBuilder.ToString();
}
}
public IDictionary<string, string> Parameters { get; set; }
public static MediaTypeHeaderValue Parse(string input)
{
if (string.IsNullOrEmpty(input))
{
return null;
}
var inputArray = input.Split(new[] { ';' }, 2);
var mediaTypeParts = inputArray[0].Split('/');
if (mediaTypeParts.Length != 2)
{
return null;
}
// TODO: throw if the media type and subtypes are invalid.
var mediaType = mediaTypeParts[0].Trim();
var mediaSubType = mediaTypeParts[1].Trim();
var mediaTypeRange = MediaTypeHeaderValueRange.None;
if (mediaType == "*" && mediaSubType == "*")
{
mediaTypeRange = MediaTypeHeaderValueRange.AllMediaRange;
}
else if (mediaSubType == "*")
{
mediaTypeRange = MediaTypeHeaderValueRange.SubtypeMediaRange;
}
Dictionary<string, string> parameters = null;
string charset = null;
if (inputArray.Length == 2)
{
parameters = ParseParameters(inputArray[1]);
parameters.TryGetValue("charset", out charset);
}
var mediaTypeHeader = new MediaTypeHeaderValue()
{
MediaType = mediaType,
MediaSubType = mediaSubType,
MediaTypeRange = mediaTypeRange,
Charset = charset,
Parameters = parameters ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase),
};
return mediaTypeHeader;
}
protected static Dictionary<string, string> ParseParameters(string inputString)
{
var acceptParameters = inputString.Split(';');
var parameterNameValue = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var parameter in acceptParameters)
{
var index = parameter.Split('=');
if (index.Length == 2)
{
// TODO: throw exception if this is not the case.
parameterNameValue.Add(index[0].Trim(), index[1].Trim());
}
}
return parameterNameValue;
}
/// <summary>
/// Determines whether this instance is a subset of passed <see cref="MediaTypeHeaderValue"/>.
/// If the media type and media type parameters of this media type are all present
/// and match those of <paramref name="otherMediaType"/> then it is a match even though
/// <paramref name="otherMediaType"/> may have additional parameters.
/// </summary>
/// <param name="mediaType">The first media type.</param>
/// <param name="otherMediaType">The second media type.</param>
/// <returns><c>true</c> if this is a subset of <paramref name="otherMediaType"/>; false otherwise.</returns>
public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType)
{
if (otherMediaType == null)
{
return false;
}
if (!MediaType.Equals(otherMediaType.MediaType, StringComparison.OrdinalIgnoreCase))
{
if (otherMediaType.MediaTypeRange != MediaTypeHeaderValueRange.AllMediaRange)
{
return false;
}
}
else if (!MediaSubType.Equals(otherMediaType.MediaSubType, StringComparison.OrdinalIgnoreCase))
{
if (otherMediaType.MediaTypeRange != MediaTypeHeaderValueRange.SubtypeMediaRange)
{
return false;
}
}
if (Parameters != null)
{
if (Parameters.Count != 0 &&
(otherMediaType.Parameters == null || otherMediaType.Parameters.Count == 0))
{
return false;
}
// So far we either have a full match or a subset match. Now check that all of
// mediaType1's parameters are present and equal in mediatype2
if(!MatchParameters(Parameters, otherMediaType.Parameters))
{
return false;
}
}
return true;
}
private static bool MatchParameters(IDictionary<string, string> parameters1,
IDictionary<string, string> parameters2)
{
foreach (var parameterKey in parameters1.Keys)
{
string parameterValue2 = null;
if (!parameters2.TryGetValue(parameterKey, out parameterValue2))
{
return false;
}
if (parameterValue2 == null || !parameterValue2.Equals(parameters1[parameterKey]))
{
return false;
}
}
return true;
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc
{
public enum MediaTypeHeaderValueRange
{
/// <summary>
/// Not a media type range
/// </summary>
None = 0,
/// <summary>
/// A subtype media range, e.g. "application/*".
/// </summary>
SubtypeMediaRange,
/// <summary>
/// An all media range, e.g. "*/*".
/// </summary>
AllMediaRange,
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public class MediaTypeWithQualityHeaderValue : MediaTypeHeaderValue
{
public double? Quality { get; private set; }
public static new MediaTypeWithQualityHeaderValue Parse(string input)
{
var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(input);
if (mediaTypeHeaderValue == null)
{
return null;
}
var quality = FormattingUtilities.Match;
string qualityStringValue = null;
if (mediaTypeHeaderValue.Parameters.TryGetValue("q", out qualityStringValue))
{
if(!Double.TryParse(qualityStringValue, out quality))
{
return null;
}
}
return
new MediaTypeWithQualityHeaderValue()
{
MediaType = mediaTypeHeaderValue.MediaType,
MediaSubType = mediaTypeHeaderValue.MediaSubType,
MediaTypeRange = mediaTypeHeaderValue.MediaTypeRange,
Charset = mediaTypeHeaderValue.Charset,
Parameters = mediaTypeHeaderValue.Parameters,
Quality = quality,
};
}
}
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>98335b23-e4b9-4cad-9749-0ded32a659a1</ProjectGuid>
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="FormattingUtilities.cs" />
<Compile Include="MediaTypeHeaderValue.cs" />
<Compile Include="MediaTypeHeaderValueRange.cs" />
<Compile Include="MediaTypeWithQualityHeaderValue.cs" />
<Compile Include="StringWithQualityHeaderValue.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="project.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public class StringWithQualityHeaderValue
{
public double? Quality { get; set; }
public string RawValue { get; set; }
public string Value { get; set; }
public static StringWithQualityHeaderValue Parse(string input)
{
var inputArray = input.Split(new[] { ';' }, 2);
var value = inputArray[0].Trim();
// Unspecified q factor value is equal to a match.
var quality = FormattingUtilities.Match;
if (inputArray.Length > 1)
{
var parameter = inputArray[1].Trim();
var nameValuePair = parameter.Split(new[] { '=' }, 2);
if (nameValuePair.Length > 1 && nameValuePair[0].Trim().Equals("q"))
{
// TODO: all extraneous parameters are ignored. Throw/return null if that is the case.
if(!Double.TryParse(nameValuePair[1].Trim(), out quality))
{
return null;
}
}
}
var stringWithQualityHeader = new StringWithQualityHeaderValue()
{
Quality = quality,
Value = value,
RawValue = input
};
return stringWithQualityHeader;
}
}
}

View File

@ -0,0 +1,14 @@
{
"version": "1.0.0-*",
"dependencies": {},
"configurations": {
"net45": {},
"k10": {
"dependencies": {
"System.Collections": "4.0.0.0",
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0"
}
}
}
}

View File

@ -13,6 +13,7 @@
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
"Microsoft.Framework.Runtime.Interfaces": "1.0.0-*",
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
"RoutingWebSite": "",
"RazorWebSite": "",
"ValueProvidersSite": "",

View File

@ -0,0 +1,135 @@
// Copyright (c) Microsoft Open Technologies, Inc. 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 Xunit;
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public class MediaTypeHeaderValueParsingTests
{
public static IEnumerable<object[]> GetValidMediaTypeWithQualityHeaderValues
{
get
{
yield return new object[]
{
"*",
"*",
null,
MediaTypeHeaderValueRange.AllMediaRange,
new Dictionary<string, string>(),
FormattingUtilities.Match,
"*/*"
};
yield return new object[]
{
"text",
"*",
"utf-8",
MediaTypeHeaderValueRange.SubtypeMediaRange,
new Dictionary<string, string>() { { "charset", "utf-8" }, { "foo", "bar" } },
FormattingUtilities.Match,
"text/*;charset=utf-8;foo=bar",
};
yield return new object[]
{
"text",
"plain",
"utf-8",
MediaTypeHeaderValueRange.None,
new Dictionary<string, string>() { { "charset", "utf-8" }, { "foo", "bar" }, { "q", "0.0" } },
FormattingUtilities.NoMatch,
"text/plain;charset=utf-8;foo=bar;q=0.0",
};
}
}
[Theory]
[MemberData("GetValidMediaTypeWithQualityHeaderValues")]
public void MediaTypeWithQualityHeaderValue_ParseSuccessfully(string mediaType,
string mediaSubType,
string charset,
MediaTypeHeaderValueRange range,
IDictionary<string, string> parameters,
double quality,
string rawValue)
{
var parsedValue = MediaTypeWithQualityHeaderValue.Parse(rawValue);
// Act and Assert
Assert.Equal(rawValue, parsedValue.RawValue);
Assert.Equal(mediaType, parsedValue.MediaType);
Assert.Equal(mediaSubType, parsedValue.MediaSubType);
Assert.Equal(charset, parsedValue.Charset);
Assert.Equal(range, parsedValue.MediaTypeRange);
ValidateParametes(parameters, parsedValue.Parameters);
}
[Theory]
[InlineData("*/*;", "*/*", true)]
[InlineData("text/*;", "text/*", true)]
[InlineData("text/plain;", "text/plain", true)]
[InlineData("*/*;", "*/*;charset=utf-8;", true)]
[InlineData("text/*;", "*/*;charset=utf-8;", true)]
[InlineData("text/plain;", "*/*;charset=utf-8;", true)]
[InlineData("text/plain;", "text/*;charset=utf-8;", true)]
[InlineData("text/plain;", "text/plain;charset=utf-8;", true)]
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/plain;charset=utf-8;foo=bar;q=0.0", true)]
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/*;charset=utf-8;foo=bar;q=0.0", true)]
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "*/*;charset=utf-8;foo=bar;q=0.0", true)]
[InlineData("*/*;", "text/plain;charset=utf-8;foo=bar;q=0.0", false)]
[InlineData("text/*;", "text/plain;charset=utf-8;foo=bar;q=0.0", false)]
[InlineData("text/plain;missingparam=4;", "text/plain;charset=utf-8;foo=bar;q=0.0", false)]
[InlineData("text/plain;missingparam=4;", "text/*;charset=utf-8;foo=bar;q=0.0", false)]
[InlineData("text/plain;missingparam=4;", "*/*;charset=utf-8;foo=bar;q=0.0", false)]
public void MediaTypeHeaderValue_IsSubTypeTests(string mediaType1,
string mediaType2,
bool isMediaType1Subset)
{
var parsedMediaType1 = MediaTypeWithQualityHeaderValue.Parse(mediaType1);
var parsedMediaType2 = MediaTypeWithQualityHeaderValue.Parse(mediaType2);
// Act
var isSubset = parsedMediaType1.IsSubsetOf(parsedMediaType2);
// Assert
Assert.Equal(isMediaType1Subset, isSubset);
}
[Theory]
[InlineData("text/plain;charset=utf-16;foo=bar", "text/plain;charset=utf-8;foo=bar")]
[InlineData("text/plain;charset=utf-16;foo=bar", "text/plain;charset=utf-16;foo=bar1")]
[InlineData("text/plain;charset=utf-16;foo=bar", "text/json;charset=utf-16;foo=bar")]
[InlineData("text/plain;charset=utf-16;foo=bar", "application/plain;charset=utf-16;foo=bar")]
[InlineData("text/plain;charset=utf-16;foo=bar", "application/json;charset=utf-8;foo=bar1")]
public void MediaTypeHeaderValue_UpdateValue_RawValueGetsUpdated(string mediaTypeValue,
string expectedRawValue)
{
// Arrange
var parsedOldValue = MediaTypeHeaderValue.Parse(mediaTypeValue);
var parsedNewValue = MediaTypeHeaderValue.Parse(expectedRawValue);
// Act
parsedOldValue.Charset = parsedNewValue.Charset;
parsedOldValue.Parameters = parsedNewValue.Parameters;
parsedOldValue.MediaType = parsedNewValue.MediaType;
parsedOldValue.MediaSubType = parsedNewValue.MediaSubType;
parsedOldValue.MediaTypeRange = parsedNewValue.MediaTypeRange;
// Assert
Assert.Equal(expectedRawValue, parsedOldValue.RawValue);
}
private static void ValidateParametes(IDictionary<string, string> expectedParameters,
IDictionary<string, string> actualParameters)
{
Assert.Equal(expectedParameters.Count, actualParameters.Count);
foreach (var key in expectedParameters.Keys)
{
Assert.Equal(expectedParameters[key], actualParameters[key]);
}
}
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>e69fd235-2042-43a4-9970-59cb29955b4e</ProjectGuid>
<OutputType>Library</OutputType>
<ActiveTargetFramework>net45</ActiveTargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Content Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="StringWithQualityHeaderValueParsingTests.cs" />
<Compile Include="MediaTypeHeaderValueParsingTests.cs" />
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Xunit;
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public class StringWithQualityHeaderValueParsingTests
{
[Theory]
[InlineData("*", FormattingUtilities.Match, "*")]
[InlineData("*", 0.7, "*;q=.7")]
[InlineData("iso-8859-5", FormattingUtilities.Match, "iso-8859-5")]
[InlineData("unicode-1-1", 0.8, "unicode-1-1;q=0.8")]
[InlineData("unicode-1-1", 0.8, "unicode-1-1;q =0.8")]
[InlineData("unicode-1-1", 0.8, "unicode-1-1;q = 0.8")]
[InlineData("unicode-1-1", 1.0, "unicode-1-1;quxx = 0.8")] // quxx gets ignored.
public void StringWithQualityHeaderValue_ParseSuccessfully(string value,
double quality,
string rawValue)
{
var parsedValue = StringWithQualityHeaderValue.Parse(rawValue);
// Act and Assert
Assert.Equal(rawValue, parsedValue.RawValue);
Assert.Equal(value, parsedValue.Value);
Assert.Equal(quality, parsedValue.Quality);
}
}
}

View File

@ -0,0 +1,18 @@
{
"dependencies": {
"Microsoft.AspNet.Http": "",
"Microsoft.AspNet.Mvc.HeaderValueAbstractions": "",
"Microsoft.AspNet.Testing": "1.0.0-*",
"Xunit.KRunner": "1.0.0-*"
},
"commands": {
"test": "Xunit.KRunner"
},
"configurations": {
"net45": {
"dependencies": {
"System.Runtime": "4.0.20.0"
}
}
}
}