public async Task <WebSocket> ConnectAsync([NotNull] Uri address, Headers <RequestHeader> requestHeaders = null, CancellationToken cancellation = default(CancellationToken))
        {
            try
            {
                cancellation.ThrowIfCancellationRequested();
                if (this.workCancellationSource.IsCancellationRequested)
                {
                    throw new WebSocketException("Client is currently closing or closed.");
                }

                var workCancellation        = this.workCancellationSource.Token;
                var negotiationCancellation = this.negotiationsTimeoutQueue?.GetSubscriptionList().Token ?? CancellationToken.None;

                if (cancellation.CanBeCanceled || workCancellation.CanBeCanceled || negotiationCancellation.CanBeCanceled)
                {
                    cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellation, workCancellation, negotiationCancellation).Token;
                }

                var request = new WebSocketHttpRequest(HttpRequestDirection.Outgoing)
                {
                    RequestUri = address,
                };

                if (requestHeaders != null)
                {
                    request.Headers.AddMany(requestHeaders);
                }

                var handshake      = new WebSocketHandshake(request);
                var pendingRequest = this.OpenConnectionAsync(handshake, cancellation);

                this.pendingRequests.TryAdd(handshake, pendingRequest);

                var webSocket = await pendingRequest.IgnoreFaultOrCancellation().ConfigureAwait(false);

                if (!workCancellation.IsCancellationRequested && negotiationCancellation.IsCancellationRequested)
                {
                    SafeEnd.Dispose(webSocket, this.log);
                    throw new WebSocketException("Negotiation timeout.");
                }

                if (this.pendingRequests.TryRemove(handshake, out pendingRequest) && this.workCancellationSource.IsCancellationRequested && this.pendingRequests.IsEmpty)
                {
                    this.closeEvent.Set();
                }

                webSocket = await pendingRequest.ConfigureAwait(false);

                this.pingQueue?.GetSubscriptionList().Add(webSocket);

                return(webSocket);
            }
            catch (Exception connectionError)
                when(connectionError.Unwrap() is ThreadAbortException == false &&
                     connectionError.Unwrap() is OperationCanceledException == false &&
                     connectionError.Unwrap() is WebSocketException == false)
                {
                    throw new WebSocketException($"An unknown error occurred while connection to '{address}'. More detailed information in inner exception.", connectionError.Unwrap());
                }
        }
        public async Task CloseAsync()
        {
            this.workCancellationSource.Cancel(throwOnFirstException: false);

            // TODO: wait for all pending websockets and set closeEvent after it

            await this.closeEvent;

            SafeEnd.Dispose(this.pingQueue, this.log);
            SafeEnd.Dispose(this.negotiationsTimeoutQueue, this.log);
            SafeEnd.Dispose(this.workCancellationSource, this.log);

            this.options.SetUsed(false);
        }
        public void Dispose()
        {
            Stop();

            if (_listener != null)
            {
                SafeEnd.Dispose(_listener.Server);
            }

            if (_disposing != null)
            {
                _disposing.Cancel();
                SafeEnd.Dispose(_disposing);
            }

            SafeEnd.Dispose(_negotiationQueue);
        }
Example #4
0
        public void Dispose()
        {
            this.Stop();

            if (_listener != null)
            {
                SafeEnd.Dispose(_listener.Server);
            }

            if (_cancel != null)
            {
                _cancel.Cancel();
                SafeEnd.Dispose(_cancel);
            }

            SafeEnd.Dispose(_negotiationQueue);
        }
Example #5
0
        public void Dispose()
        {
            if (Interlocked.Exchange(ref this.state, STATE_DISPOSED) == STATE_DISPOSED)
            {
                return;
            }

            this.stopConditionSource?.Set();

            this.localEndPoints = EmptyEndPoints;
            var listeners = Interlocked.Exchange(ref this.listeners, EmptyListeners);

            foreach (var listener in listeners)
            {
                SafeEnd.Dispose(listener, this.log);
            }

            SafeEnd.Dispose(this.negotiationQueue, this.log);
        }
Example #6
0
        private void CleanupPendingConnections(Task <NetworkConnection>[] acceptTasks)
        {
            if (acceptTasks == null)
            {
                throw new ArgumentNullException(nameof(acceptTasks));
            }

            foreach (var acceptTask in acceptTasks)
            {
                acceptTask?.ContinueWith
                (
                    t => SafeEnd.Dispose(t.Result, this.log),
                    CancellationToken.None,
                    TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously,
                    TaskScheduler.Current
                ).LogFault(this.log);
            }
            Array.Clear(acceptTasks, 0, acceptTasks.Length);
        }
Example #7
0
        public async Task <NetworkConnection> ExtendConnectionAsync(NetworkConnection networkConnection)
        {
            if (networkConnection == null)
            {
                throw new ArgumentNullException(nameof(networkConnection));
            }

            var ssl = new SslStream(networkConnection.AsStream(), false, _validation);

            try
            {
                await ssl.AuthenticateAsServerAsync(_certificate, _validation != null, _protocols, false).ConfigureAwait(false);

                return(new SslNetworkConnection(ssl, networkConnection));
            }
            catch
            {
                SafeEnd.Dispose(ssl);
                throw;
            }
        }
Example #8
0
        public async Task StopAsync()
        {
            if (Interlocked.CompareExchange(ref state, STATE_STOPPING, STATE_STARTED) != STATE_STARTED)
            {
                throw new WebSocketException("Failed to stop listener from current state. Maybe it is disposed or not started.");
            }

            this.options.SetUsed(false);
            var stopCondition = this.stopConditionSource;

            if (this.log.IsDebugEnabled)
            {
                this.log.Debug($"{nameof(WebSocketListener)} is stopping.");
            }

            // TODO: wait for all pending websockets and set stopCondition after it

            this.localEndPoints = EmptyEndPoints;
            var listeners = Interlocked.Exchange(ref this.listeners, EmptyListeners);

            foreach (var listener in listeners)
            {
                SafeEnd.Dispose(listener, this.log);
            }

            if (stopCondition != null)
            {
                await stopCondition;
            }

            if (Interlocked.CompareExchange(ref state, STATE_STOPPED, STATE_STOPPING) != STATE_STOPPING)
            {
                throw new WebSocketException("Failed to stop listener from current state. Maybe it is disposed.");
            }

            if (this.log.IsDebugEnabled)
            {
                this.log.Debug($"{nameof(WebSocketListener)} is stopped.");
            }
        }
        private async Task <WebSocket> OpenConnectionAsync(WebSocketHandshake handshake, CancellationToken cancellation)
        {
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }

            var connection = default(NetworkConnection);
            var webSocket  = default(WebSocket);

            try
            {
                cancellation.ThrowIfCancellationRequested();

                var requestUri = handshake.Request.RequestUri;
                var transport  = default(WebSocketTransport);
                if (this.options.Transports.TryGetWebSocketTransport(requestUri, out transport) == false)
                {
                    throw new WebSocketException($"Unable to find transport for '{requestUri}'. " +
                                                 $"Available transports are: {string.Join(", ", this.options.Transports.SelectMany(t => t.Schemes).Distinct())}.");
                }

                connection = await transport.ConnectAsync(requestUri, this.options, cancellation).ConfigureAwait(false);

                handshake.Request.IsSecure       = transport.ShouldUseSsl(requestUri);
                handshake.Request.LocalEndPoint  = connection.LocalEndPoint ?? WebSocketHttpRequest.NoAddress;
                handshake.Request.RemoteEndPoint = connection.RemoteEndPoint ?? WebSocketHttpRequest.NoAddress;

                webSocket = await this.NegotiateRequestAsync(handshake, connection, cancellation).ConfigureAwait(false);

                return(webSocket);
            }
            finally
            {
                if (webSocket == null) // no connection were made
                {
                    SafeEnd.Dispose(connection, this.log);
                }
            }
        }
Example #10
0
        public async Task StartAsync()
        {
            if (this.options.Standards.Count <= 0)
            {
                throw new WebSocketException($"There are no WebSocket standards. Please, register standards using {nameof(WebSocketListenerOptions)}.{nameof(WebSocketListenerOptions.Standards)}.");
            }
            if (this.options.Transports.Count <= 0)
            {
                throw new WebSocketException($"There are no WebSocket transports. Please, register transports using {nameof(WebSocketListenerOptions)}.{nameof(WebSocketListenerOptions.Transports)}.");
            }

            if (Interlocked.CompareExchange(ref state, STATE_STARTING, STATE_STOPPED) != STATE_STOPPED)
            {
                throw new WebSocketException("Failed to start listener from current state. Maybe it is disposed or already started.");
            }

            this.options.SetUsed(true);
            var listeners = default(Listener[]);

            try
            {
                if (this.log.IsDebugEnabled)
                {
                    this.log.Debug($"{nameof(WebSocketListener)} is starting.");
                }

                var endPoints = new Tuple <Uri, WebSocketTransport> [this.listenEndPoints.Length];
                for (var i = 0; i < this.listenEndPoints.Length; i++)
                {
                    var listenEndPoint = this.listenEndPoints[i];
                    var transport      = default(WebSocketTransport);
                    if (this.options.Transports.TryGetWebSocketTransport(listenEndPoint, out transport) == false)
                    {
                        throw new WebSocketException($"Unable to find transport for '{listenEndPoint}'. Available transports are: {string.Join(", ", this.options.Transports.SelectMany(t => t.Schemes).Distinct())}.");
                    }

                    endPoints[i] = Tuple.Create(listenEndPoint, transport);
                }

                listeners = new Listener[endPoints.Length];
                for (var i = 0; i < endPoints.Length; i++)
                {
                    listeners[i] = await endPoints[i].Item2.ListenAsync(endPoints[i].Item1, this.options).ConfigureAwait(false);
                }


                this.listeners           = listeners;
                this.localEndPoints      = this.listeners.SelectMany(l => l.LocalEndpoints).ToArray();
                this.stopConditionSource = new AsyncConditionSource(isSet: true)
                {
                    ContinueOnCapturedContext = false
                };

                if (Interlocked.CompareExchange(ref state, STATE_STARTED, STATE_STARTING) != STATE_STARTING)
                {
                    throw new WebSocketException("Failed to start listener from current state. Maybe it is disposed.");
                }

                this.AcceptConnectionsAsync().LogFault(this.log);

                if (this.log.IsDebugEnabled)
                {
                    this.log.Debug($"{nameof(WebSocketListener)} is started.");
                }

                listeners = null;
            }
            catch
            {
                this.options.SetUsed(false);
                throw;
            }
            finally
            {
                // try to revert from starting state to stopped state
                Interlocked.CompareExchange(ref state, STATE_STOPPED, STATE_STARTING);

                if (listeners != null)
                {
                    foreach (var listener in listeners)
                    {
                        SafeEnd.Dispose(listener);
                    }

                    this.listeners           = EmptyListeners;
                    this.localEndPoints      = EmptyEndPoints;
                    this.stopConditionSource = null;
                }
            }
        }