예제 #1
0
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            (this.clientPipe as IDisposable)?.Dispose();
            this.clientPipe = null;
        }
예제 #2
0
        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));
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        protected override void OnConnectionResetSynchronized()
        {
            base.OnConnectionResetSynchronized();

            (this.clientPipe as IDisposable)?.Dispose();
            this.clientPipe = null;
        }
예제 #6
0
 /// <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;
 }
예제 #7
0
 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;
            }
예제 #9
0
 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);
        }
예제 #12
0
        public PipeReader?GetPipeReader(int?token)
        {
            IDuplexPipe?duplexPipe = this.GetPipe(token);

            if (duplexPipe != null)
            {
                duplexPipe.Output.Complete();
                return(duplexPipe.Input);
            }

            return(null);
        }
예제 #13
0
            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;
            }
예제 #14
0
            /// <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));
        }
예제 #17
0
    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();
        }
예제 #19
0
            /// <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));