diff --git a/Elsa.sln b/Elsa.sln
index a6d64300a..f5a389155 100644
--- a/Elsa.sln
+++ b/Elsa.sln
@@ -1,4 +1,4 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34003.232
MinimumVisualStudioVersion = 10.0.40219.1
@@ -262,6 +262,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Expressions.Python", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.SasTokens", "src\modules\Elsa.SasTokens\Elsa.SasTokens.csproj", "{A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Http.UnitTests", "test\unit\Elsa.Http.UnitTests\Elsa.Http.UnitTests.csproj", "{66ACA2B3-DB48-4F68-B26D-62555F3CE69B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -450,6 +452,10 @@ Global
{A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {66ACA2B3-DB48-4F68-B26D-62555F3CE69B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {66ACA2B3-DB48-4F68-B26D-62555F3CE69B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {66ACA2B3-DB48-4F68-B26D-62555F3CE69B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {66ACA2B3-DB48-4F68-B26D-62555F3CE69B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -527,6 +533,7 @@ Global
{6AF53651-99F0-1DE0-D37B-4FF6B0348DBB} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76}
{9D8FB664-88B4-10BE-58A2-D9A1644AD2E4} = {6EF07978-A6D2-40EB-891D-7D70C5F37E76}
{A7DE02E3-405B-B6BA-7E47-9E27D4BEFB24} = {5948B0A5-7873-4DBB-BA03-EB283D6EA91B}
+ {66ACA2B3-DB48-4F68-B26D-62555F3CE69B} = {18453B51-25EB-4317-A4B3-B10518252E92}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E}
diff --git a/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs b/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs
index 2523b553a..d3c45da4e 100644
--- a/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs
+++ b/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs
@@ -22,6 +22,6 @@ public class JsonContentFactory : IHttpContentFactory
if (string.IsNullOrWhiteSpace(contentType))
contentType = MediaTypeNames.Application.Json;
- return new StringContent(text, Encoding.UTF8, contentType);
+ return new RawStringContent(text, Encoding.UTF8, contentType);
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs b/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs
new file mode 100644
index 000000000..358131ddf
--- /dev/null
+++ b/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs
@@ -0,0 +1,48 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Text;
+
+namespace Elsa.Http.ContentWriters;
+
+///
+/// A implementation that allows setting the content type without automatically appending charset information.
+///
+public class RawStringContent : HttpContent
+{
+ private readonly string _content;
+ private readonly Encoding _encoding;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The content to send.
+ /// The encoding to use when sending the content.
+ /// The media type to use for the content.
+ public RawStringContent(string content, Encoding encoding, string mediaType)
+ {
+ _content = content;
+ _encoding = encoding;
+
+ // Set the media type exactly as provided without appending charset information
+ Headers.ContentType = new MediaTypeHeaderValue(mediaType);
+ }
+
+ ///
+ protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) =>
+ SerializeToStreamAsync(stream, context, CancellationToken.None);
+
+ ///
+ protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken)
+ {
+ using var writer = new StreamWriter(stream, _encoding, leaveOpen: true);
+ await writer.WriteAsync(_content.AsMemory(), cancellationToken);
+ await writer.FlushAsync();
+ }
+
+ ///
+ protected override bool TryComputeLength(out long length)
+ {
+ length = _encoding.GetByteCount(_content);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Http/ContentWriters/TextContentFactory.cs b/src/modules/Elsa.Http/ContentWriters/TextContentFactory.cs
index d1c14167e..7b6532877 100644
--- a/src/modules/Elsa.Http/ContentWriters/TextContentFactory.cs
+++ b/src/modules/Elsa.Http/ContentWriters/TextContentFactory.cs
@@ -24,6 +24,6 @@ public class TextContentFactory : IHttpContentFactory
if (string.IsNullOrWhiteSpace(contentType))
contentType = MediaTypeNames.Text.Plain;
- return new StringContent(text!, Encoding.UTF8, contentType);
+ return new RawStringContent(text!, Encoding.UTF8, contentType);
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Http/ContentWriters/XmlContentFactory.cs b/src/modules/Elsa.Http/ContentWriters/XmlContentFactory.cs
index d45ebd61a..1d65abc6d 100644
--- a/src/modules/Elsa.Http/ContentWriters/XmlContentFactory.cs
+++ b/src/modules/Elsa.Http/ContentWriters/XmlContentFactory.cs
@@ -21,7 +21,7 @@ public class XmlContentFactory : IHttpContentFactory
public HttpContent CreateHttpContent(object content, string contentType)
{
var text = content as string ?? Serialize(content);
- return new StringContent(text, Encoding.UTF8, contentType);
+ return new RawStringContent(text, Encoding.UTF8, contentType);
}
private string Serialize(object value)
diff --git a/test/unit/Elsa.Http.UnitTests/ContentWriters/ContentFactoryTests.cs b/test/unit/Elsa.Http.UnitTests/ContentWriters/ContentFactoryTests.cs
new file mode 100644
index 000000000..e8d923bf1
--- /dev/null
+++ b/test/unit/Elsa.Http.UnitTests/ContentWriters/ContentFactoryTests.cs
@@ -0,0 +1,67 @@
+using Elsa.Http.ContentWriters;
+using Xunit;
+
+namespace Elsa.Http.UnitTests.ContentWriters;
+
+///
+/// Tests for the implementations.
+///
+public class ContentFactoryTests
+{
+ ///
+ /// Tests that doesn't append charset to content type.
+ ///
+ [Fact]
+ public void JsonContentFactory_ShouldNotAppendCharset()
+ {
+ // Arrange
+ const string contentType = "application/json";
+ const string content = "{\"test\": \"value\"}";
+ var factory = new JsonContentFactory();
+
+ // Act
+ var httpContent = factory.CreateHttpContent(content, contentType);
+
+ // Assert
+ Assert.Equal(contentType, httpContent.Headers.ContentType?.MediaType);
+ Assert.Null(httpContent.Headers.ContentType?.CharSet);
+ }
+
+ ///
+ /// Tests that doesn't append charset to content type.
+ ///
+ [Fact]
+ public void XmlContentFactory_ShouldNotAppendCharset()
+ {
+ // Arrange
+ const string contentType = "text/xml";
+ const string content = "test";
+ var factory = new XmlContentFactory();
+
+ // Act
+ var httpContent = factory.CreateHttpContent(content, contentType);
+
+ // Assert
+ Assert.Equal(contentType, httpContent.Headers.ContentType?.MediaType);
+ Assert.Null(httpContent.Headers.ContentType?.CharSet);
+ }
+
+ ///
+ /// Tests that doesn't append charset to content type.
+ ///
+ [Fact]
+ public void TextContentFactory_ShouldNotAppendCharset()
+ {
+ // Arrange
+ const string contentType = "text/html";
+ const string content = "
test";
+ var factory = new TextContentFactory();
+
+ // Act
+ var httpContent = factory.CreateHttpContent(content, contentType);
+
+ // Assert
+ Assert.Equal(contentType, httpContent.Headers.ContentType?.MediaType);
+ Assert.Null(httpContent.Headers.ContentType?.CharSet);
+ }
+}
\ No newline at end of file
diff --git a/test/unit/Elsa.Http.UnitTests/ContentWriters/RawStringContentTests.cs b/test/unit/Elsa.Http.UnitTests/ContentWriters/RawStringContentTests.cs
new file mode 100644
index 000000000..308f22c37
--- /dev/null
+++ b/test/unit/Elsa.Http.UnitTests/ContentWriters/RawStringContentTests.cs
@@ -0,0 +1,55 @@
+using System.Net.Http.Headers;
+using System.Text;
+using Elsa.Http.ContentWriters;
+using Xunit;
+
+namespace Elsa.Http.UnitTests.ContentWriters;
+
+///
+/// Tests for the class.
+///
+public class RawStringContentTests
+{
+ ///
+ /// Tests that the content type is set exactly as provided without appending charset information.
+ ///
+ [Fact]
+ public void ContentType_ShouldNotAppendCharset()
+ {
+ // Arrange
+ const string contentType = "text/xml";
+ const string content = "test";
+
+ // Act
+ var rawContent = new RawStringContent(content, Encoding.UTF8, contentType);
+
+ // Assert
+ Assert.Equal(contentType, rawContent.Headers.ContentType?.MediaType);
+ Assert.Null(rawContent.Headers.ContentType?.CharSet);
+ }
+
+ ///
+ /// Tests that the content type with parameters is preserved exactly as provided.
+ ///
+ [Fact]
+ public void ContentType_WithParameters_ShouldPreserveParameters()
+ {
+ // Arrange
+ const string contentType = "application/json; custom-param=value";
+ var expectedMediaType = new MediaTypeHeaderValue(contentType);
+ const string content = "{\"test\": \"value\"}";
+
+ // Act
+ var rawContent = new RawStringContent(content, Encoding.UTF8, contentType);
+
+ // Assert
+ Assert.Equal(expectedMediaType.MediaType, rawContent.Headers.ContentType?.MediaType);
+ Assert.Equal(expectedMediaType.Parameters.Count(), rawContent.Headers.ContentType?.Parameters.Count());
+
+ var expectedParam = expectedMediaType.Parameters.First();
+ var actualParam = rawContent.Headers.ContentType?.Parameters.First();
+
+ Assert.Equal(expectedParam.Name, actualParam?.Name);
+ Assert.Equal(expectedParam.Value, actualParam?.Value);
+ }
+}
\ No newline at end of file
diff --git a/test/unit/Elsa.Http.UnitTests/Elsa.Http.UnitTests.csproj b/test/unit/Elsa.Http.UnitTests/Elsa.Http.UnitTests.csproj
new file mode 100644
index 000000000..457f65e7f
--- /dev/null
+++ b/test/unit/Elsa.Http.UnitTests/Elsa.Http.UnitTests.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+ latest
+ false
+ true
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file