From 3e36d70b8834863b000b8bd21aa4f641737f3ccc Mon Sep 17 00:00:00 2001 From: Pragna Gopa Date: Tue, 24 Jul 2018 10:45:00 -0700 Subject: [PATCH] Normalize charset in Content-Type Header (#3173) --- .../Handlers/WebScriptHostHandler.cs | 2 + .../HttpRequestMessageExtensions.cs | 13 ++++ .../HttpRequestMessageExtensions.cs | 65 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/WebJobs.Script.WebHost/Handlers/WebScriptHostHandler.cs b/src/WebJobs.Script.WebHost/Handlers/WebScriptHostHandler.cs index a19d51ae0f..73794e00df 100644 --- a/src/WebJobs.Script.WebHost/Handlers/WebScriptHostHandler.cs +++ b/src/WebJobs.Script.WebHost/Handlers/WebScriptHostHandler.cs @@ -39,6 +39,8 @@ protected override async Task SendAsync(HttpRequestMessage await StandbyManager.WarmUp(request, scriptHostManager); } + // Workaround for .net framework bug https://github.com/dotnet/corefx/issues/5014 + request.NormalizeContentTypeCharsetHeader(); return await base.SendAsync(request, cancellationToken); } } diff --git a/src/WebJobs.Script/Extensions/HttpRequestMessageExtensions.cs b/src/WebJobs.Script/Extensions/HttpRequestMessageExtensions.cs index bd083458dd..767eafa757 100644 --- a/src/WebJobs.Script/Extensions/HttpRequestMessageExtensions.cs +++ b/src/WebJobs.Script/Extensions/HttpRequestMessageExtensions.cs @@ -37,6 +37,19 @@ public static void SetProperty(this HttpRequestMessage request, string propertyN request.Properties[propertyName] = value; } + public static void NormalizeContentTypeCharsetHeader(this HttpRequestMessage request) + { + if (request.Content != null && request.Content.Headers.ContentLength > 0 && request.Content.Headers.ContentType != null) + { + var charSet = request.Content.Headers.ContentType.CharSet; + if (!string.IsNullOrEmpty(charSet)) + { + char[] trimQuotes = new[] { '\'', '\"' }; + request.Content.Headers.ContentType.CharSet = charSet.Trim(trimQuotes); + } + } + } + public static T GetPropertyOrDefault(this HttpRequestMessage request, string propertyName) { return request.GetRequestPropertyOrDefault(propertyName); diff --git a/test/WebJobs.Script.Tests/Extensions/HttpRequestMessageExtensions.cs b/test/WebJobs.Script.Tests/Extensions/HttpRequestMessageExtensions.cs index bdfa683abc..97cbdf1ec3 100644 --- a/test/WebJobs.Script.Tests/Extensions/HttpRequestMessageExtensions.cs +++ b/test/WebJobs.Script.Tests/Extensions/HttpRequestMessageExtensions.cs @@ -42,6 +42,61 @@ public void GetHeaderValueOrDefault_ReturnsExpectedResult() Assert.Equal("One", value); } + [Theory] + [InlineData("\"UTF-8\"")] + [InlineData("utf-8")] + [InlineData("'Utf-8'")] + [InlineData("'UTF-8'")] + public void NormalizeContentTypeCharsetHeader_ContentTypeHeader_ValidCharSet(string charSet) + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://functions/{test:alpha}/test?name=Amy"); + string input = "{ name: 'body1', nestedObject: { name: 'body2' } }"; + request.Content = new StringContent(input); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + request.Content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", charSet)); + request.NormalizeContentTypeCharsetHeader(); + + string expectedHeader = GetExpectedContenTypeHeader(charSet); + string actualHeader = request.Content.Headers.ContentType.ToString(); + Assert.Equal(expectedHeader, actualHeader); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void NormalizeContentTypeCharsetHeader_ContentTypeHeader_Set_CharSet_Empty(string charSet) + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://functions/{test:alpha}/test?name=Amy"); + string input = "{ name: 'body1', nestedObject: { name: 'body2' } }"; + request.Content = new StringContent(input); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + request.Content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", charSet)); + request.NormalizeContentTypeCharsetHeader(); + + string expectedHeader = GetExpectedContenTypeHeader(charSet); + string actualHeader = request.Content.Headers.ContentType.ToString(); + Assert.Equal(expectedHeader, actualHeader); + } + + [Fact] + public void NormalizeContentTypeCharsetHeader_ContentTypeHeader_Default() + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://functions/{test:alpha}/test?name=Amy"); + request.Content = new StringContent("test"); + request.NormalizeContentTypeCharsetHeader(); + string expectedHeader = "text/plain; charset=utf-8"; + string actualHeader = request.Content.Headers.ContentType.ToString(); + Assert.Equal(expectedHeader, actualHeader); + } + + [Fact] + public void NormalizeContentTypeCharsetHeader_NoHeaders() + { + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://functions/{test:alpha}/test?name=Amy"); + request.NormalizeContentTypeCharsetHeader(); + Assert.Null(request.Content); + } + [Fact] public void IsAntaresInternalRequest_ReturnsExpectedResult() { @@ -171,5 +226,15 @@ public void HasAuthorizationLevel_ReturnsExpectedValue() request.SetProperty(ScriptConstants.AzureFunctionsHttpRequestAuthorizationDisabledKey, true); Assert.True(request.HasAuthorizationLevel(AuthorizationLevel.Admin)); } + + private static string GetExpectedContenTypeHeader(string charSet) + { + if (string.IsNullOrEmpty(charSet)) + { + return "application/json; charset"; + } + char[] trimQuotes = new[] { '\'', '\"' }; + return "application/json; charset=" + charSet.Trim(trimQuotes); + } } }