Example #1
0
        /// <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();
            }
        }
Example #2
0
        /// <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);
            }
        }
Example #3
0
            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();
                }
            }
Example #4
0
            /// <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);
            }
        }