public async Task CreateFromEnvironmentTest( Option <UpstreamProtocol> upstreamProtocol, Option <IWebProxy> webProxy, string productInfo) { // Arrange ITransportSettings receivedTransportSettings = null; var sdkModuleClient = new Mock <ISdkModuleClient>(); var sdkModuleClientProvider = new Mock <ISdkModuleClientProvider>(); sdkModuleClientProvider.Setup(s => s.GetSdkModuleClient(It.IsAny <ITransportSettings>())) .Callback <ITransportSettings>(t => receivedTransportSettings = t) .ReturnsAsync(sdkModuleClient.Object); bool closeOnIdleTimeout = false; TimeSpan idleTimeout = TimeSpan.FromMinutes(5); ConnectionStatusChangesHandler handler = (status, reason) => { }; // Act var moduleClientProvider = new ModuleClientProvider( sdkModuleClientProvider.Object, upstreamProtocol, webProxy, productInfo, closeOnIdleTimeout, idleTimeout, false); IModuleClient moduleClient = await moduleClientProvider.Create(handler); // Assert Assert.NotNull(moduleClient); sdkModuleClientProvider.Verify(s => s.GetSdkModuleClient(It.IsAny <ITransportSettings>()), Times.Once); sdkModuleClient.Verify(s => s.SetProductInfo(productInfo), Times.Once); Assert.NotNull(receivedTransportSettings); UpstreamProtocol up = upstreamProtocol.GetOrElse(UpstreamProtocol.Amqp); Assert.Equal(up, moduleClient.UpstreamProtocol); switch (up) { case UpstreamProtocol.Amqp: case UpstreamProtocol.AmqpWs: var amqpTransportSettings = receivedTransportSettings as AmqpTransportSettings; Assert.NotNull(amqpTransportSettings); webProxy.ForEach(w => Assert.Equal(w, amqpTransportSettings.Proxy)); break; case UpstreamProtocol.Mqtt: case UpstreamProtocol.MqttWs: var mqttTransportSettings = receivedTransportSettings as MqttTransportSettings; Assert.NotNull(mqttTransportSettings); webProxy.ForEach(w => Assert.Equal(w, mqttTransportSettings.Proxy)); break; } sdkModuleClient.Verify(s => s.OpenAsync(), Times.Once); }
static ITransportSettings GetTransportSettings(UpstreamProtocol protocol, Option <IWebProxy> proxy, bool useServerHeartbeat) { switch (protocol) { case UpstreamProtocol.Amqp: { var settings = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only); if (useServerHeartbeat) { settings.IdleTimeout = HeartbeatTimeout; } return(settings); } case UpstreamProtocol.AmqpWs: { var settings = new AmqpTransportSettings(TransportType.Amqp_WebSocket_Only); if (useServerHeartbeat) { settings.IdleTimeout = HeartbeatTimeout; } // Only WebSocket protocols can use an HTTP forward proxy proxy.ForEach(p => settings.Proxy = p); return(settings); } case UpstreamProtocol.Mqtt: { var settings = new MqttTransportSettings(TransportType.Mqtt_Tcp_Only); return(settings); } case UpstreamProtocol.MqttWs: { var settings = new MqttTransportSettings(TransportType.Mqtt_WebSocket_Only); // Only WebSocket protocols can use an HTTP forward proxy proxy.ForEach(p => settings.Proxy = p); return(settings); } default: { throw new InvalidEnumArgumentException(); } } }
public async Task CreateForUpstreamProtocolTest(UpstreamProtocol upstreamProtocol, Client.TransportType expectedTransportType) { // Arrange var receivedTransportType = Client.TransportType.Http1; Task <Client.ModuleClient> ModuleClientCreator(Client.TransportType transportType) { receivedTransportType = transportType; return(Task.FromResult((Client.ModuleClient)null)); } // Act await ModuleClient.CreateDeviceClientForUpstreamProtocol(Option.Some(upstreamProtocol), ModuleClientCreator); // Assert Assert.Equal(expectedTransportType, receivedTransportType); }
public async Task CreateForUpstreamProtocolTest(UpstreamProtocol upstreamProtocol) { // Arrange Option <UpstreamProtocol> receivedProtocol = Option.None <UpstreamProtocol>(); Task <Client.ModuleClient> ModuleClientCreator(UpstreamProtocol up) { receivedProtocol = Option.Some(up); return(Task.FromResult((Client.ModuleClient)null)); } // Act await ModuleClient.CreateDeviceClientForUpstreamProtocol(Option.Some(upstreamProtocol), ModuleClientCreator); // Assert Assert.Equal(Option.Some(upstreamProtocol), receivedProtocol); }
static async Task <Client.ModuleClient> CreateAndOpenDeviceClient( UpstreamProtocol upstreamProtocol, Option <string> connectionString, ConnectionStatusChangesHandler statusChangedHandler, Option <IWebProxy> proxy, Option <string> productInfo) { ITransportSettings settings = GetTransportSettings(upstreamProtocol, proxy); Events.AttemptingConnectionWithTransport(settings.GetTransportType()); Client.ModuleClient deviceClient = await connectionString .Map(cs => Task.FromResult(Client.ModuleClient.CreateFromConnectionString(cs, new[] { settings }))) .GetOrElse(() => Client.ModuleClient.CreateFromEnvironmentAsync(new[] { settings })); productInfo.ForEach(p => deviceClient.ProductInfo = p); await OpenAsync(statusChangedHandler, deviceClient); Events.ConnectedWithTransport(settings.GetTransportType()); return(deviceClient); }
async void OnConnectionStatusChanged(ConnectionStatus status, ConnectionStatusChangeReason reason) { try { UpstreamProtocol protocol = this.ModuleConnection.GetModuleClient().Map(x => x.UpstreamProtocol).GetOrElse(UpstreamProtocol.Amqp); Events.ConnectionStatusChanged(status, reason); // We want to notify the IoT Edge daemon in the following two cases - // 1. The device has been deprovisioned and might have been reprovisioned on another IoT hub. // 2. The IoT Hub that the device belongs to is no longer in existence and the device might have been // moved to a different IoT hub. // // When the Amqp or AmqpWs protocol is used, the SDK returns a connection status change reason of // Device_Disabled when a device is either disabled or deleted in IoT hub. // For the Mqtt and MqttWs protocol however, the SDK returns a Bad_Credential status as it's not // possible for IoT hub to distinguish between 'device does not exist', 'device is disabled' and // 'device exists but wrong credentials were supplied' cases. // // When an IoT hub is no longer in existence (i.e., it has been deleted), the SDK returns the // connection status change reason of Bad_Credential for all the Amqp and Mqtt protocols. if ((reason == ConnectionStatusChangeReason.Device_Disabled && (protocol == UpstreamProtocol.Amqp || protocol == UpstreamProtocol.AmqpWs)) || reason == ConnectionStatusChangeReason.Bad_Credential) { await this.deviceManager.ReprovisionDeviceAsync(); } if (this.pullOnReconnect && this.initTask.IsCompleted && status == ConnectionStatus.Connected) { using (await this.twinLock.LockAsync()) { await this.RefreshTwinAsync(); } } } catch (Exception ex) when(!ex.IsFatal()) { Events.ConnectionStatusChangedHandlingError(ex); } }
async Task <ISdkModuleClient> CreateAndOpenSdkModuleClient(UpstreamProtocol upstreamProtocol, ConnectionStatusChangesHandler statusChangedHandler) { ITransportSettings settings = GetTransportSettings(upstreamProtocol, this.proxy); Events.AttemptingConnectionWithTransport(settings.GetTransportType()); ISdkModuleClient moduleClient = await this.connectionString .Map(cs => Task.FromResult(this.sdkModuleClientProvider.GetSdkModuleClient(cs, settings))) .GetOrElse(this.sdkModuleClientProvider.GetSdkModuleClient(settings)); moduleClient.SetProductInfo(this.productInfo); // note: it's important to set the status-changed handler and // timeout value *before* we open a connection to the hub moduleClient.SetOperationTimeoutInMilliseconds(ModuleClientTimeoutMilliseconds); moduleClient.SetConnectionStatusChangesHandler(statusChangedHandler); await moduleClient.OpenAsync(); Events.ConnectedWithTransport(settings.GetTransportType()); return(moduleClient); }
static ITransportSettings GetTransportSettings(UpstreamProtocol protocol, Option <IWebProxy> proxy) { switch (protocol) { case UpstreamProtocol.Amqp: { var settings = new AmqpTransportSettings(TransportType.Amqp_Tcp_Only); proxy.ForEach(p => settings.Proxy = p); return(settings); } case UpstreamProtocol.AmqpWs: { var settings = new AmqpTransportSettings(TransportType.Amqp_WebSocket_Only); proxy.ForEach(p => settings.Proxy = p); return(settings); } case UpstreamProtocol.Mqtt: { var settings = new MqttTransportSettings(TransportType.Mqtt_Tcp_Only); proxy.ForEach(p => settings.Proxy = p); return(settings); } case UpstreamProtocol.MqttWs: { var settings = new MqttTransportSettings(TransportType.Mqtt_WebSocket_Only); proxy.ForEach(p => settings.Proxy = p); return(settings); } default: { throw new InvalidEnumArgumentException(); } } }
async void OnConnectionStatusChanged(ConnectionStatus status, ConnectionStatusChangeReason reason) { try { UpstreamProtocol protocol = this.ModuleConnection.GetModuleClient().Map(x => x.UpstreamProtocol).GetOrElse(UpstreamProtocol.Amqp); Events.ConnectionStatusChanged(status, reason); // Notify the IoT Edge daemon that a device has been deprovisioned and it should check // if the device has been provisioned to a different IoT hub instead. // When the Amqp or AmqpWs protocol is used, the SDK returns a connection status change reason of // Device_Disabled when a device is either disabled or deleted in IoT hub. // For the Mqtt and MqttWs protocol however, the SDK returns a Bad_Credential status as it's not // possible for IoT hub to distinguish between 'device does not exist', 'device is disabled' and // 'device exists but wrong credentials were supplied' cases. if ((reason == ConnectionStatusChangeReason.Device_Disabled && (protocol == UpstreamProtocol.Amqp || protocol == UpstreamProtocol.AmqpWs)) || (reason == ConnectionStatusChangeReason.Bad_Credential && (protocol == UpstreamProtocol.Mqtt || protocol == UpstreamProtocol.MqttWs))) { await this.deviceManager.ReprovisionDeviceAsync(); } if (this.pullOnReconnect && this.initTask.IsCompleted && status == ConnectionStatus.Connected) { using (await this.twinLock.LockAsync()) { await this.RefreshTwinAsync(); } } } catch (Exception ex) when(!ex.IsFatal()) { Events.ConnectionStatusChangedHandlingError(ex); } }
public ModuleClient(ISdkModuleClient inner, TimeSpan idleTimeout, bool closeOnIdleTimeout, UpstreamProtocol protocol) { this.inner = Preconditions.CheckNotNull(inner, nameof(inner)); this.UpstreamProtocol = protocol; this.inactivityTimer = new ResettableTimer(this.CloseOnInactivity, idleTimeout, Events.Log, closeOnIdleTimeout); this.inactivityTimer.Start(); }
public async Task CreateFromEnvironmentTest( Option <UpstreamProtocol> upstreamProtocol, Option <IWebProxy> webProxy, string productInfo) { // Arrange ITransportSettings receivedTransportSettings = null; var sdkModuleClient = new Mock <ISdkModuleClient>(); var runtimeInfoProvider = new Mock <IRuntimeInfoProvider>(); var systemInfo = new SystemInfo("foo", "bar", "baz"); runtimeInfoProvider.Setup(p => p.GetSystemInfo(CancellationToken.None)) .ReturnsAsync(systemInfo); var sdkModuleClientProvider = new Mock <ISdkModuleClientProvider>(); sdkModuleClientProvider.Setup(s => s.GetSdkModuleClient(It.IsAny <ITransportSettings>())) .Callback <ITransportSettings>(t => receivedTransportSettings = t) .ReturnsAsync(sdkModuleClient.Object); bool closeOnIdleTimeout = false; TimeSpan idleTimeout = TimeSpan.FromMinutes(5); ConnectionStatusChangesHandler handler = (status, reason) => { }; // Act var moduleClientProvider = new ModuleClientProvider( sdkModuleClientProvider.Object, Option.Some(Task.FromResult(runtimeInfoProvider.Object)), upstreamProtocol, webProxy, productInfo, closeOnIdleTimeout, idleTimeout, false); IModuleClient moduleClient = await moduleClientProvider.Create(handler); // Assert Assert.NotNull(moduleClient); sdkModuleClientProvider.Verify(s => s.GetSdkModuleClient(It.IsAny <ITransportSettings>()), Times.Once); // Write product info explicitly sdkModuleClient.Verify(s => s.SetProductInfo($"{productInfo} (kernel_name=foo;cpu_architecture=bar;)"), Times.Once); Assert.NotNull(receivedTransportSettings); UpstreamProtocol up = upstreamProtocol.GetOrElse(UpstreamProtocol.Amqp); Assert.Equal(up, moduleClient.UpstreamProtocol); switch (up) { case UpstreamProtocol.Amqp: case UpstreamProtocol.AmqpWs: var amqpTransportSettings = receivedTransportSettings as AmqpTransportSettings; Assert.NotNull(amqpTransportSettings); if (up == UpstreamProtocol.AmqpWs) { webProxy.ForEach(w => Assert.Equal(w, amqpTransportSettings.Proxy)); } break; case UpstreamProtocol.Mqtt: case UpstreamProtocol.MqttWs: var mqttTransportSettings = receivedTransportSettings as MqttTransportSettings; Assert.NotNull(mqttTransportSettings); if (up == UpstreamProtocol.MqttWs) { webProxy.ForEach(w => Assert.Equal(w, mqttTransportSettings.Proxy)); } break; } sdkModuleClient.Verify(s => s.OpenAsync(), Times.Once); }