Skip to content

Commit

Permalink
Fix for Content-Disposition encoding.
Browse files Browse the repository at this point in the history
  • Loading branch information
twitchax committed Jun 6, 2022
1 parent 700cbcb commit d5a589b
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/Core/Extensions/Http.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private static HttpRequestMessage CreateProxiedHttpRequest(this HttpContext cont
if (request.HasFormContentType)
{
usesStreamContent = false;
requestMessage.Content = request.Form.ToHttpContent(request.ContentType);
requestMessage.Content = request.Form.ToHttpContent(request);
}
else
{
Expand Down
15 changes: 11 additions & 4 deletions src/Core/Helpers/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -72,7 +73,7 @@ internal static string TrimTrailingSlashes(this string s)
return s.Substring(0, s.Length - count);
}

internal static HttpContent ToHttpContent(this IFormCollection collection, string contentTypeHeader)
internal static HttpContent ToHttpContent(this IFormCollection collection, HttpRequest request)
{
// @PreferLinux:
// Form content types resource: https://stackoverflow.com/questions/4526273/what-does-enctype-multipart-form-data-mean/28380690
Expand All @@ -84,7 +85,7 @@ internal static HttpContent ToHttpContent(this IFormCollection collection, strin
// A single form element can have multiple values. When sending them they are handled as separate items with the same name, not a singe item with multiple values.
// For example, a=1&a=2.

var contentType = MediaTypeHeaderValue.Parse(contentTypeHeader);
var contentType = MediaTypeHeaderValue.Parse(request.ContentType);

if (contentType.MediaType.Equals("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) // specification: https://url.spec.whatwg.org/#concept-urlencoded
return new FormUrlEncodedContent(collection.SelectMany(formItemList => formItemList.Value.Select(value => new KeyValuePair<string, string>(formItemList.Key, value))));
Expand All @@ -106,9 +107,15 @@ internal static HttpContent ToHttpContent(this IFormCollection collection, strin
foreach (var file in collection.Files)
{
var content = new StreamContent(file.OpenReadStream());
foreach (var header in file.Headers)
foreach (var header in file.Headers.Where(h => !h.Key.Equals("Content-Disposition", StringComparison.OrdinalIgnoreCase)))
content.Headers.TryAddWithoutValidation(header.Key, (IEnumerable<string>)header.Value);
multipart.Add(content, file.Name, file.FileName);

// Force content-disposition header to use raw string to ensure UTF-8 is well encoded.
content.Headers.TryAddWithoutValidation("Content-Disposition",
new string(Encoding.UTF8.GetBytes($"form-data; name=\"{file.Name}\"; filename=\"{file.FileName}\"").
Select(b => (char)b).ToArray()));

multipart.Add(content);
}
return multipart;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Test/Http/HttpIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public async Task CanProxyControllerPostWithFormAndFilesRequest()
const string fileString = "This is a test file こんにちは with non-ascii content.";
var fileContent = new StreamContent(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(fileString)));
content.Add(fileContent, "testFile", fileName);

var response = await _client.PostAsync("api/multipart", content);
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Expand Down
6 changes: 5 additions & 1 deletion src/Test/Unit/Other.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

using System;
using Microsoft.AspNetCore.Http;
using Moq;
using Xunit;

namespace AspNetCore.Proxy.Tests
Expand All @@ -10,9 +12,11 @@ public class Other
public void CanFailOnBadFormContentType()
{
var contentType = "text/plain";
var request = Mock.Of<HttpRequest>();
request.ContentType = contentType;

var e = Assert.ThrowsAny<Exception>(() => {
var dummy = Helpers.ToHttpContent(null, contentType);
var dummy = Helpers.ToHttpContent(null, request);
});

Assert.Equal($"Unknown form content type `{contentType}`.", e.Message);
Expand Down

0 comments on commit d5a589b

Please sign in to comment.