Skip to content

Commit

Permalink
Avoid a few allocations in StringContent (dotnet#103008)
Browse files Browse the repository at this point in the history
  • Loading branch information
MihaZupan authored Jun 6, 2024
1 parent 428d73b commit b2601ed
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.IO;
using System.Net.Http.Headers;
using System.Text;
Expand Down Expand Up @@ -45,8 +46,31 @@ public StringContent(string content, Encoding? encoding)
/// <param name="encoding">The encoding to use for the content.</param>
/// <param name="mediaType">The media type to use for the content.</param>
public StringContent(string content, Encoding? encoding, string mediaType)
: this(content, encoding, new MediaTypeHeaderValue(mediaType ?? DefaultMediaType, (encoding ?? DefaultStringEncoding).WebName))
: base(GetContentByteArray(content, encoding))
{
Debug.Assert(DefaultStringEncoding.WebName == "utf-8");

encoding ??= DefaultStringEncoding;
mediaType ??= DefaultMediaType;

// Avoid allocating MediaTypeHeaderValue and related objects for common media types.
if (ReferenceEquals(encoding, DefaultStringEncoding))
{
string? knownValue = mediaType switch
{
"text/plain" => "text/plain; charset=utf-8",
"application/json" => "application/json; charset=utf-8",
_ => null
};

if (knownValue is not null)
{
Headers.TryAddWithoutValidation(KnownHeaders.ContentType.Descriptor, knownValue);
return;
}
}

Headers.ContentType = new MediaTypeHeaderValue(mediaType, encoding.WebName);
}

/// <summary>Creates a new instance of the <see cref="StringContent"/> class.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.IO;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -72,14 +73,57 @@ public async Task Ctor_DefineNoEncoding_DefaultEncodingUsed()
}

[Fact]
public void Ctor_PassNullStringForMediaType_DefaultMediaTypeUsed()
public void Ctor_SetsDefaultContentTypeHeader()
{
string sourceString = "\u00C4\u00E4\u00FC\u00DC";
Encoding defaultStringEncoding = Encoding.GetEncoding("utf-8");
var content = new StringContent(sourceString, defaultStringEncoding, ((string)null)!);
var content = new StringContent("Foo");
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

// If no media is passed-in, the default is used
Assert.Equal("text/plain", content.Headers.ContentType.MediaType);
content = new StringContent("Foo", (Encoding)null);
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("Foo", Encoding.UTF8);
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("Foo", Encoding.UTF8, "text/plain");
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("Foo", null, "text/plain");
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("Foo", Encoding.UTF8, (string)null);
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("Foo", null, (string)null);
Assert.Equal("text/plain; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("Foo", (MediaTypeHeaderValue)null);
Assert.Null(content.Headers.ContentType);

content = new StringContent("Foo", null, (MediaTypeHeaderValue)null);
Assert.Null(content.Headers.ContentType);
}

[Theory]
[InlineData("text/plain")]
[InlineData("application/json")]
[InlineData("application/xml")]
[InlineData("foo/bar")]
public void Ctor_SetsContentTypeHeader(string mediaType)
{
var content = new StringContent("foo", Encoding.UTF8, mediaType);
Assert.Equal($"{mediaType}; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("foo", null, mediaType);
Assert.Equal($"{mediaType}; charset=utf-8", content.Headers.ContentType.ToString());

content = new StringContent("foo", Encoding.ASCII, mediaType);
Assert.Equal($"{mediaType}; charset=us-ascii", content.Headers.ContentType.ToString());

content = new StringContent("foo", new MediaTypeHeaderValue(mediaType));
Assert.Equal(mediaType, content.Headers.ContentType.ToString());

content = new StringContent("foo", Encoding.UTF8, new MediaTypeHeaderValue(mediaType, "ascii"));
Assert.Equal($"{mediaType}; charset=ascii", content.Headers.ContentType.ToString());
}

[Fact]
Expand Down

0 comments on commit b2601ed

Please sign in to comment.