diff --git a/src/StreamJsonRpc/NewLineDelimitedMessageHandler.cs b/src/StreamJsonRpc/NewLineDelimitedMessageHandler.cs
index 29ea48df..a7ceceb8 100644
--- a/src/StreamJsonRpc/NewLineDelimitedMessageHandler.cs
+++ b/src/StreamJsonRpc/NewLineDelimitedMessageHandler.cs
@@ -5,6 +5,7 @@
using System.Globalization;
using System.IO.Pipelines;
using System.Text;
+using Nerdbank.Streams;
using StreamJsonRpc.Protocol;
using StreamJsonRpc.Reflection;
@@ -19,6 +20,11 @@ namespace StreamJsonRpc;
///
public class NewLineDelimitedMessageHandler : PipeMessageHandler
{
+ ///
+ /// The that buffers an outgoing message.
+ ///
+ private readonly Sequence contentSequenceBuilder = new Sequence(ArrayPool.Shared);
+
///
/// Backing field for the property.
///
@@ -105,7 +111,22 @@ protected override void Write(JsonRpcMessage content, CancellationToken cancella
Assumes.NotNull(this.Writer);
cancellationToken.ThrowIfCancellationRequested();
- this.Formatter.Serialize(this.Writer, content);
+
+ // Some formatters (e.g. MessagePackFormatter) needs the encoded form in order to produce JSON for tracing.
+ // Other formatters (e.g. JsonMessageFormatter) would prefer to do its own tracing while it still has a JToken.
+ // We only help the formatters that need the byte-encoded form here. The rest can do it themselves.
+ if (this.Formatter is IJsonRpcFormatterTracingCallbacks tracer)
+ {
+ this.Formatter.Serialize(this.contentSequenceBuilder, content);
+ tracer.OnSerializationComplete(content, this.contentSequenceBuilder);
+ this.Writer.Write(this.contentSequenceBuilder);
+ this.contentSequenceBuilder.Reset();
+ }
+ else
+ {
+ this.Formatter.Serialize(this.Writer, content);
+ }
+
this.Writer.Write(this.newLineBytes.Span);
}
@@ -180,13 +201,6 @@ private static ReadOnlyMemory GetLineFeedSequence(Encoding encoding, NewLi
///
private void CommonConstructor()
{
- if (this.Formatter is IJsonRpcFormatterTracingCallbacks)
- {
- // Such a formatter requires that we make their own written bytes available back to them for logging purposes.
- // We haven't implemented support for such, and the JsonMessageFormatter doesn't need it, so no need to.
- throw new NotSupportedException("Formatters that implement " + nameof(IJsonRpcFormatterTracingCallbacks) + " are not supported. Try using " + nameof(JsonMessageFormatter) + ".");
- }
-
if (this.Formatter.Encoding.WebName != Encoding.UTF8.WebName)
{
throw new NotSupportedException("Only UTF-8 formatters are supported.");
diff --git a/test/StreamJsonRpc.Tests/NewLineDelimitedMessageHandlerTests.cs b/test/StreamJsonRpc.Tests/NewLineDelimitedMessageHandlerTests.cs
index 7ca5653d..782323bf 100644
--- a/test/StreamJsonRpc.Tests/NewLineDelimitedMessageHandlerTests.cs
+++ b/test/StreamJsonRpc.Tests/NewLineDelimitedMessageHandlerTests.cs
@@ -53,11 +53,11 @@ public void NewLine()
Assert.Equal(NewLineDelimitedMessageHandler.NewLineStyle.Lf, handler.NewLine);
}
- [Fact]
- public async Task Reading_MixedLineEndings()
+ [Theory, PairwiseData]
+ public async Task Reading_MixedLineEndings(bool useSystemTextJson)
{
var pipe = new Pipe();
- var handler = new NewLineDelimitedMessageHandler(null, pipe.Reader, new JsonMessageFormatter());
+ var handler = new NewLineDelimitedMessageHandler(null, pipe.Reader, useSystemTextJson ? new SystemTextJsonFormatter() : new JsonMessageFormatter());
// Send messages with mixed line endings to the handler..
var testFormatter = new JsonMessageFormatter();
@@ -105,11 +105,11 @@ public async Task Reading_IncompleteLine()
Assert.True(msg is JsonRpcRequest);
}
- [Fact]
- public async Task Writing_MixedLineEndings()
+ [Theory, PairwiseData]
+ public async Task Writing_MixedLineEndings(bool useSystemTextJson)
{
var pipe = new Pipe();
- var handler = new NewLineDelimitedMessageHandler(pipe.Writer, null, new JsonMessageFormatter());
+ var handler = new NewLineDelimitedMessageHandler(pipe.Writer, null, useSystemTextJson ? new SystemTextJsonFormatter() : new JsonMessageFormatter());
// Use the handler to write out a couple messages with mixed line endings.
await handler.WriteAsync(this.mockMessages[0], this.TimeoutToken); // CRLF
@@ -119,6 +119,7 @@ public async Task Writing_MixedLineEndings()
using var streamReader = new StreamReader(pipe.Reader.AsStream(), handler.Formatter.Encoding);
string allMessages = await streamReader.ReadToEndAsync();
+ this.Logger.WriteLine(allMessages);
// Use CR and LF counts to quickly figure whether our new line styles were honored.
Assert.Equal(3, allMessages.Split('\n').Length);