diff --git a/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs b/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs index d3c45da4e..38d7fa6c8 100644 --- a/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs +++ b/src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs @@ -11,7 +11,7 @@ namespace Elsa.Http.ContentWriters; public class JsonContentFactory : IHttpContentFactory { /// - public IEnumerable SupportedContentTypes => new[] { MediaTypeNames.Application.Json, "text/json" }; + public IEnumerable SupportedContentTypes => [MediaTypeNames.Application.Json, "text/json"]; /// [RequiresUnreferencedCode("The JsonSerializer type is not trim-compatible.")] diff --git a/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs b/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs index c27d476e6..6849537b6 100644 --- a/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs +++ b/src/modules/Elsa.Http/ContentWriters/RawStringContent.cs @@ -1,48 +1,29 @@ -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. +/// HTTP content based on a string that preserves the provided media type as-is (no automatic charset parameter). +/// Uses a pre-encoded byte array to guarantee Content-Length == bytes written. /// -public class RawStringContent : HttpContent +public sealed class RawStringContent : ByteArrayContent { - 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) + : base(GetBytes(content, encoding)) { - _content = content; - _encoding = encoding; - - // Set the media type exactly as provided without appending charset information - Headers.ContentType = new MediaTypeHeaderValue(mediaType); + // Set media type exactly as provided, without charset parameter. + Headers.ContentType = new(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) + private static byte[] GetBytes(string content, Encoding encoding) { - await using var writer = new StreamWriter(stream, _encoding, leaveOpen: true); - await writer.WriteAsync(_content.AsMemory(), cancellationToken); - await writer.FlushAsync(cancellationToken); - } + ArgumentNullException.ThrowIfNull(content); + ArgumentNullException.ThrowIfNull(encoding); - /// - protected override bool TryComputeLength(out long length) - { - length = _encoding.GetByteCount(_content); - return true; + // Ensure we don't include a BOM/preamble. Most Encoding instances can expose a preamble. + // If encoding has a preamble, we must not include it, so use GetBytes directly (preamble is only returned by GetPreamble()). + // For safety, if the encoding instance is a BOM-producing variant (e.g., new UTF8Encoding(true)), + // GetBytes does not include the BOM; only GetPreamble() would. So this is safe. + return encoding.GetBytes(content); } } \ No newline at end of file