public async Task ServerHasNoLeakWhenRequestIsRejected() { Task <MultiplexingStream.Channel> clientRpcChannelTask = this.clientMx.OfferChannelAsync("custom", this.TimeoutToken); Task <MultiplexingStream.Channel> serverRpcChannelTask = this.serverMx.AcceptChannelAsync("custom", this.TimeoutToken); MultiplexingStream.Channel clientRpcChannel = await clientRpcChannelTask; MultiplexingStream.Channel serverRpcChannel = await serverRpcChannelTask; this.serverRpc = new JsonRpc(new HeaderDelimitedMessageHandler(serverRpcChannel, new JsonMessageFormatter { MultiplexingStream = this.serverMx }), new ServerWithOverloads()); this.serverRpc.StartListening(); // Send a message, advertising a channel. // We *deliberately* avoid using the JsonRpc on the client side and send arguments that will cause no good match on the server // so as to exercise and verify the path where the server sends an error back to the client, after trying to open the channel, but without invoking the server method, // and without the client having a chance to close the channel before we can verify that the server did. MultiplexingStream.Channel oobChannel = this.clientMx.CreateChannel(); var clientHandler = new HeaderDelimitedMessageHandler(clientRpcChannel, new JsonMessageFormatter()); await clientHandler.WriteAsync( new StreamJsonRpc.Protocol.JsonRpcRequest { Id = 1, Method = nameof(ServerWithOverloads.OverloadedMethod), ArgumentsList = new object[] { false, oobChannel.Id, new object() }, }, this.TimeoutToken); await clientRpcChannel.Output.FlushAsync(this.TimeoutToken); // Wait for the client to receive the error message back, but don't let any of our client code handle it. ReadResult readResult = await clientRpcChannel.Input.ReadAsync(this.TimeoutToken); clientRpcChannel.Input.AdvanceTo(readResult.Buffer.Start); // The test intends to exercise that the server actually accepts (or perhaps rejects) the channel. await oobChannel.Acceptance.WithCancellation(this.TimeoutToken); // Assuming it has accepted it, we want to verify that the server closes the channel. await oobChannel.Completion.WithCancellation(this.TimeoutToken); }
public ulong?GetULongToken(IDuplexPipe?duplexPipe) { Verify.NotDisposed(this); MultiplexingStream mxstream = this.GetMultiplexingStreamOrThrow(); if (this.formatterState.SerializingMessageWithId.IsEmpty) { duplexPipe?.Output.Complete(); duplexPipe?.Input.Complete(); throw new NotSupportedException(Resources.MarshaledObjectInNotificationError); } if (duplexPipe is null) { return(null); } MultiplexingStream.Channel channel = mxstream.CreateChannel(new MultiplexingStream.ChannelOptions { ExistingPipe = duplexPipe }); if (!this.RequestIdBeingSerialized.IsEmpty) { ImmutableInterlocked.AddOrUpdate( ref this.outboundRequestChannelMap, this.RequestIdBeingSerialized, ImmutableList.Create(channel), (key, value) => value.Add(channel)); } // Track open channels to assist in diagnosing abandoned channels. ImmutableInterlocked.TryAdd(ref this.openOutboundChannels, channel.QualifiedId, channel); channel.Completion.ContinueWith(_ => ImmutableInterlocked.TryRemove(ref this.openOutboundChannels, channel.QualifiedId, out MultiplexingStream.Channel? removedChannel), CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default).Forget(); return(channel.QualifiedId.Id); }
public async Task InitializeAsync() { Tuple <Nerdbank.FullDuplexStream, Nerdbank.FullDuplexStream> streams = Nerdbank.FullDuplexStream.CreateStreams(); TraceSource mxServerTraceSource = new TraceSource("MX Server", SourceLevels.Information); TraceSource mxClientTraceSource = new TraceSource("MX Client", SourceLevels.Information); mxServerTraceSource.Listeners.Add(new XunitTraceListener(this.Logger)); mxClientTraceSource.Listeners.Add(new XunitTraceListener(this.Logger)); MultiplexingStream[] mxStreams = await Task.WhenAll( MultiplexingStream.CreateAsync( streams.Item1, new MultiplexingStream.Options { TraceSource = mxServerTraceSource, DefaultChannelTraceSourceFactory = (id, name) => new TraceSource("MX Server channel " + id, SourceLevels.Verbose) { Listeners = { new XunitTraceListener(this.Logger) } }, }, this.TimeoutToken), MultiplexingStream.CreateAsync( streams.Item2, new MultiplexingStream.Options { TraceSource = mxClientTraceSource, DefaultChannelTraceSourceFactory = (id, name) => new TraceSource("MX Client channel " + id, SourceLevels.Verbose) { Listeners = { new XunitTraceListener(this.Logger) } }, }, this.TimeoutToken)); this.serverMx = mxStreams[0]; this.clientMx = mxStreams[1]; MultiplexingStream.Channel[] rpcStreams = await Task.WhenAll( this.serverMx.AcceptChannelAsync(string.Empty, this.TimeoutToken), this.clientMx.OfferChannelAsync(string.Empty, this.TimeoutToken)); MultiplexingStream.Channel rpcServerStream = rpcStreams[0]; MultiplexingStream.Channel rpcClientStream = rpcStreams[1]; this.InitializeFormattersAndHandlers(); var serverHandler = new LengthHeaderMessageHandler(rpcServerStream, this.serverMessageFormatter); var clientHandler = new LengthHeaderMessageHandler(rpcClientStream, this.clientMessageFormatter); this.serverRpc = new JsonRpc(serverHandler, this.server); this.clientRpc = new JsonRpc(clientHandler); this.serverRpc.TraceSource = new TraceSource("Server", SourceLevels.Information); this.clientRpc.TraceSource = new TraceSource("Client", SourceLevels.Information); this.serverRpc.TraceSource.Listeners.Add(new XunitTraceListener(this.Logger)); this.clientRpc.TraceSource.Listeners.Add(new XunitTraceListener(this.Logger)); this.serverRpc.StartListening(); this.clientRpc.StartListening(); }