in src/StreamJsonRpc/HeaderDelimitedMessageHandler.cs [202:285]
protected override void Write(JsonRpcMessage content, CancellationToken cancellationToken)
{
Assumes.NotNull(this.Writer);
unsafe int WriteHeaderText(string value, Span<byte> memory)
{
fixed (char* pValue = &MemoryMarshal.GetReference(value.AsSpan()))
{
fixed (byte* pMemory = &MemoryMarshal.GetReference(memory))
{
return HeaderEncoding.GetBytes(pValue, value.Length, pMemory, memory.Length);
}
}
}
cancellationToken.ThrowIfCancellationRequested();
Encoding contentEncoding = this.Encoding;
try
{
this.Formatter.Serialize(this.contentSequenceBuilder, content);
ReadOnlySequence<byte> contentSequence = this.contentSequenceBuilder.AsReadOnlySequence;
// 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)
{
tracer.OnSerializationComplete(content, this.contentSequenceBuilder);
}
Memory<byte> headerMemory = this.Writer.GetMemory(1024);
int bytesWritten = 0;
// Transmit the Content-Length header.
ContentLengthHeaderName.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += ContentLengthHeaderName.Length;
HeaderKeyValueDelimiter.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += HeaderKeyValueDelimiter.Length;
Assumes.True(Utf8Formatter.TryFormat(contentSequence.Length, headerMemory.Span.Slice(bytesWritten), out int formattedBytes));
bytesWritten += formattedBytes;
CrlfBytes.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += CrlfBytes.Length;
// Transmit the Content-Type header, but only when using a non-default encoding.
// We suppress it when it is the default both for smaller messages and to avoid
// having to load System.Net.Http on the receiving end in order to parse it.
if (DefaultContentEncoding.WebName != contentEncoding.WebName || this.SubType != DefaultSubType)
{
ContentTypeHeaderName.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += ContentTypeHeaderName.Length;
HeaderKeyValueDelimiter.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += HeaderKeyValueDelimiter.Length;
bytesWritten += WriteHeaderText("application/", headerMemory.Slice(bytesWritten).Span);
bytesWritten += WriteHeaderText(this.SubType, headerMemory.Slice(bytesWritten).Span);
bytesWritten += WriteHeaderText("; charset=", headerMemory.Slice(bytesWritten).Span);
bytesWritten += WriteHeaderText(contentEncoding.WebName, headerMemory.Slice(bytesWritten).Span);
CrlfBytes.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += CrlfBytes.Length;
}
// Terminate the headers.
CrlfBytes.CopyTo(headerMemory.Slice(bytesWritten));
bytesWritten += CrlfBytes.Length;
this.Writer.Advance(bytesWritten);
bytesWritten = 0;
// Transmit the content itself.
Memory<byte> contentMemory = this.Writer.GetMemory((int)contentSequence.Length);
contentSequence.CopyTo(contentMemory.Span);
this.Writer.Advance((int)contentSequence.Length);
if (JsonRpcEventSource.Instance.IsEnabled(System.Diagnostics.Tracing.EventLevel.Informational, System.Diagnostics.Tracing.EventKeywords.None))
{
JsonRpcEventSource.Instance.HandlerTransmitted(contentSequence.Length);
}
}
finally
{
this.contentSequenceBuilder.Reset();
}
}