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); }
public void Dispose() { this.Stop(); if (_listener != null) { SafeEnd.Dispose(_listener.Server); } if (_cancel != null) { _cancel.Cancel(); SafeEnd.Dispose(_cancel); } SafeEnd.Dispose(_negotiationQueue); }
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); }
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); }
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; } }
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); } } }
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; } } }