/// <summary> /// Establishes a new send-side HybridConnection and returns the Stream. /// </summary> public async Task <HybridConnectionStream> CreateConnectionAsync() { TrackingContext trackingContext = CreateTrackingContext(this.Address); string traceSource = nameof(HybridConnectionClient) + "(" + trackingContext.ToString() + ")"; var timeoutHelper = new TimeoutHelper(this.OperationTimeout); RelayEventSource.Log.RelayClientConnectStart(traceSource); try { var webSocket = new ClientWebSocket(); webSocket.Options.Proxy = this.Proxy; webSocket.Options.KeepAliveInterval = HybridConnectionConstants.KeepAliveInterval; webSocket.Options.SetBuffer(this.ConnectionBufferSize, this.ConnectionBufferSize); if (this.TokenProvider != null) { RelayEventSource.Log.GetTokenStart(traceSource); string audience = this.Address.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped); var token = await this.TokenProvider.GetTokenAsync(audience, TokenProvider.DefaultTokenTimeout).ConfigureAwait(false); RelayEventSource.Log.GetTokenStop(token.ExpiresAtUtc); webSocket.Options.SetRequestHeader(RelayConstants.ServiceBusAuthorizationHeaderName, token.TokenString); } // Build the websocket uri, e.g. "wss://contoso.servicebus.windows.net:443/$hc/endpoint1?sb-hc-action=connect&sb-hc-id=E2E_TRACKING_ID" Uri webSocketUri = HybridConnectionUtility.BuildUri( this.Address.Host, this.Address.Port, this.Address.AbsolutePath, this.Address.Query, HybridConnectionConstants.Actions.Connect, trackingContext.TrackingId); using (var cancelSource = new CancellationTokenSource(timeoutHelper.RemainingTime())) { await webSocket.ConnectAsync(webSocketUri, cancelSource.Token).ConfigureAwait(false); } #if NET45 // TODO: Flow Response Headers in NETSTANDARD ClientWebSocket var trackingId = webSocket.ResponseHeaders[TrackingContext.TrackingIdName]; if (!string.IsNullOrEmpty(trackingId)) { // Update to the flown trackingId (which has _GX suffix) trackingContext = TrackingContext.Create(trackingId, trackingContext.SubsystemId); traceSource = nameof(HybridConnectionClient) + "(" + trackingContext.ToString() + ")"; } #endif return(new WebSocketStream(webSocket, trackingContext)); } catch (WebSocketException wsException) { throw RelayEventSource.Log.ThrowingException(WebSocketExceptionHelper.ConvertToRelayContract(wsException), traceSource); } finally { RelayEventSource.Log.RelayClientConnectStop(); } }
/// <summary> /// Establishes a new send-side HybridConnection and returns the Stream. /// </summary> public async Task <HybridConnectionStream> CreateConnectionAsync(IDictionary <string, string> requestHeaders) { TrackingContext trackingContext = CreateTrackingContext(this.Address); string traceSource = nameof(HybridConnectionClient) + "(" + trackingContext + ")"; // todo - Check if timeout helper needs to be started here. var timeoutHelper = TimeoutHelper.CreateOnly(this.OperationTimeout); RelayEventSource.Log.ObjectConnecting(traceSource, trackingContext); var webSocket = this.ClientWebSocketFactory.Create(); try { DefaultWebProxy.ConfigureProxy(webSocket.Options, this.Proxy); webSocket.Options.KeepAliveInterval = this.KeepAliveInterval; webSocket.Options.SetBuffer(this.ConnectionBufferSize, this.ConnectionBufferSize); webSocket.Options.SetRequestHeader(HybridConnectionConstants.Headers.RelayUserAgent, HybridConnectionConstants.ClientAgent); if (this.TokenProvider != null) { RelayEventSource.Log.GetTokenStart(traceSource); string audience = this.Address.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped); var token = await this.TokenProvider.GetTokenAsync(audience, TokenProvider.DefaultTokenTimeout).ConfigureAwait(false); RelayEventSource.Log.GetTokenStop(traceSource, token.ExpiresAtUtc); webSocket.Options.SetRequestHeader(RelayConstants.ServiceBusAuthorizationHeaderName, token.TokenString); } if (requestHeaders != null) { foreach (KeyValuePair <string, string> header in requestHeaders) { webSocket.Options.SetRequestHeader(header.Key, header.Value); } } // Build the websocket uri, e.g. "wss://contoso.servicebus.windows.net:443/$hc/endpoint1?sb-hc-action=connect&sb-hc-id=E2E_TRACKING_ID" Uri webSocketUri = HybridConnectionUtility.BuildUri( this.Address.Host, this.Address.Port, this.Address.AbsolutePath, this.Address.Query, HybridConnectionConstants.Actions.Connect, trackingContext.TrackingId); using (var cancelSource = new CancellationTokenSource(timeoutHelper.RemainingTime())) { await webSocket.ConnectAsync(webSocketUri, cancelSource.Token).ConfigureAwait(false); } RelayEventSource.Log.ObjectConnected(traceSource, trackingContext); return(new WebSocketStream(webSocket.WebSocket, trackingContext)); } catch (Exception exception) when(!WebSocketExceptionHelper.IsRelayContract(exception)) { throw RelayEventSource.Log.ThrowingException( WebSocketExceptionHelper.ConvertToRelayContract(exception, trackingContext, webSocket.Response, isListener: false), traceSource); } }
async Task <WebSocket> ConnectAsync(CancellationToken cancellationToken) { Fx.Assert(!this.closeCalled, "Shouldn't call ConnectWebSocketAsync if CloseAsync was called."); try { var connectDelay = ConnectDelayIntervals[this.connectDelayIndex]; if (connectDelay != TimeSpan.Zero) { await Task.Delay(connectDelay, cancellationToken).ConfigureAwait(false); } RelayEventSource.Log.RelayClientConnectStart(this.listener); var webSocket = new ClientWebSocket(); webSocket.Options.SetBuffer(this.bufferSize, this.bufferSize); webSocket.Options.Proxy = this.listener.Proxy; webSocket.Options.KeepAliveInterval = HybridConnectionConstants.KeepAliveInterval; webSocket.Options.SetRequestHeader("User-Agent", "ServiceBus/" + ClientAgentFileVersion); var token = await this.tokenRenewer.GetTokenAsync().ConfigureAwait(false); webSocket.Options.SetRequestHeader(RelayConstants.ServiceBusAuthorizationHeaderName, token.TokenString); // When we reconnect we need to remove the "_GXX" suffix otherwise trackingId gets longer after each reconnect string trackingId = TrackingContext.RemoveSuffix(this.listener.TrackingContext.TrackingId); // Build the websocket uri, e.g. "wss://contoso.servicebus.windows.net:443/$hc/endpoint1?sb-hc-action=listen&sb-hc-id=E2E_TRACKING_ID" var webSocketUri = HybridConnectionUtility.BuildUri( this.address.Host, this.address.Port, this.address.AbsolutePath, this.address.Query, HybridConnectionConstants.Actions.Listen, trackingId); await webSocket.ConnectAsync(webSocketUri, cancellationToken).ConfigureAwait(false); #if NET45 // TODO: Flow Response Headers in NETSTANDARD ClientWebSocket trackingId = webSocket.ResponseHeaders[TrackingContext.TrackingIdName]; if (!string.IsNullOrEmpty(trackingId)) { // Update to the flown trackingId (which has _GX suffix) this.listener.TrackingContext = TrackingContext.Create(trackingId, this.listener.TrackingContext.SubsystemId); } #endif this.OnOnline(); return(webSocket); } catch (WebSocketException wsException) { throw RelayEventSource.Log.ThrowingException(WebSocketExceptionHelper.ConvertToRelayContract(wsException), this.listener); } finally { RelayEventSource.Log.RelayClientConnectStop(); } }
/// <summary> /// Ensure we have a connected webSocket, listens for command messages, and handles those messages. /// </summary> /// <returns>A bool indicating whether or not the receive pump should keep running.</returns> async Task <bool> ReceivePumpCoreAsync() { bool keepGoing = true; CancellationToken shutdownToken = this.shutdownCancellationSource.Token; Task <WebSocket> connectTask = this.EnsureConnectTask(shutdownToken); try { WebSocket webSocket = await connectTask.ConfigureAwait(false); int totalBytesRead = 0; do { var currentReadBuffer = new ArraySegment <byte>(this.receiveBuffer.Array, this.receiveBuffer.Offset + totalBytesRead, this.receiveBuffer.Count - totalBytesRead); WebSocketReceiveResult receiveResult = await webSocket.ReceiveAsync(currentReadBuffer, CancellationToken.None).ConfigureAwait(false); if (receiveResult.MessageType == WebSocketMessageType.Close) { await this.CloseOrAbortWebSocketAsync( connectTask, false, receiveResult.CloseStatus.Value, receiveResult.CloseStatusDescription, shutdownToken).ConfigureAwait(false); if (this.listener.closeCalled) { // This is the cloud service responding to our clean shutdown. keepGoing = false; } else { keepGoing = this.OnDisconnect(new ConnectionLostException(receiveResult.CloseStatus.Value + ": " + receiveResult.CloseStatusDescription)); } break; } totalBytesRead += receiveResult.Count; if (receiveResult.EndOfMessage) { var commandBuffer = new ArraySegment <byte>(this.receiveBuffer.Array, this.receiveBuffer.Offset, totalBytesRead); await this.listener.OnCommandAsync(commandBuffer, webSocket).ConfigureAwait(false); totalBytesRead = 0; } }while (!shutdownToken.IsCancellationRequested); } catch (Exception exception) when(!Fx.IsFatal(exception)) { RelayEventSource.Log.HandledExceptionAsWarning(this.listener, exception); await this.CloseOrAbortWebSocketAsync(connectTask, abort : true).ConfigureAwait(false); keepGoing = this.OnDisconnect(WebSocketExceptionHelper.ConvertToRelayContract(exception, this.listener.TrackingContext)); } return(keepGoing); }
/// <summary> /// Ensure we have a connected webSocket, listens for command messages, and handles those messages. /// </summary> /// <returns>A bool indicating whether or not the receive pump should keep running.</returns> async Task <bool> ReceivePumpCoreAsync() { bool keepGoing = true; CancellationToken shutdownToken = this.shutdownCancellationSource.Token; Task <WebSocket> connectTask = this.EnsureConnectTask(shutdownToken); try { WebSocket webSocket = await connectTask.ConfigureAwait(false); do { WebSocketReceiveResult receiveResult = await webSocket.ReceiveAsync(this.receiveBuffer, CancellationToken.None).ConfigureAwait(false); if (receiveResult.MessageType == WebSocketMessageType.Close) { await this.CloseOrAbortWebSocketAsync( connectTask, false, receiveResult.CloseStatus.Value, receiveResult.CloseStatusDescription, shutdownToken).ConfigureAwait(false); if (this.closeCalled) { // This is the cloud service responding to our clean shutdown. keepGoing = false; } else { keepGoing = this.OnDisconnect(new ConnectionLostException(receiveResult.CloseStatus.Value + ": " + receiveResult.CloseStatusDescription)); } break; } Fx.Assert(receiveResult.Count > 0, "Expected a non-zero count of bytes received."); this.listener.OnCommandAsync(new ArraySegment <byte>(this.receiveBuffer.Array, this.receiveBuffer.Offset, receiveResult.Count)).Fork(this.listener); }while (!shutdownToken.IsCancellationRequested); } catch (Exception exception) { if (Fx.IsFatal(exception)) { throw; } RelayEventSource.Log.HandledExceptionAsWarning(this.listener, exception); await this.CloseOrAbortWebSocketAsync(connectTask, abort : true).ConfigureAwait(false); keepGoing = this.OnDisconnect(WebSocketExceptionHelper.ConvertToRelayContract(exception)); } return(keepGoing); }
async Task <WebSocket> ConnectAsync(CancellationToken cancellationToken) { this.listener.ThrowIfDisposed(); var webSocket = ClientWebSocketFactory.Create(this.listener.UseBuiltInClientWebSocket); try { var connectDelay = ConnectDelayIntervals[this.connectDelayIndex]; if (connectDelay != TimeSpan.Zero) { await Task.Delay(connectDelay, cancellationToken).ConfigureAwait(false); } RelayEventSource.Log.ObjectConnecting(this.listener); webSocket.Options.SetBuffer(this.bufferSize, this.bufferSize); DefaultWebProxy.ConfigureProxy(webSocket.Options, this.listener.Proxy); webSocket.Options.KeepAliveInterval = HybridConnectionConstants.KeepAliveInterval; webSocket.Options.SetRequestHeader(HybridConnectionConstants.Headers.RelayUserAgent, HybridConnectionConstants.ClientAgent); var token = await this.tokenRenewer.GetTokenAsync().ConfigureAwait(false); webSocket.Options.SetRequestHeader(RelayConstants.ServiceBusAuthorizationHeaderName, token.TokenString); // When we reconnect we need to remove the "_GXX" suffix otherwise trackingId gets longer after each reconnect string trackingId = TrackingContext.RemoveSuffix(this.listener.TrackingContext.TrackingId); // Build the websocket uri, e.g. "wss://contoso.servicebus.windows.net:443/$hc/endpoint1?sb-hc-action=listen&sb-hc-id=E2E_TRACKING_ID" var webSocketUri = HybridConnectionUtility.BuildUri( this.address.Host, this.address.Port, this.address.AbsolutePath, this.address.Query, HybridConnectionConstants.Actions.Listen, trackingId); await webSocket.ConnectAsync(webSocketUri, cancellationToken).ConfigureAwait(false); this.OnOnline(); RelayEventSource.Log.ObjectConnected(this.listener); return(webSocket.WebSocket); } catch (Exception exception) when(!WebSocketExceptionHelper.IsRelayContract(exception)) { throw RelayEventSource.Log.ThrowingException( WebSocketExceptionHelper.ConvertToRelayContract(exception, this.listener.TrackingContext, webSocket.Response), this.listener); } }
protected override async Task OnShutdownAsync(CancellationToken cancellationToken) { try { // Combine the provided CancellationToken with one using the WriteTimeout value using (var timeoutCancelSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(this.WriteTimeout))) using (var linkedCancelSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutCancelSource.Token, cancellationToken)) { await this.webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Shutdown", linkedCancelSource.Token).ConfigureAwait(false); } } catch (WebSocketException webSocketException) { throw RelayEventSource.Log.ThrowingException(WebSocketExceptionHelper.ConvertToRelayContract(webSocketException), this); } }
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { try { // Combine the provided CancellationToken with one using the WriteTimeout value using (var timeoutCancelSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(this.WriteTimeout))) using (var linkedCancelSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutCancelSource.Token, cancelToken)) { await this.webSocket.SendAsync( new ArraySegment <byte>(buffer, offset, count), WebSocketMessageType.Binary, true, linkedCancelSource.Token).ConfigureAwait(false); } } catch (WebSocketException webSocketException) { throw RelayEventSource.Log.ThrowingException(WebSocketExceptionHelper.ConvertToRelayContract(webSocketException), this); } }
public static async Task <TEntityDescription> GetAsync <TEntityDescription>( Uri resourceUri, TokenProvider tokenProvider, CancellationToken cancellationToken) { Fx.Assert(resourceUri != null, "resourceUri is required"); Fx.Assert(tokenProvider != null, "tokenProvider is required"); var httpClient = new HttpClient(); try { httpClient.BaseAddress = CreateManagementUri(resourceUri); httpClient.DefaultRequestHeaders.Add("X-PROCESS-AT", "ServiceBus"); var token = await tokenProvider.GetTokenAsync(resourceUri.AbsoluteUri, TokenDuration).ConfigureAwait(false); httpClient.DefaultRequestHeaders.Add("Authorization", token.TokenString); var httpResponse = await httpClient.GetAsync(string.Empty, cancellationToken).ConfigureAwait(false); if (httpResponse.IsSuccessStatusCode) { if (IsFeedContentType(httpResponse)) { // REST management operations will return an atom feed for unknown paths throw new EndpointNotFoundException(resourceUri.AbsolutePath.TrimStart('/')); } using (var stream = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false)) { return(DeserializeFromAtomEntry <TEntityDescription>(stream)); } } else { throw await CreateExceptionForFailedResponseAsync(httpResponse).ConfigureAwait(false); } } catch (Exception exception) when(!Fx.IsFatal(exception) && !(exception is RelayException)) { throw WebSocketExceptionHelper.ConvertToRelayContract(exception); } finally { httpClient.Dispose(); } }
public override async Task <int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { try { // Combine the provided CancellationToken with one using the ReadTimeout value using (var timeoutCancelSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(this.ReadTimeout))) using (var linkedCancelSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutCancelSource.Token, cancelToken)) { WebSocketReceiveResult result = await this.webSocket.ReceiveAsync( new ArraySegment <byte>(buffer, offset, count), linkedCancelSource.Token).ConfigureAwait(false); return(result.Count); } } catch (WebSocketException webSocketException) { throw RelayEventSource.Log.ThrowingException(WebSocketExceptionHelper.ConvertToRelayContract(webSocketException), this); } }