protected override void Dispose(bool disposing) { base.Dispose(disposing); (this.clientPipe as IDisposable)?.Dispose(); this.clientPipe = null; }
protected override Task <IDuplexPipe> ConnectPipelineAsync(int sendMaxMessageSize, int receiveMaxMessageSize, CancellationToken cancellationToken) { var pipe = this.clientPipe ?? throw new ObjectDisposedException(this.ToString()); this.clientPipe = null; return(Task.FromResult(pipe)); }
public int?GetToken(IDuplexPipe?duplexPipe) { Verify.NotDisposed(this); MultiplexingStream mxstream = this.GetMultiplexingStreamOrThrow(); if (this.RequestIdBeingSerialized.IsEmpty) { throw new NotSupportedException(Resources.MarshaledObjectInResponseOrNotificationError); } if (duplexPipe is null) { return(null); } MultiplexingStream.Channel channel = mxstream.CreateChannel(new MultiplexingStream.ChannelOptions { ExistingPipe = duplexPipe }); 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.Id, channel); channel.Completion.ContinueWith(_ => ImmutableInterlocked.TryRemove(ref this.openOutboundChannels, channel.Id, out MultiplexingStream.Channel removedChannel), CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default).Forget(); return(channel.Id); }
public Task StartAsync(Uri url, TransferFormat transferFormat) { if (url is null) { throw new ArgumentNullException(nameof(url)); } if (transferFormat != TransferFormat.Text) { throw new ArgumentException( $"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat)); } Log.StartTransport(_logger, transferFormat); // Create pipe var options = ClientPipeOptions.DefaultOptions; var pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; // Start streams Running = ProcessAsync(url); return(Task.CompletedTask); }
protected override void OnConnectionResetSynchronized() { base.OnConnectionResetSynchronized(); (this.clientPipe as IDisposable)?.Dispose(); this.clientPipe = null; }
/// <summary> /// Initializes a new instance of the <see cref="Connection" /> class. /// </summary> /// <param name="duplexPipe">The duplex pipe of the (socket) connection.</param> /// <param name="decryptionPipe">The decryption pipe.</param> /// <param name="encryptionPipe">The encryption pipe.</param> /// <param name="logger">The logger.</param> public Connection(IDuplexPipe duplexPipe, IPipelinedDecryptor?decryptionPipe, IPipelinedEncryptor?encryptionPipe, ILogger <Connection> logger) { this.duplexPipe = duplexPipe; this.encryptionPipe = encryptionPipe; this.logger = logger; this.Source = decryptionPipe?.Reader ?? this.duplexPipe !.Input; this.remoteEndPoint = this.SocketConnection?.Socket.RemoteEndPoint; }
public ConnectionFromPipe(IDuplexPipe pipe, bool leaveOpen, IConnectionProperties?properties, EndPoint?localEndPoint, EndPoint?remoteEndPoint) { _originalPipe = pipe; _leaveOpen = leaveOpen; _properties = properties; LocalEndPoint = localEndPoint; RemoteEndPoint = remoteEndPoint; }
public async ValueTask DisposeAsync() { if (this.isListening) { await this.StopAsync().ContextFree(); } (this.pipe as IDisposable)?.Dispose(); this.pipe = null; }
public InprocRpcConnection( RpcConnectionInfo connectionInfo, IDuplexPipe clientPipe, IRpcClientOptions?options = null, LightweightOptions?lightweightOptions = null) : base(connectionInfo, options, LightweightProxyGenerator.Default, lightweightOptions) { this.clientPipe = clientPipe; }
public void Listen() { if (this.isListening || this.pipe == null) { throw new InvalidOperationException("DirectListener is already listening or has been stopped."); } this.isListening = true; this.connectionHandler.RunPipelineClientAsync(this.pipe, this.endPoint, Thread.CurrentPrincipal);//, this.clientCts.Token); this.pipe = null; }
public PipeWriter?GetPipeWriter(ulong?token) { IDuplexPipe?duplexPipe = this.GetPipe(token); if (duplexPipe is not null) { duplexPipe.Input.Complete(); return(duplexPipe.Output); } return(null); }
public PipeReader?GetPipeReader(int?token) { IDuplexPipe?duplexPipe = this.GetPipe(token); if (duplexPipe != null) { duplexPipe.Output.Complete(); return(duplexPipe.Input); } return(null); }
protected override async ValueTask CloseAsyncCore(ConnectionCloseMethod method, CancellationToken cancellationToken) { if (_originalPipe == null) { return; } Exception?inputException, outputException; if (method == ConnectionCloseMethod.GracefulShutdown) { // Flush happens implicitly from CompleteAsync(null), so only flush here if we need cancellation. if (cancellationToken.CanBeCanceled) { FlushResult r = await _originalPipe.Output.FlushAsync(cancellationToken).ConfigureAwait(false); if (r.IsCanceled) { cancellationToken.ThrowIfCancellationRequested(); } } inputException = null; outputException = null; } else { inputException = ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedException(nameof(Connection))); outputException = ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedException(nameof(Connection))); } await _originalPipe.Input.CompleteAsync(inputException).ConfigureAwait(false); await _originalPipe.Output.CompleteAsync(outputException).ConfigureAwait(false); if (!_leaveOpen) { switch (_originalPipe) { case IAsyncDisposable d: await d.DisposeAsync().ConfigureAwait(false); break; case IDisposable d: d.Dispose(); break; } } _originalPipe = null; }
/// <summary> /// Apply channel options to this channel, including setting up or linking to an user-supplied pipe writer/reader pair. /// </summary> /// <param name="channelOptions">The channel options to apply.</param> private void ApplyChannelOptions(ChannelOptions channelOptions) { Requires.NotNull(channelOptions, nameof(channelOptions)); Assumes.Null(this.TraceSource); // We've already applied options try { this.TraceSource = channelOptions.TraceSource ?? this.MultiplexingStream.DefaultChannelTraceSourceFactory?.Invoke(this.QualifiedId, this.Name) ?? new TraceSource($"{nameof(Streams.MultiplexingStream)}.{nameof(Channel)} {this.QualifiedId} ({this.Name})", SourceLevels.Critical); lock (this.SyncObject) { Verify.NotDisposed(this); this.InitializeOwnPipes(); if (channelOptions.ExistingPipe is object) { Assumes.NotNull(this.channelIO); this.existingPipe = channelOptions.ExistingPipe; this.existingPipeGiven = true; // We always want to write ALL received data to the user's ExistingPipe, rather than truncating it on disposal, so don't use a cancellation token in that direction. this.DisposeSelfOnFailure(this.channelIO.Input.LinkToAsync(channelOptions.ExistingPipe.Output)); // Upon disposal, we no longer want to continue reading from the user's ExistingPipe into our buffer since we won't be propagating it any further, so use our DisposalToken. this.DisposeSelfOnFailure(channelOptions.ExistingPipe.Input.LinkToAsync(this.channelIO.Output, this.DisposalToken)); } else { this.existingPipeGiven = false; } } this.mxStreamIOReaderCompleted = this.ProcessOutboundTransmissionsAsync(); this.DisposeSelfOnFailure(this.mxStreamIOReaderCompleted); this.DisposeSelfOnFailure(this.AutoCloseOnPipesClosureAsync()); } catch (Exception ex) { this.optionsAppliedTaskSource?.TrySetException(ex); throw; } finally { this.optionsAppliedTaskSource?.TrySetResult(null); } }
/// <summary> /// Set up our own (buffering) Pipes if they have not been set up yet. /// </summary> /// <param name="inputPipeOptions">The options for the reading relay <see cref="Pipe"/>. Must not be null.</param> private void InitializeOwnPipes(PipeOptions inputPipeOptions) { Requires.NotNull(inputPipeOptions, nameof(inputPipeOptions)); lock (this.SyncObject) { Verify.NotDisposed(this); if (this.mxStreamIOReader == null) { var writerRelay = new Pipe(); var readerRelay = new Pipe(inputPipeOptions); this.mxStreamIOReader = writerRelay.Reader; this.mxStreamIOWriter = readerRelay.Writer; this.channelIO = new DuplexPipe(readerRelay.Reader, writerRelay.Writer); } } }
protected internal override ILightweightRpcListener CreateListener( IRpcConnectionHandler connectionHandler, int maxRequestSize, int maxResponseSize) { IDuplexPipe pipe; lock (this.syncRoot) { if (this.clientPipe == null) { throw new InvalidOperationException("A DirectLightweightRpcEndPoint listener can only be created once."); } pipe = this.clientPipe; this.clientPipe = null; } return(new DirectListener(this, pipe, connectionHandler)); }
private async ValueTask <ServerBound> ConnectAsync(string?dst, IPAddress?dstAddress, ushort dstPort, CancellationToken token = default) { IDuplexPipe pipe = await HandshakeAsync(token); ServerBound bound = await SendCommandAsync( pipe, Command.Connect, dst, dstAddress, dstPort, token ); _pipe = pipe; Status = Status.Established; return(bound); }
public async Task StartAsync(Uri url, TransferFormat transferFormat) { if (url == null) { throw new ArgumentNullException(nameof(url)); } if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text) { throw new ArgumentException( $"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat)); } Log.StartTransport(_logger, transferFormat); // Create connection _startTask = new TaskCompletionSource <object?>(); await _jsRuntime.InvokeAsync <object>( "BlazorSignalR.WebSocketsTransport.CreateConnection", url.ToString(), transferFormat == TransferFormat.Binary, DotNetObjectReference.Create(this)); await _startTask.Task.ConfigureAwait(false); _startTask = null; Log.StartedTransport(_logger); // Create the pipe pair (Application's writer is connected to Transport's reader, and vice versa) var options = ClientPipeOptions.DefaultOptions; var pair = DuplexPipe.CreateConnectionPair(options, options); _transport = pair.Transport; _application = pair.Application; Running = ProcessSocketAsync(); }
/// <summary> /// Set up our own (buffering) Pipes if they have not been set up yet. /// </summary> private void InitializeOwnPipes() { lock (this.SyncObject) { Verify.NotDisposed(this); if (this.mxStreamIOReader is null) { if (this.localWindowSize is null) { // If an offer came in along with data before we accepted the channel, we have to set up the pipe // before we know what the preferred local window size is. We can't change it after the fact, so just use the default. this.localWindowSize = this.MultiplexingStream.DefaultChannelReceivingWindowSize; } var writerRelay = new Pipe(); var readerRelay = this.BackpressureSupportEnabled ? new Pipe(new PipeOptions(pauseWriterThreshold: this.localWindowSize.Value + 1)) // +1 prevents pause when remote window is exactly filled : new Pipe(); this.mxStreamIOReader = writerRelay.Reader; this.mxStreamIOWriter = readerRelay.Writer; this.channelIO = new DuplexPipe(this.BackpressureSupportEnabled ? new WindowPipeReader(this, readerRelay.Reader) : readerRelay.Reader, writerRelay.Writer); } } }
/// <summary> /// Apply channel options to this channel, including setting up or migrating to an user-supplied pipe writer/reader pair. /// </summary> /// <param name="channelOptions">The channel options to apply.</param> private void ApplyChannelOptions(ChannelOptions channelOptions) { Requires.NotNull(channelOptions, nameof(channelOptions)); Assumes.Null(this.TraceSource); // We've already applied options try { this.TraceSource = channelOptions.TraceSource ?? this.MultiplexingStream.DefaultChannelTraceSourceFactory?.Invoke(this.Id, this.Name) ?? new TraceSource($"{nameof(Streams.MultiplexingStream)}.{nameof(Channel)} {this.Id} ({this.Name})", SourceLevels.Critical); lock (this.SyncObject) { Verify.NotDisposed(this); if (channelOptions.ExistingPipe != null) { if (this.mxStreamIOWriter != null) { // A Pipe was already created (because data has been coming in for this channel even before it was accepted). // To be most efficient, we need to: // 1. Start forwarding all bytes written with this.mxStreamIOWriter to channelOptions.ExistingPipe.Output // 2. Arrange for the *next* call to GetReceivedMessagePipeWriterAsync to: // call this.mxStreamIOWriter.Complete() // wait for our forwarding code to finish (without propagating copmletion to channel.ExistingPipe.Output) // return channel.ExistingPipe.Output // From then on, GetReceivedMessagePipeWriterAsync should simply return channel.ExistingPipe.Output // Since this channel hasn't yet been exposed to the local owner, we can just replace the PipeWriter they use to transmit. // Take ownership of reading bytes that the MultiplexingStream may have already written to this channel. var mxStreamIncomingBytesReader = this.channelIO !.Input; this.channelIO = null; // Forward any bytes written by the MultiplexingStream to the ExistingPipe.Output writer, // and make that ExistingPipe.Output writer available only after the old Pipe-based writer has completed. // First, capture the ExistingPipe as a local since ChannelOptions is a mutable type, and we're going to need // its current value later on. var existingPipe = channelOptions.ExistingPipe; this.switchingToExistingPipe = Task.Run(async delegate { // Await propagation of all bytes. Don't complete the ExistingPipe.Output when we're done because we still want to use it. await mxStreamIncomingBytesReader.LinkToAsync(existingPipe.Output, propagateSuccessfulCompletion: false).ConfigureAwait(false); return(existingPipe.Output); }); } else { // We haven't created a Pipe yet, so we can simply direct all writing to the ExistingPipe.Output immediately. this.mxStreamIOWriter = channelOptions.ExistingPipe.Output; } this.mxStreamIOReader = channelOptions.ExistingPipe.Input; } else if (channelOptions.InputPipeOptions != null && this.mxStreamIOWriter != null) { // Similar strategy to the situation above with ExistingPipe. // Take ownership of reading bytes that the MultiplexingStream may have already written to this channel. var mxStreamIncomingBytesReader = this.channelIO !.Input; var writerRelay = new Pipe(); var readerRelay = new Pipe(channelOptions.InputPipeOptions); this.mxStreamIOReader = writerRelay.Reader; this.channelIO = new DuplexPipe(readerRelay.Reader, writerRelay.Writer); this.switchingToExistingPipe = Task.Run(async delegate { // Await propagation of all bytes. Don't complete the readerRelay.Writer when we're done because we still want to use it. await mxStreamIncomingBytesReader.LinkToAsync(readerRelay.Writer, propagateSuccessfulCompletion: false).ConfigureAwait(false); return(readerRelay.Writer); }); } else { this.InitializeOwnPipes(channelOptions.InputPipeOptions ?? PipeOptions.Default); } } this.mxStreamIOReaderCompleted = this.ProcessOutboundTransmissionsAsync(); this.DisposeSelfOnFailure(this.mxStreamIOReaderCompleted); this.DisposeSelfOnFailure(this.AutoCloseOnPipesClosureAsync()); } catch (Exception ex) { this.optionsAppliedTaskSource?.TrySetException(ex); throw; } finally { this.optionsAppliedTaskSource?.TrySetResult(null); } }
/// <summary> /// /// </summary> /// <param name="clientPipe"></param> public InprocRpcEndPoint(IDuplexPipe clientPipe) { this.clientPipe = clientPipe; }
internal DirectListener(InprocRpcEndPoint endPoint, IDuplexPipe pipe, IRpcConnectionHandler connectionHandler) { this.endPoint = endPoint; this.pipe = pipe; this.connectionHandler = connectionHandler; }
public async Task RunAsync(CancellationToken cancellationToken) { Stream stream = _inboundStream; IDuplexPipe?tunnel = null; try { using (var parser = new HttpParser(stream)) { // Parse HTTP header if (!(await parser.ParseAsync(cancellationToken).ConfigureAwait(false))) { await WriteBadRequestAsync(stream, cancellationToken).ConfigureAwait(false); return; } // Authenticate if (!Authenticate(parser.ProxyAuthorization)) { if (parser.ProxyAuthorization is null) { await WriteProxyAuthenticationAsync(stream, cancellationToken).ConfigureAwait(false); return; } await WriteForbiddenAsync(stream, cancellationToken).ConfigureAwait(false); return; } // Process CONNECT requests if (parser.Method.Equals(s_connectMethod)) { if (!TryParseConnectEndpoint(parser.Url, out EndPoint? endPoint)) { await WriteBadRequestAsync(stream, cancellationToken).ConfigureAwait(false); return; } try { tunnel = await _tunnelFactory.CreateAsync(endPoint, cancellationToken).ConfigureAwait(false); } catch (Exception) { tunnel = null; } if (tunnel is null) { await WriteServerFailureAsync(stream, cancellationToken).ConfigureAwait(false); return; } await WriteOkStatusAsync(stream, cancellationToken).ConfigureAwait(false); await SendInitialRequestForConnectAsync(parser, tunnel.Output, cancellationToken).ConfigureAwait(false); } // Process other requests else { if (!Uri.TryCreate(parser.Url, UriKind.Absolute, out Uri? uri) || !uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase)) { await WriteBadRequestAsync(stream, cancellationToken).ConfigureAwait(false); return; } EndPoint endPoint; if (uri.HostNameType == UriHostNameType.IPv4 || uri.HostNameType == UriHostNameType.IPv6) { if (!IPAddress.TryParse(uri.Host, out IPAddress ip)) { await WriteBadRequestAsync(stream, cancellationToken).ConfigureAwait(false); return; } endPoint = new IPEndPoint(ip, uri.Port); } else { endPoint = new DnsEndPoint(uri.DnsSafeHost, uri.Port); } try { tunnel = await _tunnelFactory.CreateAsync(endPoint, cancellationToken).ConfigureAwait(false); } catch (Exception) { tunnel = null; } if (tunnel is null) { await WriteServerFailureAsync(stream, cancellationToken).ConfigureAwait(false); return; } await SendInitialRequestForHttpAsync(parser, uri, tunnel.Output, cancellationToken).ConfigureAwait(false); } } await PumpDataAsync(stream, tunnel, cancellationToken).ConfigureAwait(false); } finally { if (!(tunnel is null) && tunnel is IDisposable disposable) { disposable.Dispose(); } } }
public int?GetToken(IDuplexPipe?duplexPipe) => checked ((int?)this.GetULongToken(duplexPipe));