Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Merge branch 'rel/1.1.0-preview1' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
javiercn committed Oct 18, 2016
2 parents d8c6c4a + 9579806 commit d5b0ebd
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 13 deletions.
9 changes: 8 additions & 1 deletion src/Microsoft.AspNetCore.Mvc.Core/Formatters/MediaType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@ public static Encoding GetEncoding(StringSegment mediaType)
public static MediaTypeSegmentWithQuality CreateMediaTypeSegmentWithQuality(string mediaType, int start)
{
var parsedMediaType = new MediaType(mediaType, start, length: null);
if (parsedMediaType.Type.Equals(default(StringSegment)) ||
parsedMediaType.SubType.Equals(default(StringSegment)))
{
return default(MediaTypeSegmentWithQuality);
}

var parser = parsedMediaType._parameterParser;

double quality = 1.0d;
Expand All @@ -306,7 +312,7 @@ public static MediaTypeSegmentWithQuality CreateMediaTypeSegmentWithQuality(stri
}
}

// We check if the parsed media type has value at this stage when we have iterated
// We check if the parsed media type has a value at this stage when we have iterated
// over all the parameters and we know if the parsing was sucessful.
if (!parser.ParsingFailed)
{
Expand Down Expand Up @@ -395,6 +401,7 @@ public bool ParseNextParameter(out MediaTypeParameter result)
{
if (_mediaTypeBuffer == null)
{
ParsingFailed = true;
result = default(MediaTypeParameter);
return false;
}
Expand Down
35 changes: 25 additions & 10 deletions src/Microsoft.AspNetCore.Mvc.Core/Internal/AcceptHeaderParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ public static void ParseAcceptHeader(IList<string> acceptHeaders, IList<MediaTyp
{
throw new ArgumentNullException(nameof(parsedValues));
}

for (var i = 0; i < acceptHeaders.Count; i++)
{
var charIndex = 0;
Expand All @@ -46,13 +45,6 @@ public static void ParseAcceptHeader(IList<string> acceptHeaders, IList<MediaTyp
parsedValues.Add(output);
}
}
else
{
var invalidValuesError = Resources.FormatAcceptHeaderParser_ParseAcceptHeader_InvalidValues(
value.Substring(charIndex));

throw new FormatException(invalidValuesError);
}
}
}
}
Expand Down Expand Up @@ -80,11 +72,34 @@ private static bool TryParseValue(string value, ref int index, out MediaTypeSegm
return true;
}

MediaTypeSegmentWithQuality result;
var length = GetMediaTypeWithQualityLength(value, currentIndex, out result);
// We deliberately want to ignore media types that we are not capable of parsing.
// This is due to the fact that some browsers will send invalid media types like
// ; q=0.9 or */;q=0.2, etc.
// In this scenario, our recovery action consists of advancing the pointer to the
// next separator and moving on.
// In case we don't find the next separator, we simply advance the cursor to the
// end of the string to signal that we are done parsing.
var result = default(MediaTypeSegmentWithQuality);
var length = 0;
try
{
length = GetMediaTypeWithQualityLength(value, currentIndex, out result);
}
catch
{
length = 0;
}

if (length == 0)
{
// The parsing failed.
currentIndex = value.IndexOf(',', currentIndex);
if (currentIndex == -1)
{
index = value.Length;
return false;
}
index = currentIndex;
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) .NET Foundation. 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.Linq;
using Microsoft.Extensions.Primitives;
using Xunit;

Expand Down Expand Up @@ -48,14 +50,69 @@ public void ParseAcceptHeader_ParsesSimpleHeaderWithMultipleValues()
}
}

[Fact]
public void ParseAcceptHeader_ParsesSimpleHeaderWithMultipleValues_InvalidFormat()
{
// Arrange
var header = "application/json, application/xml,;q=0.8";
var expectedMediaTypes = new List<MediaTypeSegmentWithQuality>
{
new MediaTypeSegmentWithQuality(new StringSegment("application/json"),1.0),
new MediaTypeSegmentWithQuality(new StringSegment("application/xml"),1.0),
};

// Act
var mediaTypes = AcceptHeaderParser.ParseAcceptHeader(new List<string> { header });

// Assert
Assert.Equal(expectedMediaTypes, mediaTypes);
}

public static TheoryData<string[], string[]> ParseAcceptHeaderWithInvalidMediaTypesData =>
new TheoryData<string[], string[]>
{
{ new [] { ";q=0.9" }, new string[] { } },
{ new [] { "/" }, new string[] { } },
{ new [] { "*/" }, new string[] { } },
{ new [] { "/*" }, new string[] { } },
{ new [] { "/;q=0.9" }, new string[] { } },
{ new [] { "*/;q=0.9" }, new string[] { } },
{ new [] { "/*;q=0.9" }, new string[] { } },
{ new [] { "/;q=0.9,text/html" }, new string[] { "text/html" } },
{ new [] { "*/;q=0.9,text/html" }, new string[] { "text/html" } },
{ new [] { "/*;q=0.9,text/html" }, new string[] { "text/html" } },
{ new [] { "img/png,/;q=0.9,text/html" }, new string[] { "img/png", "text/html" } },
{ new [] { "img/png,*/;q=0.9,text/html" }, new string[] { "img/png", "text/html" } },
{ new [] { "img/png,/*;q=0.9,text/html" }, new string[] { "img/png", "text/html" } },
{ new [] { "img/png, /;q=0.9" }, new string[] { "img/png", } },
{ new [] { "img/png, */;q=0.9" }, new string[] { "img/png", } },
{ new [] { "img/png;q=1.0, /*;q=0.9" }, new string[] { "img/png;q=1.0", } },
};

[Theory]
[MemberData(nameof(ParseAcceptHeaderWithInvalidMediaTypesData))]
public void ParseAcceptHeader_GracefullyRecoversFromInvalidMediaTypeValues_AndReturnsValidMediaTypes(
string[] acceptHeader,
string[] expected)
{
// Arrange
var expectedMediaTypes = expected.Select(e => new MediaTypeSegmentWithQuality(new StringSegment(e), 1.0)).ToList();

// Act
var parsed = AcceptHeaderParser.ParseAcceptHeader(acceptHeader);

// Assert
Assert.Equal(expectedMediaTypes, parsed);
}

[Fact]
public void ParseAcceptHeader_ParsesMultipleHeaderValues()
{
// Arrange
var expected = new List<MediaTypeSegmentWithQuality>
{
new MediaTypeSegmentWithQuality(new StringSegment("application/json"),1.0),
new MediaTypeSegmentWithQuality(new StringSegment("application/xml;q=0.8"),0.8)
new MediaTypeSegmentWithQuality(new StringSegment("application/json"), 1.0),
new MediaTypeSegmentWithQuality(new StringSegment("application/xml;q=0.8"), 0.8)
};

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@ public async Task NoProducesAttribute_ActionReturningAnyObject_RunsUsingDefaultF
Assert.Equal(expectedContentType, response.Content.Headers.ContentType);
}

[Theory]
[InlineData("/;q=0.9")]
[InlineData("/;q=0.9, invalid;q=0.5;application/json;q=0.1")]
[InlineData("/invalid;q=0.9, application/json;q=0.1,invalid;q=0.5")]
[InlineData("text/html, application/json, image/jpeg, *; q=.2, */*; q=.2")]
public async Task ContentNegotiationWithPartiallyValidAcceptHeader_SkipsInvalidEntries(string acceptHeader)
{
// Arrange
var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8");
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/ContentNegotiation/UserInfo_ProducesWithTypeOnly");
request.Headers.TryAddWithoutValidation("Accept", acceptHeader);

// Act
var response = await Client.SendAsync(request);

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedContentType, response.Content.Headers.ContentType);
}

[Fact]
public async Task ProducesAttributeWithTypeOnly_RunsRegularContentNegotiation()
{
Expand Down

0 comments on commit d5b0ebd

Please sign in to comment.