Http Abstractions for parsing Http headers needed for conneg.
This commit is contained in:
parent
d13f6474d8
commit
b8ab5c5063
26
Mvc.sln
26
Mvc.sln
|
|
@ -2,6 +2,7 @@
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.21901.1
|
VisualStudioVersion = 14.0.21901.1
|
||||||
|
VisualStudioVersion = 14.0.21806.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -54,6 +55,9 @@ EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorWebSite", "test\WebSites\RazorWebSite\RazorWebSite.kproj", "{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorWebSite", "test\WebSites\RazorWebSite\RazorWebSite.kproj", "{B07CAF59-11ED-40E3-A5DB-E1178F84FA78}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValueProvidersSite", "test\WebSites\ValueProvidersSite\ValueProvidersSite.kproj", "{14F79E79-AE79-48FA-95DE-D794EF4EABB3}"
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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.ActiveCfg = Release|Any CPU
|
||||||
{14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Mixed Platforms.Build.0 = 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
|
{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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -313,5 +337,7 @@ Global
|
||||||
{A853B2BA-4449-4908-A416-5A3C027FC22B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{A853B2BA-4449-4908-A416-5A3C027FC22B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{B07CAF59-11ED-40E3-A5DB-E1178F84FA78} = {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}
|
{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
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
|
||||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||||
"Microsoft.Framework.Runtime.Interfaces": "1.0.0-*",
|
"Microsoft.Framework.Runtime.Interfaces": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.PipelineCore": "1.0.0-*",
|
||||||
"RoutingWebSite": "",
|
"RoutingWebSite": "",
|
||||||
"RazorWebSite": "",
|
"RazorWebSite": "",
|
||||||
"ValueProvidersSite": "",
|
"ValueProvidersSite": "",
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue