From b8ab5c5063871cb0fb0d24a6f0fdaae02e813078 Mon Sep 17 00:00:00 2001 From: harshgMSFT Date: Tue, 15 Jul 2014 17:51:24 -0700 Subject: [PATCH] Http Abstractions for parsing Http headers needed for conneg. --- Mvc.sln | 26 +++ .../FormattingUtilities.cs | 18 ++ .../MediaTypeHeaderValue.cs | 187 ++++++++++++++++++ .../MediaTypeHeaderValueRange.cs | 23 +++ .../MediaTypeWithQualityHeaderValue.cs | 43 ++++ ...t.AspNet.Mvc.HeaderValueAbstractions.kproj | 30 +++ .../StringWithQualityHeaderValue.cs | 47 +++++ .../project.json | 14 ++ .../project.json | 1 + .../MediaTypeHeaderValueParsingTests.cs | 135 +++++++++++++ ...et.Mvc.HeaderValueAbstractions.Tests.kproj | 28 +++ ...tringWithQualityHeaderValueParsingTests.cs | 29 +++ .../project.json | 18 ++ 13 files changed, 599 insertions(+) create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValue.cs create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValueRange.cs create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs create mode 100644 src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/project.json create mode 100644 test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs create mode 100644 test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Tests.kproj create mode 100644 test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs create mode 100644 test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/project.json diff --git a/Mvc.sln b/Mvc.sln index ef7698c239..dad54ecc62 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -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 diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs new file mode 100644 index 0000000000..f36bb61559 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs @@ -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 + { + /// + /// Quality factor to indicate a perfect match. + /// + public const double Match = 1.0; + + /// + /// Quality factor to indicate no match. + /// + public const double NoMatch = 0.0; + } +} diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValue.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValue.cs new file mode 100644 index 0000000000..ed3e5691a2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValue.cs @@ -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 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 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(StringComparer.OrdinalIgnoreCase), + }; + + return mediaTypeHeader; + } + + protected static Dictionary ParseParameters(string inputString) + { + var acceptParameters = inputString.Split(';'); + var parameterNameValue = new Dictionary(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; + } + + /// + /// Determines whether this instance is a subset of passed . + /// If the media type and media type parameters of this media type are all present + /// and match those of then it is a match even though + /// may have additional parameters. + /// + /// The first media type. + /// The second media type. + /// true if this is a subset of ; false otherwise. + 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 parameters1, + IDictionary 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; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValueRange.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValueRange.cs new file mode 100644 index 0000000000..11bbeb5712 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeHeaderValueRange.cs @@ -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 + { + /// + /// Not a media type range + /// + None = 0, + + /// + /// A subtype media range, e.g. "application/*". + /// + SubtypeMediaRange, + + /// + /// An all media range, e.g. "*/*". + /// + AllMediaRange, + } +} diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs new file mode 100644 index 0000000000..cef3eac13d --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs @@ -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, + }; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj new file mode 100644 index 0000000000..d9ecfd883f --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj @@ -0,0 +1,30 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 98335b23-e4b9-4cad-9749-0ded32a659a1 + Library + + + + + + + 2.0 + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs new file mode 100644 index 0000000000..327c60f205 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs @@ -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; + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/project.json b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/project.json new file mode 100644 index 0000000000..3e937c23ae --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/project.json @@ -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" + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index 717d9af643..8c4c09f25a 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -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": "", diff --git a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs new file mode 100644 index 0000000000..96779feb76 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs @@ -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 GetValidMediaTypeWithQualityHeaderValues + { + get + { + yield return new object[] + { + "*", + "*", + null, + MediaTypeHeaderValueRange.AllMediaRange, + new Dictionary(), + FormattingUtilities.Match, + "*/*" + }; + + yield return new object[] + { + "text", + "*", + "utf-8", + MediaTypeHeaderValueRange.SubtypeMediaRange, + new Dictionary() { { "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() { { "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 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 expectedParameters, + IDictionary actualParameters) + { + Assert.Equal(expectedParameters.Count, actualParameters.Count); + foreach (var key in expectedParameters.Keys) + { + Assert.Equal(expectedParameters[key], actualParameters[key]); + } + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Tests.kproj b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Tests.kproj new file mode 100644 index 0000000000..16c5a045af --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Tests.kproj @@ -0,0 +1,28 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + e69fd235-2042-43a4-9970-59cb29955b4e + Library + net45 + + + + + + + 2.0 + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs new file mode 100644 index 0000000000..6992c48e32 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs @@ -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); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/project.json b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/project.json new file mode 100644 index 0000000000..5b8e9aaba2 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/project.json @@ -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" + } + } + } +} \ No newline at end of file