diff --git a/src/StreamJsonRpc/JsonMessageFormatter.cs b/src/StreamJsonRpc/JsonMessageFormatter.cs
index 2d92f931a..1e883630b 100644
--- a/src/StreamJsonRpc/JsonMessageFormatter.cs
+++ b/src/StreamJsonRpc/JsonMessageFormatter.cs
@@ -72,16 +72,24 @@ public class JsonMessageFormatter : IJsonRpcAsyncMessageTextFormatter, IJsonRpcF
///
private readonly SequenceTextReader sequenceTextReader = new SequenceTextReader();
+ ///
+ /// Backing field for the property.
+ ///
+ private MultiplexingStream? multiplexingStream;
+
///
/// instance containing useful methods to help on the implementation of message formatters.
///
- private readonly MessageFormatterProgressTracker formatterProgressTracker = new MessageFormatterProgressTracker();
+ private MessageFormatterProgressTracker? formatterProgressTracker;
///
/// The helper for marshaling pipes as RPC method arguments.
///
- private readonly MessageFormatterDuplexPipeTracker duplexPipeTracker = new MessageFormatterDuplexPipeTracker();
+ private MessageFormatterDuplexPipeTracker? duplexPipeTracker;
+ ///
+ /// The helper for marshaling in RPC method arguments or return values.
+ ///
private MessageFormatterEnumerableTracker? enumerableTracker;
///
@@ -212,11 +220,11 @@ public Version ProtocolVersion
///
public MultiplexingStream? MultiplexingStream
{
- get => this.duplexPipeTracker.MultiplexingStream;
+ get => this.multiplexingStream;
set
{
Verify.Operation(this.rpc == null, Resources.FormatterConfigurationLockedAfterJsonRpcAssigned);
- this.duplexPipeTracker.MultiplexingStream = value;
+ this.multiplexingStream = value;
}
}
@@ -230,7 +238,9 @@ JsonRpc IJsonRpcInstanceContainer.Rpc
if (value != null)
{
+ this.formatterProgressTracker = new MessageFormatterProgressTracker(value, this);
this.enumerableTracker = new MessageFormatterEnumerableTracker(value, this);
+ this.duplexPipeTracker = new MessageFormatterDuplexPipeTracker(value, this) { MultiplexingStream = this.multiplexingStream };
}
}
}
@@ -244,6 +254,42 @@ JsonRpc IJsonRpcInstanceContainer.Rpc
///
bool IJsonRpcFormatterState.SerializingRequest => this.serializingRequest;
+ ///
+ /// Gets the instance containing useful methods to help on the implementation of message formatters.
+ ///
+ private MessageFormatterProgressTracker FormatterProgressTracker
+ {
+ get
+ {
+ Assumes.NotNull(this.formatterProgressTracker); // This should have been set in the Rpc property setter.
+ return this.formatterProgressTracker;
+ }
+ }
+
+ ///
+ /// Gets the helper for marshaling pipes as RPC method arguments.
+ ///
+ private MessageFormatterDuplexPipeTracker DuplexPipeTracker
+ {
+ get
+ {
+ Assumes.NotNull(this.duplexPipeTracker); // This should have been set in the Rpc property setter.
+ return this.duplexPipeTracker;
+ }
+ }
+
+ ///
+ /// Gets the helper for marshaling in RPC method arguments or return values.
+ ///
+ private MessageFormatterEnumerableTracker EnumerableTracker
+ {
+ get
+ {
+ Assumes.NotNull(this.enumerableTracker); // This should have been set in the Rpc property setter.
+ return this.enumerableTracker;
+ }
+ }
+
///
public JsonRpcMessage Deserialize(ReadOnlySequence contentBuffer) => this.Deserialize(contentBuffer, this.Encoding);
@@ -332,11 +378,6 @@ public JsonRpcMessage Deserialize(JToken json)
/// The JSON of the message.
public JToken Serialize(JsonRpcMessage message)
{
- if (message is IJsonRpcMessageWithId msgWithId && (message is JsonRpcResult || message is JsonRpcError))
- {
- this.duplexPipeTracker.OnResponseSent(msgWithId.RequestId, successful: msgWithId is JsonRpcResult);
- }
-
this.observedTransmittedRequestWithStringId |= message is JsonRpcRequest request && request.RequestId.String != null;
// Pre-tokenize the user data so we can use their custom converters for just their data and not for the base message.
@@ -365,7 +406,7 @@ public JToken Serialize(JsonRpcMessage message)
///
public void Dispose()
{
- this.duplexPipeTracker.Dispose();
+ this.duplexPipeTracker?.Dispose();
}
private static IReadOnlyDictionary PartiallyParseNamedArguments(JObject args)
@@ -465,8 +506,6 @@ private void TokenizeUserData(JsonRpcMessage jsonRpcMessage)
if (jsonRpcMessage is Protocol.JsonRpcRequest request)
{
this.serializingRequest = true;
- this.formatterProgressTracker.RequestIdBeingSerialized = request.RequestId;
- this.duplexPipeTracker.RequestIdBeingSerialized = request.RequestId;
if (request.ArgumentsList != null)
{
@@ -493,8 +532,6 @@ private void TokenizeUserData(JsonRpcMessage jsonRpcMessage)
{
this.serializingMessageWithId = default;
this.serializingRequest = false;
- this.formatterProgressTracker.RequestIdBeingSerialized = default;
- this.duplexPipeTracker.RequestIdBeingSerialized = default;
}
}
@@ -535,7 +572,7 @@ private JsonRpcRequest ReadRequest(JToken json)
// If method is $/progress, get the progress instance from the dictionary and call Report
string method = json.Value("method");
- if (string.Equals(method, MessageFormatterProgressTracker.ProgressRequestSpecialMethod, StringComparison.Ordinal))
+ if (this.formatterProgressTracker != null && string.Equals(method, MessageFormatterProgressTracker.ProgressRequestSpecialMethod, StringComparison.Ordinal))
{
try
{
@@ -578,9 +615,6 @@ private JsonRpcResult ReadResult(JToken json)
JToken result = json["result"];
- this.formatterProgressTracker.OnResponseReceived(id);
- this.duplexPipeTracker.OnResponseReceived(id, successful: true);
-
return new JsonRpcResult(this.JsonSerializer)
{
RequestId = id,
@@ -595,9 +629,6 @@ private JsonRpcError ReadError(JToken json)
RequestId id = this.NormalizeId(json["id"].ToObject());
JToken error = json["error"];
- this.formatterProgressTracker.OnResponseReceived(id);
- this.duplexPipeTracker.OnResponseReceived(id, successful: false);
-
return new JsonRpcError
{
RequestId = id,
@@ -679,7 +710,6 @@ public override bool TryGetArgumentByNameOrIndex(string? name, int position, Typ
// Deserialization of messages should never occur concurrently for a single instance of a formatter.
Assumes.True(this.formatter.deserializingMessageWithId.IsEmpty);
this.formatter.deserializingMessageWithId = this.RequestId;
- this.formatter.duplexPipeTracker.RequestIdBeingDeserialized = this.RequestId;
try
{
value = token?.ToObject(typeHint, this.formatter.JsonSerializer);
@@ -687,7 +717,6 @@ public override bool TryGetArgumentByNameOrIndex(string? name, int position, Typ
finally
{
this.formatter.deserializingMessageWithId = default;
- this.formatter.duplexPipeTracker.RequestIdBeingDeserialized = default;
}
return true;
@@ -801,7 +830,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object? exis
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- long progressId = this.formatter.formatterProgressTracker.GetTokenForProgress(value);
+ long progressId = this.formatter.FormatterProgressTracker.GetTokenForProgress(value);
writer.WriteValue(progressId);
}
}
@@ -832,7 +861,7 @@ public override bool CanConvert(Type objectType)
Assumes.NotNull(this.formatter.rpc);
JToken token = JToken.Load(reader);
- return this.formatter.formatterProgressTracker.CreateProgress(this.formatter.rpc, token, objectType);
+ return this.formatter.FormatterProgressTracker.CreateProgress(this.formatter.rpc, token, objectType);
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
@@ -853,7 +882,7 @@ internal AsyncEnumerableConsumerConverter(JsonMessageFormatter jsonMessageFormat
this.formatter = jsonMessageFormatter;
}
- public override bool CanConvert(Type objectType) => this.formatter.enumerableTracker != null && MessageFormatterEnumerableTracker.CanDeserialize(objectType);
+ public override bool CanConvert(Type objectType) => MessageFormatterEnumerableTracker.CanDeserialize(objectType);
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
@@ -862,9 +891,8 @@ internal AsyncEnumerableConsumerConverter(JsonMessageFormatter jsonMessageFormat
return null;
}
- Assumes.NotNull(this.formatter.enumerableTracker);
JToken token = JToken.Load(reader);
- return this.formatter.enumerableTracker.CreateEnumerableProxy(objectType, token);
+ return this.formatter.EnumerableTracker.CreateEnumerableProxy(objectType, token);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
@@ -885,7 +913,7 @@ internal AsyncEnumerableGeneratorConverter(JsonMessageFormatter jsonMessageForma
this.formatter = jsonMessageFormatter;
}
- public override bool CanConvert(Type objectType) => this.formatter.enumerableTracker != null && MessageFormatterEnumerableTracker.CanSerialize(objectType);
+ public override bool CanConvert(Type objectType) => MessageFormatterEnumerableTracker.CanSerialize(objectType);
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
@@ -894,9 +922,7 @@ internal AsyncEnumerableGeneratorConverter(JsonMessageFormatter jsonMessageForma
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- Assumes.NotNull(this.formatter.enumerableTracker);
-
- long token = this.formatter.enumerableTracker.GetToken(value);
+ long token = this.formatter.EnumerableTracker.GetToken(value);
writer.WriteValue(token);
}
}
@@ -913,12 +939,12 @@ public DuplexPipeConverter(JsonMessageFormatter jsonMessageFormatter)
public override IDuplexPipe? ReadJson(JsonReader reader, Type objectType, IDuplexPipe? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
int? tokenId = JToken.Load(reader).Value();
- return this.jsonMessageFormatter.duplexPipeTracker.GetPipe(tokenId);
+ return this.jsonMessageFormatter.duplexPipeTracker!.GetPipe(tokenId);
}
public override void WriteJson(JsonWriter writer, IDuplexPipe? value, JsonSerializer serializer)
{
- var token = this.jsonMessageFormatter.duplexPipeTracker.GetToken(value);
+ var token = this.jsonMessageFormatter.duplexPipeTracker!.GetToken(value);
writer.WriteValue(token);
}
}
@@ -935,12 +961,12 @@ public PipeReaderConverter(JsonMessageFormatter jsonMessageFormatter)
public override PipeReader? ReadJson(JsonReader reader, Type objectType, PipeReader? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
int? tokenId = JToken.Load(reader).Value();
- return this.jsonMessageFormatter.duplexPipeTracker.GetPipeReader(tokenId);
+ return this.jsonMessageFormatter.DuplexPipeTracker.GetPipeReader(tokenId);
}
public override void WriteJson(JsonWriter writer, PipeReader? value, JsonSerializer serializer)
{
- var token = this.jsonMessageFormatter.duplexPipeTracker.GetToken(value);
+ var token = this.jsonMessageFormatter.DuplexPipeTracker.GetToken(value);
writer.WriteValue(token);
}
}
@@ -957,12 +983,12 @@ public PipeWriterConverter(JsonMessageFormatter jsonMessageFormatter)
public override PipeWriter? ReadJson(JsonReader reader, Type objectType, PipeWriter? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
int? tokenId = JToken.Load(reader).Value();
- return this.jsonMessageFormatter.duplexPipeTracker.GetPipeWriter(tokenId);
+ return this.jsonMessageFormatter.DuplexPipeTracker.GetPipeWriter(tokenId);
}
public override void WriteJson(JsonWriter writer, PipeWriter? value, JsonSerializer serializer)
{
- var token = this.jsonMessageFormatter.duplexPipeTracker.GetToken(value);
+ var token = this.jsonMessageFormatter.DuplexPipeTracker.GetToken(value);
writer.WriteValue(token);
}
}
@@ -979,12 +1005,12 @@ public StreamConverter(JsonMessageFormatter jsonMessageFormatter)
public override Stream? ReadJson(JsonReader reader, Type objectType, Stream? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
int? tokenId = JToken.Load(reader).Value();
- return this.jsonMessageFormatter.duplexPipeTracker.GetPipe(tokenId)?.AsStream();
+ return this.jsonMessageFormatter.DuplexPipeTracker.GetPipe(tokenId)?.AsStream();
}
public override void WriteJson(JsonWriter writer, Stream? value, JsonSerializer serializer)
{
- var token = this.jsonMessageFormatter.duplexPipeTracker.GetToken(value?.UsePipe());
+ var token = this.jsonMessageFormatter.DuplexPipeTracker.GetToken(value?.UsePipe());
writer.WriteValue(token);
}
}
diff --git a/src/StreamJsonRpc/JsonRpc.cs b/src/StreamJsonRpc/JsonRpc.cs
index 4e6f5b52c..32d167684 100644
--- a/src/StreamJsonRpc/JsonRpc.cs
+++ b/src/StreamJsonRpc/JsonRpc.cs
@@ -132,9 +132,20 @@ public class JsonRpc : IDisposableObservable, IJsonRpcFormatterCallbacks
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private SynchronizationContext? synchronizationContext;
- private EventHandler? requestTransmissionAborted;
- private EventHandler? resultReceived;
- private EventHandler? errorReceived;
+ ///
+ /// Backing field for the event.
+ ///
+ private EventHandler? requestTransmissionAborted;
+
+ ///
+ /// Backing field for the event.
+ ///
+ private EventHandler? responseReceived;
+
+ ///
+ /// Backing field for the event.
+ ///
+ private EventHandler? responseSent;
///
/// Initializes a new instance of the class that uses
@@ -239,22 +250,25 @@ public event EventHandler? Disconnected
}
}
- event EventHandler IJsonRpcFormatterCallbacks.RequestTransmissionAborted
+ ///
+ event EventHandler IJsonRpcFormatterCallbacks.RequestTransmissionAborted
{
add => this.requestTransmissionAborted += value;
remove => this.requestTransmissionAborted -= value;
}
- event EventHandler IJsonRpcFormatterCallbacks.ResultReceived
+ ///
+ event EventHandler IJsonRpcFormatterCallbacks.ResponseReceived
{
- add => this.resultReceived += value;
- remove => this.resultReceived -= value;
+ add => this.responseReceived += value;
+ remove => this.responseReceived -= value;
}
- event EventHandler IJsonRpcFormatterCallbacks.ErrorReceived
+ ///
+ event EventHandler IJsonRpcFormatterCallbacks.ResponseSent
{
- add => this.errorReceived += value;
- remove => this.errorReceived -= value;
+ add => this.responseSent += value;
+ remove => this.responseSent -= value;
}
private event EventHandler? DisconnectedPrivate;
@@ -1302,21 +1316,21 @@ protected virtual void OnRequestTransmissionAborted(JsonRpcRequest request)
{
if (!request.RequestId.IsEmpty)
{
- this.requestTransmissionAborted?.Invoke(this, new JsonRpcFormatterCallbackEventArgs(request));
+ this.requestTransmissionAborted?.Invoke(this, new JsonRpcMessageEventArgs(request));
}
}
///
- /// Raises the event.
+ /// Raises the event.
///
- /// The result that was received.
- protected virtual void OnResultReceived(JsonRpcResult result) => this.resultReceived?.Invoke(this, new JsonRpcFormatterCallbackEventArgs(result));
+ /// The result or error that was received.
+ protected virtual void OnResponseReceived(JsonRpcMessage response) => this.responseReceived?.Invoke(this, new JsonRpcResponseEventArgs((IJsonRpcMessageWithId)response));
///
- /// Raises the event.
+ /// Raises the event.
///
- /// The error that was received.
- protected virtual void OnErrorReceived(JsonRpcError error) => this.errorReceived?.Invoke(this, new JsonRpcFormatterCallbackEventArgs(error));
+ /// The result or error that was sent.
+ protected virtual void OnResponseSent(JsonRpcMessage response) => this.responseSent?.Invoke(this, new JsonRpcResponseEventArgs((IJsonRpcMessageWithId)response));
///
/// Creates a dictionary which maps a request method name to its clr method name via value.
@@ -2181,6 +2195,7 @@ private async Task HandleRpcAsync(JsonRpcMessage rpc)
try
{
await this.TransmitAsync(result, this.DisconnectedToken).ConfigureAwait(false);
+ this.OnResponseSent(result);
}
catch (OperationCanceledException) when (this.DisconnectedToken.IsCancellationRequested)
{
@@ -2202,16 +2217,9 @@ private async Task HandleRpcAsync(JsonRpcMessage rpc)
}
else if (rpc is IJsonRpcMessageWithId resultOrError)
{
+ this.OnResponseReceived(rpc);
JsonRpcResult? result = resultOrError as JsonRpcResult;
JsonRpcError? error = resultOrError as JsonRpcError;
- if (result != null)
- {
- this.OnResultReceived(result);
- }
- else if (error != null)
- {
- this.OnErrorReceived(error);
- }
OutstandingCallData? data = null;
lock (this.dispatcherMapLock)
diff --git a/src/StreamJsonRpc/MessagePackFormatter.cs b/src/StreamJsonRpc/MessagePackFormatter.cs
index 3559a5969..865f7fdec 100644
--- a/src/StreamJsonRpc/MessagePackFormatter.cs
+++ b/src/StreamJsonRpc/MessagePackFormatter.cs
@@ -43,16 +43,6 @@ public class MessagePackFormatter : IJsonRpcMessageFormatter, IJsonRpcInstanceCo
private const string ErrorPropertyName = "error";
- ///
- /// instance containing useful methods to help on the implementation of message formatters.
- ///
- private readonly MessageFormatterProgressTracker formatterProgressTracker = new MessageFormatterProgressTracker();
-
- ///
- /// The helper for marshaling pipes as RPC method arguments.
- ///
- private readonly MessageFormatterDuplexPipeTracker duplexPipeTracker = new MessageFormatterDuplexPipeTracker();
-
///
/// The options to use for serializing top-level RPC messages.
///
@@ -64,6 +54,24 @@ public class MessagePackFormatter : IJsonRpcMessageFormatter, IJsonRpcInstanceCo
private readonly PipeFormatterResolver pipeFormatterResolver;
+ ///
+ /// Backing field for the property.
+ ///
+ private MultiplexingStream? multiplexingStream;
+
+ ///
+ /// The we use to support method arguments.
+ ///
+ private MessageFormatterProgressTracker? formatterProgressTracker;
+
+ ///
+ /// The helper for marshaling pipes as RPC method arguments.
+ ///
+ private MessageFormatterDuplexPipeTracker? duplexPipeTracker;
+
+ ///
+ /// The tracker we use to support transmission of types.
+ ///
private MessageFormatterEnumerableTracker? enumerableTracker;
///
@@ -154,6 +162,8 @@ JsonRpc IJsonRpcInstanceContainer.Rpc
if (value != null)
{
+ this.formatterProgressTracker = new MessageFormatterProgressTracker(value, this);
+ this.duplexPipeTracker = new MessageFormatterDuplexPipeTracker(value, this) { MultiplexingStream = this.multiplexingStream };
this.enumerableTracker = new MessageFormatterEnumerableTracker(value, this);
}
}
@@ -164,11 +174,11 @@ JsonRpc IJsonRpcInstanceContainer.Rpc
///
public MultiplexingStream? MultiplexingStream
{
- get => this.duplexPipeTracker.MultiplexingStream;
+ get => this.multiplexingStream;
set
{
Verify.Operation(this.rpc == null, Resources.FormatterConfigurationLockedAfterJsonRpcAssigned);
- this.duplexPipeTracker.MultiplexingStream = value;
+ this.multiplexingStream = value;
}
}
@@ -181,6 +191,42 @@ public MultiplexingStream? MultiplexingStream
///
bool IJsonRpcFormatterState.SerializingRequest => this.serializingRequest;
+ ///
+ /// Gets the instance containing useful methods to help on the implementation of message formatters.
+ ///
+ private MessageFormatterProgressTracker FormatterProgressTracker
+ {
+ get
+ {
+ Assumes.NotNull(this.formatterProgressTracker); // This should have been set in the Rpc property setter.
+ return this.formatterProgressTracker;
+ }
+ }
+
+ ///
+ /// Gets the helper for marshaling pipes as RPC method arguments.
+ ///
+ private MessageFormatterDuplexPipeTracker DuplexPipeTracker
+ {
+ get
+ {
+ Assumes.NotNull(this.duplexPipeTracker); // This should have been set in the Rpc property setter.
+ return this.duplexPipeTracker;
+ }
+ }
+
+ ///
+ /// Gets the helper for marshaling in RPC method arguments or return values.
+ ///
+ private MessageFormatterEnumerableTracker EnumerableTracker
+ {
+ get
+ {
+ Assumes.NotNull(this.enumerableTracker); // This should have been set in the Rpc property setter.
+ return this.enumerableTracker;
+ }
+ }
+
///
/// Sets the to use for serialization of user data.
///
@@ -198,11 +244,6 @@ public void SetMessagePackSerializerOptions(MessagePackSerializerOptions options
///
public void Serialize(IBufferWriter contentBuffer, JsonRpcMessage message)
{
- if (message is IJsonRpcMessageWithId msgWithId && (message is Protocol.JsonRpcResult || message is Protocol.JsonRpcError))
- {
- this.duplexPipeTracker.OnResponseSent(msgWithId.RequestId, successful: msgWithId is Protocol.JsonRpcResult);
- }
-
if (message is Protocol.JsonRpcRequest request && request.Arguments != null && request.ArgumentsList == null && !(request.Arguments is IReadOnlyDictionary))
{
// This request contains named arguments, but not using a standard dictionary. Convert it to a dictionary so that
@@ -221,7 +262,7 @@ public void Serialize(IBufferWriter contentBuffer, JsonRpcMessage message)
///
public void Dispose()
{
- this.duplexPipeTracker.Dispose();
+ this.duplexPipeTracker?.Dispose();
}
///
@@ -513,7 +554,7 @@ public void Serialize(ref MessagePackWriter writer, TClass value, MessagePackSer
}
else
{
- long progressId = this.formatter.formatterProgressTracker.GetTokenForProgress(value);
+ long progressId = this.formatter.FormatterProgressTracker.GetTokenForProgress(value);
writer.Write(progressId);
}
}
@@ -541,7 +582,7 @@ public TClass Deserialize(ref MessagePackReader reader, MessagePackSerializerOpt
Assumes.NotNull(this.formatter.rpc);
RawMessagePack token = RawMessagePack.ReadRaw(ref reader, copy: true);
- return (TClass)this.formatter.formatterProgressTracker.CreateProgress(this.formatter.rpc, token, typeof(TClass));
+ return (TClass)this.formatter.FormatterProgressTracker.CreateProgress(this.formatter.rpc, token, typeof(TClass));
}
public void Serialize(ref MessagePackWriter writer, TClass value, MessagePackSerializerOptions options)
@@ -552,7 +593,7 @@ public void Serialize(ref MessagePackWriter writer, TClass value, MessagePackSer
}
else
{
- long progressId = this.formatter.formatterProgressTracker.GetTokenForProgress(value);
+ long progressId = this.formatter.FormatterProgressTracker.GetTokenForProgress(value);
writer.Write(progressId);
}
}
@@ -724,12 +765,12 @@ public DuplexPipeFormatter(MessagePackFormatter formatter)
return null;
}
- return (T)this.formatter.duplexPipeTracker.GetPipe(reader.ReadInt32());
+ return (T)this.formatter.DuplexPipeTracker.GetPipe(reader.ReadInt32());
}
public void Serialize(ref MessagePackWriter writer, T? value, MessagePackSerializerOptions options)
{
- if (this.formatter.duplexPipeTracker.GetToken(value) is { } token)
+ if (this.formatter.DuplexPipeTracker.GetToken(value) is { } token)
{
writer.Write(token);
}
@@ -757,12 +798,12 @@ public PipeReaderFormatter(MessagePackFormatter formatter)
return null;
}
- return (T)this.formatter.duplexPipeTracker.GetPipeReader(reader.ReadInt32());
+ return (T)this.formatter.DuplexPipeTracker.GetPipeReader(reader.ReadInt32());
}
public void Serialize(ref MessagePackWriter writer, T? value, MessagePackSerializerOptions options)
{
- if (this.formatter.duplexPipeTracker.GetToken(value) is { } token)
+ if (this.formatter.DuplexPipeTracker.GetToken(value) is { } token)
{
writer.Write(token);
}
@@ -790,12 +831,12 @@ public PipeWriterFormatter(MessagePackFormatter formatter)
return null;
}
- return (T)this.formatter.duplexPipeTracker.GetPipeWriter(reader.ReadInt32());
+ return (T)this.formatter.DuplexPipeTracker.GetPipeWriter(reader.ReadInt32());
}
public void Serialize(ref MessagePackWriter writer, T? value, MessagePackSerializerOptions options)
{
- if (this.formatter.duplexPipeTracker.GetToken(value) is { } token)
+ if (this.formatter.DuplexPipeTracker.GetToken(value) is { } token)
{
writer.Write(token);
}
@@ -823,12 +864,12 @@ public StreamFormatter(MessagePackFormatter formatter)
return null;
}
- return (T)this.formatter.duplexPipeTracker.GetPipe(reader.ReadInt32()).AsStream();
+ return (T)this.formatter.DuplexPipeTracker.GetPipe(reader.ReadInt32()).AsStream();
}
public void Serialize(ref MessagePackWriter writer, T? value, MessagePackSerializerOptions options)
{
- if (this.formatter.duplexPipeTracker.GetToken(value?.UsePipe()) is { } token)
+ if (this.formatter.DuplexPipeTracker.GetToken(value?.UsePipe()) is { } token)
{
writer.Write(token);
}
@@ -987,7 +1028,7 @@ public Protocol.JsonRpcRequest Deserialize(ref MessagePackReader reader, Message
if (result.TryGetArgumentByNameOrIndex("token", 0, typeof(long), out object? tokenObject) && tokenObject is long progressId)
{
MessageFormatterProgressTracker.ProgressParamInformation? progressInfo = null;
- if (this.formatter.formatterProgressTracker.TryGetProgressObject(progressId, out progressInfo))
+ if (this.formatter.FormatterProgressTracker.TryGetProgressObject(progressId, out progressInfo))
{
if (result.TryGetArgumentByNameOrIndex("value", 1, progressInfo.ValueType, out object? value))
{
@@ -1007,49 +1048,38 @@ public Protocol.JsonRpcRequest Deserialize(ref MessagePackReader reader, Message
public void Serialize(ref MessagePackWriter writer, Protocol.JsonRpcRequest value, MessagePackSerializerOptions options)
{
- try
- {
- this.formatter.formatterProgressTracker.RequestIdBeingSerialized = value.RequestId;
- this.formatter.duplexPipeTracker.RequestIdBeingSerialized = value.RequestId;
+ writer.WriteMapHeader(4);
- writer.WriteMapHeader(4);
-
- writer.Write(VersionPropertyName);
- writer.Write(value.Version);
+ writer.Write(VersionPropertyName);
+ writer.Write(value.Version);
- writer.Write(IdPropertyName);
- options.Resolver.GetFormatterWithVerify().Serialize(ref writer, value.RequestId, options);
+ writer.Write(IdPropertyName);
+ options.Resolver.GetFormatterWithVerify().Serialize(ref writer, value.RequestId, options);
- writer.Write(MethodPropertyName);
- writer.Write(value.Method);
+ writer.Write(MethodPropertyName);
+ writer.Write(value.Method);
- writer.Write(ParamsPropertyName);
- if (value.ArgumentsList != null)
- {
- writer.WriteArrayHeader(value.ArgumentsList.Count);
- foreach (var arg in value.ArgumentsList)
- {
- this.formatter.dynamicObjectTypeFormatterForUserSuppliedResolver.Serialize(ref writer, arg, this.formatter.userDataSerializationOptions);
- }
- }
- else if (value.NamedArguments != null)
+ writer.Write(ParamsPropertyName);
+ if (value.ArgumentsList != null)
+ {
+ writer.WriteArrayHeader(value.ArgumentsList.Count);
+ foreach (var arg in value.ArgumentsList)
{
- writer.WriteMapHeader(value.NamedArguments.Count);
- foreach (KeyValuePair entry in value.NamedArguments)
- {
- writer.Write(entry.Key);
- this.formatter.dynamicObjectTypeFormatterForUserSuppliedResolver.Serialize(ref writer, entry.Value, this.formatter.userDataSerializationOptions);
- }
+ this.formatter.dynamicObjectTypeFormatterForUserSuppliedResolver.Serialize(ref writer, arg, this.formatter.userDataSerializationOptions);
}
- else
+ }
+ else if (value.NamedArguments != null)
+ {
+ writer.WriteMapHeader(value.NamedArguments.Count);
+ foreach (KeyValuePair entry in value.NamedArguments)
{
- writer.WriteNil();
+ writer.Write(entry.Key);
+ this.formatter.dynamicObjectTypeFormatterForUserSuppliedResolver.Serialize(ref writer, entry.Value, this.formatter.userDataSerializationOptions);
}
}
- finally
+ else
{
- this.formatter.formatterProgressTracker.RequestIdBeingSerialized = default;
- this.formatter.duplexPipeTracker.RequestIdBeingSerialized = default;
+ writer.WriteNil();
}
}
}
@@ -1087,9 +1117,6 @@ public Protocol.JsonRpcResult Deserialize(ref MessagePackReader reader, MessageP
}
}
- this.formatter.formatterProgressTracker.OnResponseReceived(result.RequestId);
- this.formatter.duplexPipeTracker.OnResponseReceived(result.RequestId, successful: true);
-
return result;
}
@@ -1141,9 +1168,6 @@ public Protocol.JsonRpcError Deserialize(ref MessagePackReader reader, MessagePa
}
}
- this.formatter.formatterProgressTracker.OnResponseReceived(error.RequestId);
- this.formatter.duplexPipeTracker.OnResponseReceived(error.RequestId, successful: false);
-
return error;
}
@@ -1302,7 +1326,6 @@ public override bool TryGetArgumentByNameOrIndex(string? name, int position, Typ
// Deserialization of messages should never occur concurrently for a single instance of a formatter.
Assumes.True(this.formatter.deserializingMessageWithId.IsEmpty);
this.formatter.deserializingMessageWithId = this.RequestId;
- this.formatter.duplexPipeTracker.RequestIdBeingDeserialized = this.RequestId;
value = MessagePackSerializer.Deserialize(typeHint ?? typeof(object), ref reader, this.formatter.userDataSerializationOptions);
return true;
@@ -1321,7 +1344,6 @@ public override bool TryGetArgumentByNameOrIndex(string? name, int position, Typ
finally
{
this.formatter.deserializingMessageWithId = default;
- this.formatter.duplexPipeTracker.RequestIdBeingDeserialized = default;
}
}
}
diff --git a/src/StreamJsonRpc/Reflection/IJsonRpcFormatterCallbacks.cs b/src/StreamJsonRpc/Reflection/IJsonRpcFormatterCallbacks.cs
index 26a7be8b2..702283082 100644
--- a/src/StreamJsonRpc/Reflection/IJsonRpcFormatterCallbacks.cs
+++ b/src/StreamJsonRpc/Reflection/IJsonRpcFormatterCallbacks.cs
@@ -4,8 +4,6 @@
namespace StreamJsonRpc.Reflection
{
using System;
- using Microsoft;
- using StreamJsonRpc.Protocol;
///
/// Implemented by to expose callbacks allowing an to perform resource cleanup.
@@ -18,48 +16,16 @@ public interface IJsonRpcFormatterCallbacks
///
/// This usually occurs because of an exception during serialization or transmission, possibly due to cancellation.
///
- event EventHandler RequestTransmissionAborted;
+ event EventHandler RequestTransmissionAborted;
///
- /// Occurs when receives a successful response to a previously sent request.
+ /// Occurs when receives a response to a previously sent request.
///
- event EventHandler ResultReceived;
+ event EventHandler ResponseReceived;
///
- /// Occurs when receives an error response to a previously sent request.
+ /// Occurs when transmits a response message.
///
- event EventHandler ErrorReceived;
- }
-
- ///
- /// The data passed to event handlers.
- ///
- public class JsonRpcFormatterCallbackEventArgs : EventArgs
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The ID from the request or response that the event is regarding.
- public JsonRpcFormatterCallbackEventArgs(RequestId requestId)
- {
- Requires.Argument(!requestId.IsEmpty, nameof(requestId), "Non-default ID required.");
- this.RequestId = requestId;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The message the event is regarding.
- internal JsonRpcFormatterCallbackEventArgs(IJsonRpcMessageWithId message)
- {
- Requires.NotNull(message, nameof(message));
- Requires.Argument(!message.RequestId.IsEmpty, nameof(message), "Non-default ID required.");
- this.RequestId = message.RequestId;
- }
-
- ///
- /// Gets the id on the request, result or error.
- ///
- public RequestId RequestId { get; private set; }
+ event EventHandler ResponseSent;
}
}
diff --git a/src/StreamJsonRpc/Reflection/JsonRpcMessageEventArgs.cs b/src/StreamJsonRpc/Reflection/JsonRpcMessageEventArgs.cs
new file mode 100644
index 000000000..a815bf777
--- /dev/null
+++ b/src/StreamJsonRpc/Reflection/JsonRpcMessageEventArgs.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace StreamJsonRpc.Reflection
+{
+ using System;
+ using Microsoft;
+ using StreamJsonRpc.Protocol;
+
+ ///
+ /// Carries the from request or response messages.
+ ///
+ public class JsonRpcMessageEventArgs : EventArgs
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ID from the request or response that the event is regarding.
+ public JsonRpcMessageEventArgs(RequestId requestId)
+ {
+ Requires.Argument(!requestId.IsEmpty, nameof(requestId), "Non-default ID required.");
+ this.RequestId = requestId;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message the event is regarding.
+ internal JsonRpcMessageEventArgs(IJsonRpcMessageWithId message)
+ {
+ Requires.NotNull(message, nameof(message));
+ Requires.Argument(!message.RequestId.IsEmpty, nameof(message), "Non-default ID required.");
+ this.RequestId = message.RequestId;
+ }
+
+ ///
+ /// Gets the id on the request, result or error.
+ ///
+ public RequestId RequestId { get; private set; }
+ }
+}
diff --git a/src/StreamJsonRpc/Reflection/JsonRpcResponseEventArgs.cs b/src/StreamJsonRpc/Reflection/JsonRpcResponseEventArgs.cs
new file mode 100644
index 000000000..60ce5689d
--- /dev/null
+++ b/src/StreamJsonRpc/Reflection/JsonRpcResponseEventArgs.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace StreamJsonRpc.Reflection
+{
+ using System;
+ using Microsoft;
+ using StreamJsonRpc.Protocol;
+
+ ///
+ /// Carries the and success status of response messages.
+ ///
+ public class JsonRpcResponseEventArgs : JsonRpcMessageEventArgs
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ID from the request or response that the event is regarding.
+ /// A flag indicating whether the response is a result (as opposed to an error).
+ public JsonRpcResponseEventArgs(RequestId requestId, bool isSuccessfulResponse)
+ : base(requestId)
+ {
+ this.IsSuccessfulResponse = isSuccessfulResponse;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The message the event is regarding.
+ internal JsonRpcResponseEventArgs(IJsonRpcMessageWithId message)
+ : this(message.RequestId, message is JsonRpcResult)
+ {
+ }
+
+ ///
+ /// Gets a value indicating whether the response is a result (as opposed to an error).
+ ///
+ public bool IsSuccessfulResponse { get; }
+ }
+}
diff --git a/src/StreamJsonRpc/Reflection/MessageFormatterDuplexPipeTracker.cs b/src/StreamJsonRpc/Reflection/MessageFormatterDuplexPipeTracker.cs
index cf9f3cb00..93f37c00a 100644
--- a/src/StreamJsonRpc/Reflection/MessageFormatterDuplexPipeTracker.cs
+++ b/src/StreamJsonRpc/Reflection/MessageFormatterDuplexPipeTracker.cs
@@ -29,6 +29,11 @@ namespace StreamJsonRpc.Reflection
///
public class MessageFormatterDuplexPipeTracker : IDisposableObservable
{
+ ///
+ /// The formatter that owns this tracker.
+ ///
+ private readonly IJsonRpcFormatterState formatterState;
+
///
/// A map of outbound request IDs to channels that they included.
///
@@ -57,19 +62,21 @@ public class MessageFormatterDuplexPipeTracker : IDisposableObservable
///
/// Initializes a new instance of the class.
///
- public MessageFormatterDuplexPipeTracker()
+ /// The instance that may be used to send or receive RPC messages related to .
+ /// The formatter that owns this tracker.
+ public MessageFormatterDuplexPipeTracker(JsonRpc jsonRpc, IJsonRpcFormatterState formatterState)
{
- }
+ Requires.NotNull(jsonRpc, nameof(jsonRpc));
+ Requires.NotNull(formatterState, nameof(formatterState));
- ///
- /// Gets or sets the id of the request currently being serialized for use as a key in .
- ///
- public RequestId RequestIdBeingSerialized { get; set; }
+ this.formatterState = formatterState;
- ///
- /// Gets or sets the ID of the request currently being deserialized for use as a key in .
- ///
- public RequestId RequestIdBeingDeserialized { get; set; }
+ // We don't offer a way to remove these handlers because this object should has a lifetime closely tied to the JsonRpc object anyway.
+ IJsonRpcFormatterCallbacks callbacks = jsonRpc;
+ callbacks.RequestTransmissionAborted += (s, e) => this.CleanUpOutboundResources(e.RequestId, successful: false);
+ callbacks.ResponseReceived += (s, e) => this.CleanUpOutboundResources(e.RequestId, successful: e.IsSuccessfulResponse);
+ callbacks.ResponseSent += (s, e) => this.CleanUpInboundResources(e.RequestId, successful: e.IsSuccessfulResponse);
+ }
///
/// Gets or sets the multiplexing stream used to create and accept channels.
@@ -82,16 +89,22 @@ public MessageFormatterDuplexPipeTracker()
///
bool IDisposableObservable.IsDisposed => this.isDisposed;
+ ///
+ /// Gets the id of the request currently being serialized for use as a key in .
+ ///
+ private RequestId RequestIdBeingSerialized => this.formatterState.SerializingRequest ? this.formatterState.SerializingMessageWithId : default;
+
+ ///
+ /// Gets the ID of the request currently being deserialized for use as a key in .
+ ///
+ private RequestId RequestIdBeingDeserialized => this.formatterState.DeserializingMessageWithId;
+
///
/// Creates a token to represent an as it is transmitted from the client to an RPC server as a method argument.
///
/// The client pipe that is to be shared with the RPC server. May be null.
/// The token to use as the RPC method argument; or null if was null.
- ///
- /// This method should only be called while serializing requests that include an ID (i.e. requests for which we expect a response).
- /// When the response is received, a call should always be made to .
- ///
- /// Thrown if no was provided to the constructor.
+ /// Thrown if no was provided to the constructor or when serializing a message without an ID property.
[return: NotNullIfNotNull("duplexPipe")]
public int? GetToken(IDuplexPipe? duplexPipe)
{
@@ -128,11 +141,7 @@ public MessageFormatterDuplexPipeTracker()
///
/// The client pipe that is to be shared with the RPC server. May be null.
/// The token to use as the RPC method argument; or null if was null.
- ///
- /// This method should only be called while serializing requests that include an ID (i.e. requests for which we expect a response).
- /// When the response is received, a call should always be made to .
- ///
- /// Thrown if no was provided to the constructor.
+ /// Thrown if no was provided to the constructor or when serializing a message without an ID property.
[return: NotNullIfNotNull("reader")]
public int? GetToken(PipeReader? reader) => this.GetToken(reader != null ? new DuplexPipe(reader) : null);
@@ -141,11 +150,7 @@ public MessageFormatterDuplexPipeTracker()
///
/// The client pipe that is to be shared with the RPC server. May be null.
/// The token to use as the RPC method argument; or null if was null.
- ///
- /// This method should only be called while serializing requests that include an ID (i.e. requests for which we expect a response).
- /// When the response is received, a call should always be made to .
- ///
- /// Thrown if no was provided to the constructor.
+ /// Thrown if no was provided to the constructor or when serializing a message without an ID property.
[return: NotNullIfNotNull("writer")]
public int? GetToken(PipeWriter? writer) => this.GetToken(writer != null ? new DuplexPipe(writer) : null);
@@ -232,15 +237,34 @@ public MessageFormatterDuplexPipeTracker()
return null;
}
- ///
- /// Notifies this tracker when a response to any request is sent
- /// so that appropriate channel and state cleanup can take place.
- ///
- /// The ID of the request for which a response was sent.
- /// A value indicating whether the response represents a successful result (i.e. a instead of an ).
- public void OnResponseSent(RequestId requestId, bool successful)
+ ///
+ public void Dispose()
{
- Verify.NotDisposed(this);
+ this.isDisposed = true;
+
+ // Release memory and shutdown channels that outlived the RPC channel.
+ this.outboundRequestChannelMap = this.outboundRequestChannelMap.Clear();
+ this.inboundRequestChannelMap = this.inboundRequestChannelMap.Clear();
+
+ ImmutableDictionary openInboundChannels = Interlocked.Exchange(ref this.openInboundChannels, this.openInboundChannels.Clear());
+ foreach (KeyValuePair entry in openInboundChannels)
+ {
+ entry.Value.Dispose();
+ }
+
+ ImmutableDictionary openOutboundChannels = Interlocked.Exchange(ref this.openOutboundChannels, this.openOutboundChannels.Clear());
+ foreach (KeyValuePair entry in openOutboundChannels)
+ {
+ entry.Value.Dispose();
+ }
+ }
+
+ private void CleanUpInboundResources(RequestId requestId, bool successful)
+ {
+ if (this.isDisposed)
+ {
+ return;
+ }
if (ImmutableInterlocked.TryRemove(ref this.inboundRequestChannelMap, requestId, out ImmutableList channels))
{
@@ -256,15 +280,12 @@ public void OnResponseSent(RequestId requestId, bool successful)
}
}
- ///
- /// Notifies this tracker when a response to any request is received
- /// so that appropriate channel and state cleanup can take place.
- ///
- /// The ID of the request for which a response was received.
- /// A value indicating whether the response represents a successful result (i.e. a instead of an ).
- public void OnResponseReceived(RequestId requestId, bool successful)
+ private void CleanUpOutboundResources(RequestId requestId, bool successful)
{
- Verify.NotDisposed(this);
+ if (this.isDisposed)
+ {
+ return;
+ }
if (ImmutableInterlocked.TryRemove(ref this.outboundRequestChannelMap, requestId, out ImmutableList channels))
{
@@ -280,28 +301,6 @@ public void OnResponseReceived(RequestId requestId, bool successful)
}
}
- ///
- public void Dispose()
- {
- this.isDisposed = true;
-
- // Release memory and shutdown channels that outlived the RPC channel.
- this.outboundRequestChannelMap = this.outboundRequestChannelMap.Clear();
- this.inboundRequestChannelMap = this.inboundRequestChannelMap.Clear();
-
- ImmutableDictionary openInboundChannels = Interlocked.Exchange(ref this.openInboundChannels, this.openInboundChannels.Clear());
- foreach (KeyValuePair entry in openInboundChannels)
- {
- entry.Value.Dispose();
- }
-
- ImmutableDictionary openOutboundChannels = Interlocked.Exchange(ref this.openOutboundChannels, this.openOutboundChannels.Clear());
- foreach (KeyValuePair entry in openOutboundChannels)
- {
- entry.Value.Dispose();
- }
- }
-
///
/// Throws if is null.
///
diff --git a/src/StreamJsonRpc/Reflection/MessageFormatterEnumerableTracker.cs b/src/StreamJsonRpc/Reflection/MessageFormatterEnumerableTracker.cs
index 3cbcae417..9db91a682 100644
--- a/src/StreamJsonRpc/Reflection/MessageFormatterEnumerableTracker.cs
+++ b/src/StreamJsonRpc/Reflection/MessageFormatterEnumerableTracker.cs
@@ -51,18 +51,20 @@ public class MessageFormatterEnumerableTracker
/// The formatter that owns this tracker.
public MessageFormatterEnumerableTracker(JsonRpc jsonRpc, IJsonRpcFormatterState formatterState)
{
- this.jsonRpc = jsonRpc ?? throw new ArgumentNullException(nameof(jsonRpc));
- this.formatterState = formatterState ?? throw new ArgumentNullException(nameof(formatterState));
+ Requires.NotNull(jsonRpc, nameof(jsonRpc));
+ Requires.NotNull(formatterState, nameof(formatterState));
- this.jsonRpc.AddLocalRpcMethod(NextMethodName, new Func>(this.OnNextAsync));
- this.jsonRpc.AddLocalRpcMethod(DisposeMethodName, new Func(this.OnDisposeAsync));
+ this.jsonRpc = jsonRpc;
+ this.formatterState = formatterState;
+
+ jsonRpc.AddLocalRpcMethod(NextMethodName, new Func>(this.OnNextAsync));
+ jsonRpc.AddLocalRpcMethod(DisposeMethodName, new Func(this.OnDisposeAsync));
this.formatterState = formatterState;
// We don't offer a way to remove these handlers because this object should has a lifetime closely tied to the JsonRpc object anyway.
- IJsonRpcFormatterCallbacks callbacks = this.jsonRpc;
- callbacks.RequestTransmissionAborted += this.JsonRpc_RequestTransmissionAborted;
- callbacks.ResultReceived += this.JsonRpc_ResultReceived;
- callbacks.ErrorReceived += this.JsonRpc_ErrorReceived;
+ IJsonRpcFormatterCallbacks callbacks = jsonRpc;
+ callbacks.RequestTransmissionAborted += (s, e) => this.CleanUpResources(e.RequestId);
+ callbacks.ResponseReceived += (s, e) => this.CleanUpResources(e.RequestId);
}
private interface IGeneratingEnumeratorTracker : System.IAsyncDisposable
@@ -206,28 +208,7 @@ private ValueTask OnDisposeAsync(long token)
return generator.DisposeAsync();
}
- ///
- /// Handles the event.
- ///
- /// The instance that raised the event.
- /// The event args.
- private void JsonRpc_ResultReceived(object sender, JsonRpcFormatterCallbackEventArgs e) => this.CleanUpResources(e.RequestId, error: false);
-
- ///
- /// Handles the event.
- ///
- /// The instance that raised the event.
- /// The event args.
- private void JsonRpc_ErrorReceived(object sender, JsonRpcFormatterCallbackEventArgs e) => this.CleanUpResources(e.RequestId, error: true);
-
- ///
- /// Handles the event.
- ///
- /// The instance that raised the event.
- /// The event args.
- private void JsonRpc_RequestTransmissionAborted(object sender, JsonRpcFormatterCallbackEventArgs e) => this.CleanUpResources(e.RequestId, error: true);
-
- private void CleanUpResources(RequestId requestId, bool error)
+ private void CleanUpResources(RequestId requestId)
{
lock (this.syncObject)
{
diff --git a/src/StreamJsonRpc/Reflection/MessageFormatterProgressTracker.cs b/src/StreamJsonRpc/Reflection/MessageFormatterProgressTracker.cs
index 239737aea..dea7768b2 100644
--- a/src/StreamJsonRpc/Reflection/MessageFormatterProgressTracker.cs
+++ b/src/StreamJsonRpc/Reflection/MessageFormatterProgressTracker.cs
@@ -36,15 +36,37 @@ public class MessageFormatterProgressTracker
///
private readonly object progressLock = new object();
+ ///
+ /// State from the formatter that owns this tracker.
+ ///
+ private readonly IJsonRpcFormatterState formatterState;
+
///
/// Gets or sets the the next id value to assign as token for the progress objects.
///
private long nextProgressId;
///
- /// Gets or Sets the id of the request currently being serialized so the converter can use it to create the request-progress map.
+ /// Initializes a new instance of the class.
+ ///
+ /// The object that ultimately owns this tracker.
+ /// The formatter that owns this tracker.
+ public MessageFormatterProgressTracker(JsonRpc jsonRpc, IJsonRpcFormatterState formatterState)
+ {
+ Requires.NotNull(jsonRpc, nameof(jsonRpc));
+ Requires.NotNull(formatterState, nameof(formatterState));
+
+ this.formatterState = formatterState;
+
+ IJsonRpcFormatterCallbacks callbacks = jsonRpc;
+ callbacks.RequestTransmissionAborted += (s, e) => this.CleanUpResources(e.RequestId);
+ callbacks.ResponseReceived += (s, e) => this.CleanUpResources(e.RequestId);
+ }
+
+ ///
+ /// Gets the id of the request currently being serialized so the converter can use it to create the request-progress map.
///
- public RequestId RequestIdBeingSerialized { get; set; }
+ private RequestId RequestIdBeingSerialized => this.formatterState.SerializingRequest ? this.formatterState.SerializingMessageWithId : default;
///
/// Converts given to its type.
@@ -104,25 +126,6 @@ public long GetTokenForProgress(object value)
}
}
- ///
- /// Call this method when a response is received to clear the objects associated with the request and avoid a memory leak.
- ///
- /// The id of the request whose associated objects need to be cleared.
- public void OnResponseReceived(RequestId requestId)
- {
- lock (this.progressLock)
- {
- if (this.requestProgressMap.TryGetValue(requestId, out ImmutableList? progressInfos))
- {
- this.requestProgressMap.Remove(requestId);
- foreach (ProgressParamInformation progressInfo in progressInfos)
- {
- this.progressMap.Remove(progressInfo.Token);
- }
- }
- }
- }
-
///
/// Gets the object associated with the given progress id.
///
@@ -168,6 +171,21 @@ public object CreateProgress(JsonRpc rpc, object token, Type valueType)
return Activator.CreateInstance(progressType, new object[] { rpc, token })!;
}
+ private void CleanUpResources(RequestId requestId)
+ {
+ lock (this.progressLock)
+ {
+ if (this.requestProgressMap.TryGetValue(requestId, out ImmutableList? progressInfos))
+ {
+ this.requestProgressMap.Remove(requestId);
+ foreach (ProgressParamInformation progressInfo in progressInfos)
+ {
+ this.progressMap.Remove(progressInfo.Token);
+ }
+ }
+ }
+ }
+
///
/// Class used to keep relevant information of an object that implements .
///
diff --git a/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Shipped.txt b/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Shipped.txt
index 505477aad..06105b606 100644
--- a/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Shipped.txt
+++ b/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Shipped.txt
@@ -319,20 +319,15 @@ StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetPipeWriter(int? to
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetToken(System.IO.Pipelines.IDuplexPipe duplexPipe) -> int?
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetToken(System.IO.Pipelines.PipeReader reader) -> int?
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetToken(System.IO.Pipelines.PipeWriter writer) -> int?
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MessageFormatterDuplexPipeTracker() -> void
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MultiplexingStream.get -> Nerdbank.Streams.MultiplexingStream
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MultiplexingStream.set -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingDeserialized.set -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingSerialized.set -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.CreateProgress(StreamJsonRpc.JsonRpc rpc, object token, System.Type valueType) -> object
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.CreateProgress(StreamJsonRpc.JsonRpc rpc, object token) -> System.IProgress
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.GetTokenForProgress(object value) -> long
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.MessageFormatterProgressTracker() -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation.InvokeReport(object typedValue) -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation.ValueType.get -> System.Type
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.RequestIdBeingSerialized.set -> void
const StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressRequestSpecialMethod = "$/progress" -> string
static StreamJsonRpc.JsonRpc.Attach(StreamJsonRpc.IJsonRpcMessageHandler handler) -> T
static StreamJsonRpc.JsonRpc.Attach(StreamJsonRpc.IJsonRpcMessageHandler handler, StreamJsonRpc.JsonRpcProxyOptions options) -> T
diff --git a/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt b/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt
index 955b7bab8..0a8871934 100644
--- a/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt
+++ b/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt
@@ -30,31 +30,30 @@ StreamJsonRpc.Protocol.JsonRpcRequest.RequestId.set -> void
StreamJsonRpc.Protocol.JsonRpcResult.RequestId.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Protocol.JsonRpcResult.RequestId.set -> void
StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks
-StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ErrorReceived -> System.EventHandler
-StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.RequestTransmissionAborted -> System.EventHandler
-StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ResultReceived -> System.EventHandler
+StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.RequestTransmissionAborted -> System.EventHandler
+StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ResponseReceived -> System.EventHandler
+StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ResponseSent -> System.EventHandler
StreamJsonRpc.Reflection.IJsonRpcFormatterState
StreamJsonRpc.Reflection.IJsonRpcFormatterState.DeserializingMessageWithId.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Reflection.IJsonRpcFormatterState.SerializingMessageWithId.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Reflection.IJsonRpcFormatterState.SerializingRequest.get -> bool
StreamJsonRpc.Reflection.IJsonRpcMessageBufferManager
StreamJsonRpc.Reflection.IJsonRpcMessageBufferManager.DeserializationComplete(StreamJsonRpc.Protocol.JsonRpcMessage message) -> void
-StreamJsonRpc.Reflection.JsonRpcFormatterCallbackEventArgs
-StreamJsonRpc.Reflection.JsonRpcFormatterCallbackEventArgs.JsonRpcFormatterCallbackEventArgs(StreamJsonRpc.RequestId requestId) -> void
-StreamJsonRpc.Reflection.JsonRpcFormatterCallbackEventArgs.RequestId.get -> StreamJsonRpc.RequestId
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.OnResponseReceived(StreamJsonRpc.RequestId requestId, bool successful) -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.OnResponseSent(StreamJsonRpc.RequestId requestId, bool successful) -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingDeserialized.get -> StreamJsonRpc.RequestId
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingSerialized.get -> StreamJsonRpc.RequestId
+StreamJsonRpc.Reflection.JsonRpcMessageEventArgs
+StreamJsonRpc.Reflection.JsonRpcMessageEventArgs.JsonRpcMessageEventArgs(StreamJsonRpc.RequestId requestId) -> void
+StreamJsonRpc.Reflection.JsonRpcMessageEventArgs.RequestId.get -> StreamJsonRpc.RequestId
+StreamJsonRpc.Reflection.JsonRpcResponseEventArgs
+StreamJsonRpc.Reflection.JsonRpcResponseEventArgs.IsSuccessfulResponse.get -> bool
+StreamJsonRpc.Reflection.JsonRpcResponseEventArgs.JsonRpcResponseEventArgs(StreamJsonRpc.RequestId requestId, bool isSuccessfulResponse) -> void
+StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MessageFormatterDuplexPipeTracker(StreamJsonRpc.JsonRpc jsonRpc, StreamJsonRpc.Reflection.IJsonRpcFormatterState formatterState) -> void
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.CreateEnumerableProxy(System.Type enumeratedType, object handle) -> object
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.CreateEnumerableProxy(object handle) -> System.Collections.Generic.IAsyncEnumerable
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.GetToken(object enumerable) -> long
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.GetToken(System.Collections.Generic.IAsyncEnumerable enumerable) -> long
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.MessageFormatterEnumerableTracker(StreamJsonRpc.JsonRpc jsonRpc, StreamJsonRpc.Reflection.IJsonRpcFormatterState formatterState) -> void
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.OnResponseReceived(StreamJsonRpc.RequestId requestId) -> void
+StreamJsonRpc.Reflection.MessageFormatterProgressTracker.MessageFormatterProgressTracker(StreamJsonRpc.JsonRpc jsonRpc, StreamJsonRpc.Reflection.IJsonRpcFormatterState formatterState) -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation.Token.get -> long
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.RequestIdBeingSerialized.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.TryGetProgressObject(long progressId, out StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation valueType) -> bool
StreamJsonRpc.RemoteInvocationException.DeserializedErrorData.get -> object
StreamJsonRpc.RemoteInvocationException.RemoteInvocationException(string message, int errorCode, object errorData, object deserializedErrorData) -> void
@@ -84,9 +83,9 @@ static StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.CanSerialize(S
static StreamJsonRpc.RequestId.NotSpecified.get -> StreamJsonRpc.RequestId
virtual StreamJsonRpc.JsonRpc.CreateNewRequestId() -> StreamJsonRpc.RequestId
virtual StreamJsonRpc.JsonRpc.GetErrorDetailsDataType(StreamJsonRpc.Protocol.JsonRpcError error) -> System.Type
-virtual StreamJsonRpc.JsonRpc.OnErrorReceived(StreamJsonRpc.Protocol.JsonRpcError error) -> void
virtual StreamJsonRpc.JsonRpc.OnRequestTransmissionAborted(StreamJsonRpc.Protocol.JsonRpcRequest request) -> void
-virtual StreamJsonRpc.JsonRpc.OnResultReceived(StreamJsonRpc.Protocol.JsonRpcResult result) -> void
+virtual StreamJsonRpc.JsonRpc.OnResponseReceived(StreamJsonRpc.Protocol.JsonRpcMessage response) -> void
+virtual StreamJsonRpc.JsonRpc.OnResponseSent(StreamJsonRpc.Protocol.JsonRpcMessage response) -> void
virtual StreamJsonRpc.MessageHandlerBase.DisposeAsync() -> System.Threading.Tasks.Task
virtual StreamJsonRpc.MessageHandlerBase.DisposeReader() -> void
virtual StreamJsonRpc.MessageHandlerBase.DisposeWriter() -> void
diff --git a/src/StreamJsonRpc/netstandard2.0/PublicAPI.Shipped.txt b/src/StreamJsonRpc/netstandard2.0/PublicAPI.Shipped.txt
index 505477aad..06105b606 100644
--- a/src/StreamJsonRpc/netstandard2.0/PublicAPI.Shipped.txt
+++ b/src/StreamJsonRpc/netstandard2.0/PublicAPI.Shipped.txt
@@ -319,20 +319,15 @@ StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetPipeWriter(int? to
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetToken(System.IO.Pipelines.IDuplexPipe duplexPipe) -> int?
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetToken(System.IO.Pipelines.PipeReader reader) -> int?
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.GetToken(System.IO.Pipelines.PipeWriter writer) -> int?
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MessageFormatterDuplexPipeTracker() -> void
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MultiplexingStream.get -> Nerdbank.Streams.MultiplexingStream
StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MultiplexingStream.set -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingDeserialized.set -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingSerialized.set -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.CreateProgress(StreamJsonRpc.JsonRpc rpc, object token, System.Type valueType) -> object
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.CreateProgress(StreamJsonRpc.JsonRpc rpc, object token) -> System.IProgress
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.GetTokenForProgress(object value) -> long
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.MessageFormatterProgressTracker() -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation.InvokeReport(object typedValue) -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation.ValueType.get -> System.Type
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.RequestIdBeingSerialized.set -> void
const StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressRequestSpecialMethod = "$/progress" -> string
static StreamJsonRpc.JsonRpc.Attach(StreamJsonRpc.IJsonRpcMessageHandler handler) -> T
static StreamJsonRpc.JsonRpc.Attach(StreamJsonRpc.IJsonRpcMessageHandler handler, StreamJsonRpc.JsonRpcProxyOptions options) -> T
diff --git a/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt b/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt
index 955b7bab8..0a8871934 100644
--- a/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt
@@ -30,31 +30,30 @@ StreamJsonRpc.Protocol.JsonRpcRequest.RequestId.set -> void
StreamJsonRpc.Protocol.JsonRpcResult.RequestId.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Protocol.JsonRpcResult.RequestId.set -> void
StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks
-StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ErrorReceived -> System.EventHandler
-StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.RequestTransmissionAborted -> System.EventHandler
-StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ResultReceived -> System.EventHandler
+StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.RequestTransmissionAborted -> System.EventHandler
+StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ResponseReceived -> System.EventHandler
+StreamJsonRpc.Reflection.IJsonRpcFormatterCallbacks.ResponseSent -> System.EventHandler
StreamJsonRpc.Reflection.IJsonRpcFormatterState
StreamJsonRpc.Reflection.IJsonRpcFormatterState.DeserializingMessageWithId.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Reflection.IJsonRpcFormatterState.SerializingMessageWithId.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Reflection.IJsonRpcFormatterState.SerializingRequest.get -> bool
StreamJsonRpc.Reflection.IJsonRpcMessageBufferManager
StreamJsonRpc.Reflection.IJsonRpcMessageBufferManager.DeserializationComplete(StreamJsonRpc.Protocol.JsonRpcMessage message) -> void
-StreamJsonRpc.Reflection.JsonRpcFormatterCallbackEventArgs
-StreamJsonRpc.Reflection.JsonRpcFormatterCallbackEventArgs.JsonRpcFormatterCallbackEventArgs(StreamJsonRpc.RequestId requestId) -> void
-StreamJsonRpc.Reflection.JsonRpcFormatterCallbackEventArgs.RequestId.get -> StreamJsonRpc.RequestId
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.OnResponseReceived(StreamJsonRpc.RequestId requestId, bool successful) -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.OnResponseSent(StreamJsonRpc.RequestId requestId, bool successful) -> void
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingDeserialized.get -> StreamJsonRpc.RequestId
-StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.RequestIdBeingSerialized.get -> StreamJsonRpc.RequestId
+StreamJsonRpc.Reflection.JsonRpcMessageEventArgs
+StreamJsonRpc.Reflection.JsonRpcMessageEventArgs.JsonRpcMessageEventArgs(StreamJsonRpc.RequestId requestId) -> void
+StreamJsonRpc.Reflection.JsonRpcMessageEventArgs.RequestId.get -> StreamJsonRpc.RequestId
+StreamJsonRpc.Reflection.JsonRpcResponseEventArgs
+StreamJsonRpc.Reflection.JsonRpcResponseEventArgs.IsSuccessfulResponse.get -> bool
+StreamJsonRpc.Reflection.JsonRpcResponseEventArgs.JsonRpcResponseEventArgs(StreamJsonRpc.RequestId requestId, bool isSuccessfulResponse) -> void
+StreamJsonRpc.Reflection.MessageFormatterDuplexPipeTracker.MessageFormatterDuplexPipeTracker(StreamJsonRpc.JsonRpc jsonRpc, StreamJsonRpc.Reflection.IJsonRpcFormatterState formatterState) -> void
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.CreateEnumerableProxy(System.Type enumeratedType, object handle) -> object
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.CreateEnumerableProxy(object handle) -> System.Collections.Generic.IAsyncEnumerable
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.GetToken(object enumerable) -> long
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.GetToken(System.Collections.Generic.IAsyncEnumerable enumerable) -> long
StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.MessageFormatterEnumerableTracker(StreamJsonRpc.JsonRpc jsonRpc, StreamJsonRpc.Reflection.IJsonRpcFormatterState formatterState) -> void
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.OnResponseReceived(StreamJsonRpc.RequestId requestId) -> void
+StreamJsonRpc.Reflection.MessageFormatterProgressTracker.MessageFormatterProgressTracker(StreamJsonRpc.JsonRpc jsonRpc, StreamJsonRpc.Reflection.IJsonRpcFormatterState formatterState) -> void
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation.Token.get -> long
-StreamJsonRpc.Reflection.MessageFormatterProgressTracker.RequestIdBeingSerialized.get -> StreamJsonRpc.RequestId
StreamJsonRpc.Reflection.MessageFormatterProgressTracker.TryGetProgressObject(long progressId, out StreamJsonRpc.Reflection.MessageFormatterProgressTracker.ProgressParamInformation valueType) -> bool
StreamJsonRpc.RemoteInvocationException.DeserializedErrorData.get -> object
StreamJsonRpc.RemoteInvocationException.RemoteInvocationException(string message, int errorCode, object errorData, object deserializedErrorData) -> void
@@ -84,9 +83,9 @@ static StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.CanSerialize(S
static StreamJsonRpc.RequestId.NotSpecified.get -> StreamJsonRpc.RequestId
virtual StreamJsonRpc.JsonRpc.CreateNewRequestId() -> StreamJsonRpc.RequestId
virtual StreamJsonRpc.JsonRpc.GetErrorDetailsDataType(StreamJsonRpc.Protocol.JsonRpcError error) -> System.Type
-virtual StreamJsonRpc.JsonRpc.OnErrorReceived(StreamJsonRpc.Protocol.JsonRpcError error) -> void
virtual StreamJsonRpc.JsonRpc.OnRequestTransmissionAborted(StreamJsonRpc.Protocol.JsonRpcRequest request) -> void
-virtual StreamJsonRpc.JsonRpc.OnResultReceived(StreamJsonRpc.Protocol.JsonRpcResult result) -> void
+virtual StreamJsonRpc.JsonRpc.OnResponseReceived(StreamJsonRpc.Protocol.JsonRpcMessage response) -> void
+virtual StreamJsonRpc.JsonRpc.OnResponseSent(StreamJsonRpc.Protocol.JsonRpcMessage response) -> void
virtual StreamJsonRpc.MessageHandlerBase.DisposeAsync() -> System.Threading.Tasks.Task
virtual StreamJsonRpc.MessageHandlerBase.DisposeReader() -> void
virtual StreamJsonRpc.MessageHandlerBase.DisposeWriter() -> void