public ConnectivityChecker(ICloudProxy client) { this.client = Preconditions.CheckNotNull(client, nameof(client)); }
public void SetTestCloudProxy(ICloudProxy testClient) { this.connectivityChecker = new ConnectivityChecker(testClient); this.connectedTimer.Start(); Events.SetTestCloudProxy(); }
public async Task ProcessInBatchesWithBatchSizeTest() { // Arrange string device1 = "d1"; string device2 = "d2"; string device3 = "d3"; IList <IRoutingMessage> device1Messages = GetMessages(device1, 45); IList <IRoutingMessage> device2Messages = GetMessages(device2, 25); IList <IRoutingMessage> device3Messages = GetMessages(device3, 30); IList <IRoutingMessage> messagesToProcess = device1Messages .Concat(device2Messages) .Concat(device3Messages) .ToList(); Mock <ICloudProxy> InitCloudProxy(List <int> receivedMsgCountList) { var cp = new Mock <ICloudProxy>(); cp.Setup(c => c.SendMessageBatchAsync(It.IsAny <IEnumerable <IMessage> >())) .Callback <IEnumerable <IMessage> >(b => receivedMsgCountList.Add(b.Count())) .Returns(Task.CompletedTask); cp.SetupGet(p => p.IsActive).Returns(true); return(cp); } var device1CloudReceivedMessagesCountList = new List <int>(); Mock <ICloudProxy> device1CloudProxy = InitCloudProxy(device1CloudReceivedMessagesCountList); var device2CloudReceivedMessagesCountList = new List <int>(); Mock <ICloudProxy> device2CloudProxy = InitCloudProxy(device2CloudReceivedMessagesCountList); var device3CloudReceivedMessagesCountList = new List <int>(); Mock <ICloudProxy> device3CloudProxy = InitCloudProxy(device3CloudReceivedMessagesCountList); Task <Option <ICloudProxy> > GetCloudProxy(string id) { ICloudProxy cp = null; if (id == device1) { cp = device1CloudProxy.Object; } else if (id == device2) { cp = device2CloudProxy.Object; } else if (id == device3) { cp = device3CloudProxy.Object; } return(Task.FromResult(Option.Maybe(cp))); } Core.IMessageConverter <IRoutingMessage> routingMessageConverter = new RoutingMessageConverter(); string cloudEndpointId = Guid.NewGuid().ToString(); var cloudEndpoint = new CloudEndpoint(cloudEndpointId, GetCloudProxy, routingMessageConverter, 30); // Act IProcessor cloudMessageProcessor = cloudEndpoint.CreateProcessor(); ISinkResult <IRoutingMessage> sinkResult = await cloudMessageProcessor.ProcessAsync(messagesToProcess, CancellationToken.None); // Assert Assert.Equal(messagesToProcess, sinkResult.Succeeded); Assert.Equal(device1CloudReceivedMessagesCountList, new[] { 30, 15 }); Assert.Equal(device2CloudReceivedMessagesCountList, new[] { 25 }); Assert.Equal(device3CloudReceivedMessagesCountList, new[] { 30 }); }
public async Task TestSendMessages() { // Arrange const string Id = "id1"; var identity = Mock.Of <IIdentity>(i => i.Id == Id); var twinMessageConverter = new TwinMessageConverter(); var twinCollectionMessageConverter = new TwinCollectionMessageConverter(); var messageConverterProvider = new MessageConverterProvider( new Dictionary <Type, IMessageConverter>() { { typeof(Message), new DeviceClientMessageConverter() }, { typeof(Shared.Twin), twinMessageConverter }, { typeof(TwinCollection), twinCollectionMessageConverter } }); var edgeHubTokenProvider = new Mock <ITokenProvider>(); var clientWatcher = new ClientWatcher(); var clientProvider = new Mock <IClientProvider>(); clientProvider.Setup(c => c.Create(identity, edgeHubTokenProvider.Object, It.IsAny <ITransportSettings[]>())) .Returns(() => new ThrowingClient(clientWatcher, 3)); var deviceScopeIdentitiesCache = new Mock <IDeviceScopeIdentitiesCache>(); deviceScopeIdentitiesCache.Setup(d => d.GetServiceIdentity(Id, false)) .ReturnsAsync( Option.Some( new ServiceIdentity( Id, "dummy", new List <string>(), new ServiceAuthentication(new SymmetricKeyAuthentication("foo", "bar")), ServiceIdentityStatus.Enabled))); var edgeHubIdentity = Mock.Of <IIdentity>(); var productInfoStore = new Mock <IProductInfoStore>(); productInfoStore.Setup(p => p.GetEdgeProductInfo(Id)) .ReturnsAsync("ProdInfo1"); var identityProvider = new Mock <IIdentityProvider>(); identityProvider.Setup(i => i.Create(Id)).Returns(identity); var credentialsCache = new Mock <ICredentialsCache>(); var edgeHub = new Mock <IEdgeHub>(); var connectionProvider = new CloudConnectionProvider( messageConverterProvider, 1, clientProvider.Object, Option.None <UpstreamProtocol>(), edgeHubTokenProvider.Object, deviceScopeIdentitiesCache.Object, credentialsCache.Object, edgeHubIdentity, TimeSpan.FromMinutes(10), false, TimeSpan.FromMinutes(10), Option.None <IWebProxy>(), productInfoStore.Object); connectionProvider.BindEdgeHub(edgeHub.Object); var connectionManager = new ConnectionManager(connectionProvider, credentialsCache.Object, identityProvider.Object); var messagesToSend = new List <IMessage>(); for (int i = 0; i < 10; i++) { var message = new EdgeMessage.Builder(new byte[i]) .SetSystemProperties( new Dictionary <string, string>() { [SystemProperties.MessageId] = i.ToString() }) .Build(); messagesToSend.Add(message); } // Act Option <ICloudProxy> cloudProxyOption = await connectionManager.GetCloudConnection(Id); // Assert Assert.True(cloudProxyOption.HasValue); ICloudProxy cloudProxy = cloudProxyOption.OrDefault(); Assert.True(cloudProxy.IsActive); // Act await RunSendMessages(cloudProxy, messagesToSend); // Assert Assert.Equal(messagesToSend.Count, clientWatcher.ReceivedMessages.Count()); Assert.Equal(5, clientWatcher.OpenAsyncCount); Assert.True(cloudProxy.IsActive); IEnumerable <string> expectedMessageIds = messagesToSend.Select(m => m.SystemProperties[SystemProperties.MessageId]); IEnumerable <string> receivedMessageIds = clientWatcher.ReceivedMessages.Select(m => m.MessageId); Assert.Equal(expectedMessageIds, receivedMessageIds); }
public async Task RefreshTokenWithRetryTest() { string iothubHostName = "test.azure-devices.net"; string deviceId = "device1"; ITokenCredentials GetClientCredentialsWithExpiringToken() { string token = TokenHelper.CreateSasToken(iothubHostName, DateTime.UtcNow.AddMinutes(3)); var identity = new DeviceIdentity(iothubHostName, deviceId); return(new TokenCredentials(identity, token, string.Empty, false)); } ITokenCredentials GetClientCredentialsWithNonExpiringToken() { string token = TokenHelper.CreateSasToken(iothubHostName, DateTime.UtcNow.AddMinutes(10)); var identity = new DeviceIdentity(iothubHostName, deviceId); return(new TokenCredentials(identity, token, string.Empty, false)); } ITokenProvider tokenProvider = null; IClientProvider clientProvider = GetMockDeviceClientProviderWithToken((s, a, t, m) => tokenProvider = a); var transportSettings = new ITransportSettings[] { new AmqpTransportSettings(TransportType.Amqp_Tcp_Only) }; var receivedStatuses = new List <CloudConnectionStatus>(); void ConnectionStatusHandler(string id, CloudConnectionStatus status) => receivedStatuses.Add(status); var messageConverterProvider = new MessageConverterProvider(new Dictionary <Type, IMessageConverter> { [typeof(TwinCollection)] = Mock.Of <IMessageConverter>() }); ITokenCredentials clientCredentialsWithExpiringToken1 = GetClientCredentialsWithExpiringToken(); ClientTokenCloudConnection cloudConnection = await ClientTokenCloudConnection.Create( clientCredentialsWithExpiringToken1, ConnectionStatusHandler, transportSettings, messageConverterProvider, clientProvider, Mock.Of <ICloudListener>(), TimeSpan.FromMinutes(60), true, TimeSpan.FromSeconds(20), DummyProductInfo, Option.None <string>()); Option <ICloudProxy> cloudProxy1 = cloudConnection.CloudProxy; Assert.True(cloudProxy1.HasValue); Assert.True(cloudProxy1.OrDefault().IsActive); Assert.NotNull(tokenProvider); // Try to refresh token but get an expiring token Task <string> getTokenTask = tokenProvider.GetTokenAsync(Option.None <TimeSpan>()); Assert.False(getTokenTask.IsCompleted); Assert.Single(receivedStatuses); Assert.Equal(CloudConnectionStatus.TokenNearExpiry, receivedStatuses[0]); ICloudProxy cloudProxy2 = await cloudConnection.UpdateTokenAsync(clientCredentialsWithExpiringToken1); // Wait for the task to process await Task.Delay(TimeSpan.FromSeconds(5)); Assert.False(getTokenTask.IsCompletedSuccessfully); Assert.Equal(cloudProxy2, cloudConnection.CloudProxy.OrDefault()); Assert.True(cloudProxy2.IsActive); Assert.True(cloudProxy1.OrDefault().IsActive); Assert.Equal(cloudProxy1.OrDefault(), cloudProxy2); // Wait for 20 secs for retry to happen await Task.Delay(TimeSpan.FromSeconds(20)); // Check if retry happened Assert.Equal(2, receivedStatuses.Count); Assert.Equal(CloudConnectionStatus.TokenNearExpiry, receivedStatuses[1]); ITokenCredentials clientCredentialsWithNonExpiringToken = GetClientCredentialsWithNonExpiringToken(); ICloudProxy cloudProxy3 = await cloudConnection.UpdateTokenAsync(clientCredentialsWithNonExpiringToken); // Wait for the task to complete await Task.Delay(TimeSpan.FromSeconds(5)); Assert.True(getTokenTask.IsCompletedSuccessfully); Assert.Equal(cloudProxy3, cloudConnection.CloudProxy.OrDefault()); Assert.True(cloudProxy3.IsActive); Assert.True(cloudProxy1.OrDefault().IsActive); Assert.Equal(cloudProxy1.OrDefault(), cloudProxy3); Assert.Equal(getTokenTask.Result, clientCredentialsWithNonExpiringToken.Token); }
public async Task CloudConnectionCallbackTest() { int receivedConnectedStatusCount = 0; ConnectionStatusChangesHandler connectionStatusChangesHandler = (_, __) => { }; IClient GetMockedDeviceClient() { var deviceClient = new Mock <IClient>(); deviceClient.SetupGet(dc => dc.IsActive).Returns(true); deviceClient.Setup(dc => dc.CloseAsync()) .Callback(() => deviceClient.SetupGet(dc => dc.IsActive).Returns(false)) .Returns(Task.FromResult(true)); deviceClient.Setup(dc => dc.SetConnectionStatusChangedHandler(It.IsAny <ConnectionStatusChangesHandler>())) .Callback <ConnectionStatusChangesHandler>(c => connectionStatusChangesHandler = c); deviceClient.Setup(dc => dc.OpenAsync()) .Callback( () => { Assert.NotNull(connectionStatusChangesHandler); connectionStatusChangesHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); }) .Returns(Task.CompletedTask); return(deviceClient.Object); } var deviceClientProvider = new Mock <IClientProvider>(); deviceClientProvider.Setup(dc => dc.Create(It.IsAny <IIdentity>(), It.IsAny <ITokenProvider>(), It.IsAny <ITransportSettings[]>(), Option.None <string>())) .Returns(() => GetMockedDeviceClient()); var transportSettings = new ITransportSettings[] { new AmqpTransportSettings(TransportType.Amqp_Tcp_Only) }; void ConnectionStatusHandler(string id, CloudConnectionStatus status) { if (status == CloudConnectionStatus.ConnectionEstablished) { receivedConnectedStatusCount++; } } var messageConverterProvider = new MessageConverterProvider(new Dictionary <Type, IMessageConverter> { [typeof(TwinCollection)] = Mock.Of <IMessageConverter>() }); ITokenCredentials clientCredentialsWithExpiringToken1 = GetMockClientCredentialsWithToken(); ClientTokenCloudConnection cloudConnection = await ClientTokenCloudConnection.Create( clientCredentialsWithExpiringToken1, ConnectionStatusHandler, transportSettings, messageConverterProvider, deviceClientProvider.Object, Mock.Of <ICloudListener>(), TimeSpan.FromMinutes(60), true, TimeSpan.FromSeconds(20), DummyProductInfo, Option.None <string>()); Assert.Equal(1, receivedConnectedStatusCount); Option <ICloudProxy> cloudProxy1 = cloudConnection.CloudProxy; Assert.True(cloudProxy1.HasValue); Assert.True(cloudProxy1.OrDefault().IsActive); Assert.NotNull(connectionStatusChangesHandler); connectionStatusChangesHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); Assert.Equal(2, receivedConnectedStatusCount); ITokenCredentials clientCredentialsWithExpiringToken2 = GetMockClientCredentialsWithToken(); ICloudProxy cloudProxy2 = await cloudConnection.UpdateTokenAsync(clientCredentialsWithExpiringToken2); Assert.True(cloudProxy2.IsActive); Assert.Equal(cloudProxy2, cloudConnection.CloudProxy.OrDefault()); Assert.Equal(2, receivedConnectedStatusCount); connectionStatusChangesHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); Assert.Equal(3, receivedConnectedStatusCount); }
public async Task CloudConnectionCallbackTest() { int receivedConnectedStatusCount = 0; ConnectionStatusChangesHandler connectionStatusChangesHandler = (_, __) => { }; IClient GetMockedDeviceClient() { var deviceClient = new Mock <IClient>(); deviceClient.SetupGet(dc => dc.IsActive).Returns(true); deviceClient.Setup(dc => dc.CloseAsync()) .Callback(() => deviceClient.SetupGet(dc => dc.IsActive).Returns(false)) .Returns(Task.FromResult(true)); deviceClient.Setup(dc => dc.SetConnectionStatusChangedHandler(It.IsAny <ConnectionStatusChangesHandler>())) .Callback <ConnectionStatusChangesHandler>(c => connectionStatusChangesHandler = c); deviceClient.Setup(dc => dc.OpenAsync()) .Callback(() => { int currentCount = receivedConnectedStatusCount; Assert.NotNull(connectionStatusChangesHandler); connectionStatusChangesHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); Assert.Equal(receivedConnectedStatusCount, currentCount); }) .Returns(Task.CompletedTask); return(deviceClient.Object); } var deviceClientProvider = new Mock <IClientProvider>(); deviceClientProvider.Setup(dc => dc.Create(It.IsAny <IIdentity>(), It.IsAny <IAuthenticationMethod>(), It.IsAny <ITransportSettings[]>())) .Returns(() => GetMockedDeviceClient()); var transportSettings = new ITransportSettings[] { new AmqpTransportSettings(TransportType.Amqp_Tcp_Only) }; void ConnectionStatusHandler(string id, CloudConnectionStatus status) { if (status == CloudConnectionStatus.ConnectionEstablished) { receivedConnectedStatusCount++; } } var messageConverterProvider = new MessageConverterProvider(new Dictionary <Type, IMessageConverter>()); var cloudConnection = new CloudConnection(ConnectionStatusHandler, transportSettings, messageConverterProvider, deviceClientProvider.Object); IClientCredentials clientCredentialsWithExpiringToken1 = GetMockClientCredentialsWithToken(); Assert.Equal(receivedConnectedStatusCount, 0); ICloudProxy cloudProxy1 = await cloudConnection.CreateOrUpdateAsync(clientCredentialsWithExpiringToken1); Assert.True(cloudProxy1.IsActive); Assert.Equal(cloudProxy1, cloudConnection.CloudProxy.OrDefault()); Assert.Equal(receivedConnectedStatusCount, 1); Assert.NotNull(connectionStatusChangesHandler); connectionStatusChangesHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); Assert.Equal(receivedConnectedStatusCount, 2); IClientCredentials clientCredentialsWithExpiringToken2 = GetMockClientCredentialsWithToken(); ICloudProxy cloudProxy2 = await cloudConnection.CreateOrUpdateAsync(clientCredentialsWithExpiringToken2); Assert.True(cloudProxy2.IsActive); Assert.Equal(cloudProxy2, cloudConnection.CloudProxy.OrDefault()); Assert.Equal(receivedConnectedStatusCount, 3); connectionStatusChangesHandler.Invoke(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); Assert.Equal(receivedConnectedStatusCount, 4); }
public async Task TestGetTwin() { // Arrange const string Id = "id1"; var identity = Mock.Of <IIdentity>(i => i.Id == Id); var twinMessageConverter = new TwinMessageConverter(); var twinCollectionMessageConverter = new TwinCollectionMessageConverter(); var messageConverterProvider = new MessageConverterProvider( new Dictionary <Type, IMessageConverter>() { { typeof(Message), new DeviceClientMessageConverter() }, { typeof(Shared.Twin), twinMessageConverter }, { typeof(TwinCollection), twinCollectionMessageConverter } }); var edgeHubTokenProvider = new Mock <ITokenProvider>(); var clientWatcher = new ClientWatcher(); var clientProvider = new Mock <IClientProvider>(); clientProvider.Setup(c => c.Create(identity, edgeHubTokenProvider.Object, It.IsAny <ITransportSettings[]>())) .Returns(() => new ThrowingClient(clientWatcher, 3)); var deviceScopeIdentitiesCache = new Mock <IDeviceScopeIdentitiesCache>(); deviceScopeIdentitiesCache.Setup(d => d.GetServiceIdentity(Id, false)) .ReturnsAsync( Option.Some( new ServiceIdentity( Id, "dummy", new List <string>(), new ServiceAuthentication(new SymmetricKeyAuthentication("foo", "bar")), ServiceIdentityStatus.Enabled))); var edgeHubIdentity = Mock.Of <IIdentity>(); var productInfoStore = new Mock <IProductInfoStore>(); productInfoStore.Setup(p => p.GetEdgeProductInfo(Id)) .ReturnsAsync("ProdInfo1"); var identityProvider = new Mock <IIdentityProvider>(); identityProvider.Setup(i => i.Create(Id)).Returns(identity); var credentialsCache = new Mock <ICredentialsCache>(); var edgeHub = new Mock <IEdgeHub>(); var connectionProvider = new CloudConnectionProvider( messageConverterProvider, 1, clientProvider.Object, Option.None <UpstreamProtocol>(), edgeHubTokenProvider.Object, deviceScopeIdentitiesCache.Object, credentialsCache.Object, edgeHubIdentity, TimeSpan.FromMinutes(10), false, TimeSpan.FromMinutes(10), false, Option.None <IWebProxy>(), productInfoStore.Object); connectionProvider.BindEdgeHub(edgeHub.Object); var deviceConnectivityManager = Mock.Of <IDeviceConnectivityManager>(); var connectionManager = new ConnectionManager(connectionProvider, credentialsCache.Object, identityProvider.Object, deviceConnectivityManager); // Act Option <ICloudProxy> cloudProxyOption = await connectionManager.GetCloudConnection(Id); // Assert Assert.True(cloudProxyOption.HasValue); ICloudProxy cloudProxy = cloudProxyOption.OrDefault(); Assert.True(cloudProxy.IsActive); // Act await RunGetTwin(cloudProxy, 10); // Assert Assert.Equal(5, clientWatcher.OpenAsyncCount); Assert.True(cloudProxy.IsActive); Assert.Equal(10, clientWatcher.GetTwinCount); }
protected override void Load(ContainerBuilder builder) { // IMessageConverter<IRoutingMessage> builder.Register(c => new RoutingMessageConverter()) .As <Core.IMessageConverter <IRoutingMessage> >() .SingleInstance(); // IRoutingPerfCounter builder.Register( c => { Routing.PerfCounter = NullRoutingPerfCounter.Instance; return(Routing.PerfCounter); }) .As <IRoutingPerfCounter>() .AutoActivate() .SingleInstance(); // IRoutingUserAnalyticsLogger builder.Register( c => { Routing.UserAnalyticsLogger = NullUserAnalyticsLogger.Instance; return(Routing.UserAnalyticsLogger); }) .As <IRoutingUserAnalyticsLogger>() .AutoActivate() .SingleInstance(); // IRoutingUserMetricLogger builder.Register( c => { Routing.UserMetricLogger = NullRoutingUserMetricLogger.Instance; return(Routing.UserMetricLogger); }) .As <IRoutingUserMetricLogger>() .AutoActivate() .SingleInstance(); // IMessageConverter<Message> builder.Register(c => new DeviceClientMessageConverter()) .As <Core.IMessageConverter <Message> >() .SingleInstance(); // IMessageConverter<Twin> builder.Register(c => new TwinMessageConverter()) .As <Core.IMessageConverter <Twin> >() .SingleInstance(); // IMessageConverter<TwinCollection> builder.Register(c => new TwinCollectionMessageConverter()) .As <Core.IMessageConverter <TwinCollection> >() .SingleInstance(); // IMessageConverterProvider builder.Register( c => new MessageConverterProvider(new Dictionary <Type, IMessageConverter>() { { typeof(Message), c.Resolve <Core.IMessageConverter <Message> >() }, { typeof(Twin), c.Resolve <Core.IMessageConverter <Twin> >() }, { typeof(TwinCollection), c.Resolve <Core.IMessageConverter <TwinCollection> >() } })) .As <Core.IMessageConverterProvider>() .SingleInstance(); // IDeviceConnectivityManager builder.Register( c => { IDeviceConnectivityManager deviceConnectivityManager = new DeviceConnectivityManager(this.connectivityCheckFrequency, TimeSpan.FromMinutes(2)); return(deviceConnectivityManager); }) .As <IDeviceConnectivityManager>() .SingleInstance(); // IDeviceClientProvider builder.Register(c => { IClientProvider underlyingClientProvider = new ClientProvider(); IClientProvider connectivityAwareClientProvider = new ConnectivityAwareClientProvider(underlyingClientProvider, c.Resolve <IDeviceConnectivityManager>()); return(connectivityAwareClientProvider); }) .As <IClientProvider>() .SingleInstance(); // ICloudConnectionProvider builder.Register(c => new CloudConnectionProvider(c.Resolve <Core.IMessageConverterProvider>(), this.connectionPoolSize, c.Resolve <IClientProvider>(), this.upstreamProtocol)) .As <ICloudConnectionProvider>() .SingleInstance(); if (this.isStoreAndForwardEnabled || this.cacheTokens) { // Detect system environment builder.Register(c => new SystemEnvironment()) .As <ISystemEnvironment>() .SingleInstance(); // DataBase options builder.Register(c => new Storage.RocksDb.RocksDbOptionsProvider(c.Resolve <ISystemEnvironment>(), this.optimizeForPerformance)) .As <Storage.RocksDb.IRocksDbOptionsProvider>() .SingleInstance(); // IDbStore builder.Register( c => { var loggerFactory = c.Resolve <ILoggerFactory>(); ILogger logger = loggerFactory.CreateLogger(typeof(RoutingModule)); if (this.usePersistentStorage) { // Create partitions for messages and twins var partitionsList = new List <string> { Core.Constants.MessageStorePartitionKey, Core.Constants.TwinStorePartitionKey, Core.Constants.CheckpointStorePartitionKey }; try { IDbStoreProvider dbStoreprovider = Storage.RocksDb.DbStoreProvider.Create(c.Resolve <Storage.RocksDb.IRocksDbOptionsProvider>(), this.storagePath, partitionsList); logger.LogInformation($"Created persistent store at {this.storagePath}"); return(dbStoreprovider); } catch (Exception ex) when(!ExceptionEx.IsFatal(ex)) { logger.LogError(ex, "Error creating RocksDB store. Falling back to in-memory store."); return(new InMemoryDbStoreProvider()); } } else { logger.LogInformation($"Using in-memory store"); return(new InMemoryDbStoreProvider()); } }) .As <IDbStoreProvider>() .SingleInstance(); } // Task<ICredentialsStore> builder.Register(async c => { if (this.cacheTokens) { var dbStoreProvider = c.Resolve <IDbStoreProvider>(); IEncryptionProvider encryptionProvider = await this.workloadUri.Map( async uri => await EncryptionProvider.CreateAsync( this.storagePath, new Uri(uri), Service.Constants.WorkloadApiVersion, this.edgeModuleId, this.edgeModuleGenerationId.Expect(() => new InvalidOperationException("Missing generation ID")), Service.Constants.InitializationVectorFileName) as IEncryptionProvider) .GetOrElse(() => Task.FromResult <IEncryptionProvider>(NullEncryptionProvider.Instance)); IStoreProvider storeProvider = new StoreProvider(dbStoreProvider); IEntityStore <string, string> tokenCredentialsEntityStore = storeProvider.GetEntityStore <string, string>("tokenCredentials"); return(new TokenCredentialsStore(tokenCredentialsEntityStore, encryptionProvider)); } else { return(new NullCredentialsStore() as ICredentialsStore); } }) .As <Task <ICredentialsStore> >() .SingleInstance(); // IConnectionManager builder.Register(c => new ConnectionManager(c.Resolve <ICloudConnectionProvider>(), this.maxConnectedClients)) .As <IConnectionManager>() .SingleInstance(); // IEndpointFactory builder.Register(c => new EndpointFactory(c.Resolve <IConnectionManager>(), c.Resolve <Core.IMessageConverter <IRoutingMessage> >(), this.edgeDeviceId)) .As <IEndpointFactory>() .SingleInstance(); // RouteFactory builder.Register(c => new EdgeRouteFactory(c.Resolve <IEndpointFactory>())) .As <RouteFactory>() .SingleInstance(); // RouterConfig builder.Register(c => new RouterConfig(Enumerable.Empty <Route>())) .As <RouterConfig>() .SingleInstance(); if (!this.isStoreAndForwardEnabled) { // EndpointExecutorConfig builder.Register( c => { RetryStrategy defaultRetryStrategy = new FixedInterval(0, TimeSpan.FromSeconds(1)); TimeSpan defaultRevivePeriod = TimeSpan.FromHours(1); TimeSpan defaultTimeout = TimeSpan.FromSeconds(60); return(new EndpointExecutorConfig(defaultTimeout, defaultRetryStrategy, defaultRevivePeriod, true)); }) .As <EndpointExecutorConfig>() .SingleInstance(); // IEndpointExecutorFactory builder.Register(c => new SyncEndpointExecutorFactory(c.Resolve <EndpointExecutorConfig>())) .As <IEndpointExecutorFactory>() .SingleInstance(); // Task<Router> builder.Register( async c => { var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>(); var routerConfig = c.Resolve <RouterConfig>(); Router router = await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory); return(router); }) .As <Task <Router> >() .SingleInstance(); // ITwinManager builder.Register(c => TwinManager.CreateTwinManager(c.Resolve <IConnectionManager>(), c.Resolve <IMessageConverterProvider>(), Option.None <IStoreProvider>())) .As <ITwinManager>() .SingleInstance(); } else { // EndpointExecutorConfig builder.Register( c => { // Endpoint executor config values - // ExponentialBackoff - minBackoff = 1s, maxBackoff = 60s, delta (used to add randomness to backoff) - 1s (default) // Num of retries = int.MaxValue(we want to keep retrying till the message is sent) // Revive period - period for which the endpoint should be considered dead if it doesn't respond - 1 min (we want to try continuously till the message expires) // Timeout - time for which we want for the ack from the endpoint = 30s // TODO - Should the number of retries be tied to the Store and Forward ttl? Not // doing that right now as that value can be changed at runtime, but these settings // cannot. Need to make the number of retries dynamically configurable for that. TimeSpan minWait = TimeSpan.FromSeconds(1); TimeSpan maxWait = TimeSpan.FromSeconds(60); TimeSpan delta = TimeSpan.FromSeconds(1); int retries = int.MaxValue; RetryStrategy retryStrategy = new ExponentialBackoff(retries, minWait, maxWait, delta); TimeSpan timeout = TimeSpan.FromSeconds(30); TimeSpan revivePeriod = TimeSpan.FromSeconds(30); return(new EndpointExecutorConfig(timeout, retryStrategy, revivePeriod)); }) .As <EndpointExecutorConfig>() .SingleInstance(); // ICheckpointStore builder.Register(c => CheckpointStore.Create(c.Resolve <IDbStoreProvider>())) .As <ICheckpointStore>() .SingleInstance(); // IMessageStore builder.Register( c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var dbStoreProvider = c.Resolve <IDbStoreProvider>(); IStoreProvider storeProvider = new StoreProvider(dbStoreProvider); IMessageStore messageStore = new MessageStore(storeProvider, checkpointStore, TimeSpan.MaxValue); return(messageStore); }) .As <IMessageStore>() .SingleInstance(); // IEndpointExecutorFactory builder.Register( c => { var endpointExecutorConfig = c.Resolve <EndpointExecutorConfig>(); var messageStore = c.Resolve <IMessageStore>(); IEndpointExecutorFactory endpointExecutorFactory = new StoringAsyncEndpointExecutorFactory(endpointExecutorConfig, new AsyncEndpointExecutorOptions(10, TimeSpan.FromSeconds(10)), messageStore); return(endpointExecutorFactory); }) .As <IEndpointExecutorFactory>() .SingleInstance(); // Task<Router> builder.Register( async c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var routerConfig = c.Resolve <RouterConfig>(); var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>(); return(await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory, checkpointStore)); }) .As <Task <Router> >() .SingleInstance(); // ITwinManager builder.Register(c => TwinManager.CreateTwinManager(c.Resolve <IConnectionManager>(), c.Resolve <IMessageConverterProvider>(), Option.Some <IStoreProvider>(new StoreProvider(c.Resolve <IDbStoreProvider>())))) .As <ITwinManager>() .SingleInstance(); } // IClientCredentials "EdgeHubCredentials" builder.Register( c => { var identityFactory = c.Resolve <IClientCredentialsFactory>(); IClientCredentials edgeHubCredentials = this.connectionString.Map(cs => identityFactory.GetWithConnectionString(cs)).GetOrElse( () => identityFactory.GetWithIotEdged(this.edgeDeviceId, this.edgeModuleId)); return(edgeHubCredentials); }) .Named <IClientCredentials>("EdgeHubCredentials") .SingleInstance(); // Task<ICloudProxy> "EdgeHubCloudProxy" builder.Register( async c => { var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); var connectionManager = c.Resolve <IConnectionManager>(); Try <ICloudProxy> cloudProxyTry = await connectionManager.CreateCloudConnectionAsync(edgeHubCredentials); if (!cloudProxyTry.Success) { throw new EdgeHubConnectionException("Edge hub is unable to connect to IoT Hub", cloudProxyTry.Exception); } ICloudProxy cloudProxy = cloudProxyTry.Value; return(cloudProxy); }) .Named <Task <ICloudProxy> >("EdgeHubCloudProxy") .SingleInstance(); // IInvokeMethodHandler builder.Register(c => new InvokeMethodHandler(c.Resolve <IConnectionManager>())) .As <IInvokeMethodHandler>() .SingleInstance(); // Task<IEdgeHub> builder.Register( async c => { Router router = await c.Resolve <Task <Router> >(); IEdgeHub hub = new RoutingEdgeHub(router, c.Resolve <Core.IMessageConverter <IRoutingMessage> >(), c.Resolve <IConnectionManager>(), c.Resolve <ITwinManager>(), this.edgeDeviceId, c.Resolve <IInvokeMethodHandler>()); return(hub); }) .As <Task <IEdgeHub> >() .SingleInstance(); // Task<ConfigUpdater> builder.Register( async c => { IMessageStore messageStore = this.isStoreAndForwardEnabled ? c.Resolve <IMessageStore>() : null; Router router = await c.Resolve <Task <Router> >(); var configUpdater = new ConfigUpdater(router, messageStore); return(configUpdater); }) .As <Task <ConfigUpdater> >() .SingleInstance(); // Task<IConfigSource> builder.Register( async c => { var routeFactory = c.Resolve <RouteFactory>(); if (this.useTwinConfig) { var connectionManager = c.Resolve <IConnectionManager>(); var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); var twinCollectionMessageConverter = c.Resolve <Core.IMessageConverter <TwinCollection> >(); var twinMessageConverter = c.Resolve <Core.IMessageConverter <Twin> >(); var twinManager = c.Resolve <ITwinManager>(); ICloudProxy cloudProxy = await c.ResolveNamed <Task <ICloudProxy> >("EdgeHubCloudProxy"); IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConfigSource edgeHubConnection = await EdgeHubConnection.Create( edgeHubCredentials.Identity as IModuleIdentity, edgeHub, twinManager, connectionManager, cloudProxy, routeFactory, twinCollectionMessageConverter, twinMessageConverter, this.versionInfo ); return(edgeHubConnection); } else { return(new LocalConfigSource(routeFactory, this.routes, this.storeAndForwardConfiguration)); } }) .As <Task <IConfigSource> >() .SingleInstance(); // Task<IConnectionProvider> builder.Register( async c => { IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConnectionProvider connectionProvider = new ConnectionProvider(c.Resolve <IConnectionManager>(), edgeHub); return(connectionProvider); }) .As <Task <IConnectionProvider> >() .SingleInstance(); base.Load(builder); }
public RetryingCloudProxy(string id, Func <Task <Try <ICloudProxy> > > cloudProxyGetter, ICloudProxy cloudProxyImplementation) { this.id = Preconditions.CheckNonWhiteSpace(id, nameof(id)); this.cloudProxyGetter = Preconditions.CheckNotNull(cloudProxyGetter, nameof(cloudProxyGetter)); this.innerCloudProxy = Preconditions.CheckNotNull(cloudProxyImplementation, nameof(cloudProxyImplementation)); }
public async Task CreateCloudProxyTest() { string edgeDeviceId = "edgeDevice"; string module1Id = "module1"; var module1Credentials = new TokenCredentials(new ModuleIdentity("iotHub", edgeDeviceId, module1Id), "token", DummyProductInfo, false); IClient client1 = GetDeviceClient(); IClient client2 = GetDeviceClient(); var messageConverterProvider = Mock.Of <IMessageConverterProvider>(); var deviceClientProvider = new Mock <IClientProvider>(); deviceClientProvider.SetupSequence(d => d.Create(It.IsAny <IIdentity>(), It.IsAny <ITokenProvider>(), It.IsAny <ITransportSettings[]>())) .Returns(client1) .Returns(client2); var productInfoStore = Mock.Of <IProductInfoStore>(); var credentialsCache = Mock.Of <ICredentialsCache>(); var edgeHubIdentity = Mock.Of <IIdentity>(i => i.Id == "edgeDevice/$edgeHub"); var cloudConnectionProvider = new CloudConnectionProvider( messageConverterProvider, 1, deviceClientProvider.Object, Option.None <UpstreamProtocol>(), Mock.Of <ITokenProvider>(), Mock.Of <IDeviceScopeIdentitiesCache>(), credentialsCache, edgeHubIdentity, TimeSpan.FromMinutes(60), true, TimeSpan.FromSeconds(20), Option.None <IWebProxy>(), productInfoStore); cloudConnectionProvider.BindEdgeHub(Mock.Of <IEdgeHub>()); IConnectionManager connectionManager = new ConnectionManager(cloudConnectionProvider, credentialsCache, GetIdentityProvider()); Task <Try <ICloudProxy> > getCloudProxyTask1 = connectionManager.CreateCloudConnectionAsync(module1Credentials); Task <Try <ICloudProxy> > getCloudProxyTask2 = connectionManager.CreateCloudConnectionAsync(module1Credentials); Try <ICloudProxy>[] cloudProxies = await Task.WhenAll(getCloudProxyTask1, getCloudProxyTask2); Assert.NotEqual(cloudProxies[0].Value, cloudProxies[1].Value); Option <ICloudProxy> currentCloudProxyId1 = await connectionManager.GetCloudConnection(module1Credentials.Identity.Id); ICloudProxy currentCloudProxy = currentCloudProxyId1.OrDefault(); ICloudProxy cloudProxy1 = cloudProxies[0].Value; ICloudProxy cloudProxy2 = cloudProxies[1].Value; Assert.True(currentCloudProxy == cloudProxy1 || currentCloudProxy == cloudProxy2); if (currentCloudProxy == cloudProxy1) { Mock.Get(client2).Verify(cp => cp.CloseAsync(), Times.Once); Mock.Get(client1).Verify(cp => cp.CloseAsync(), Times.Never); } else { Mock.Get(client1).Verify(cp => cp.CloseAsync(), Times.Once); Mock.Get(client2).Verify(cp => cp.CloseAsync(), Times.Never); } }
public async Task RefreshTokenTest() { string iothubHostName = "test.azure-devices.net"; string deviceId = "device1"; IClientCredentials GetClientCredentialsWithExpiringToken() { string token = TokenHelper.CreateSasToken(iothubHostName, DateTime.UtcNow.AddMinutes(3)); var identity = new DeviceIdentity(iothubHostName, deviceId); return(new TokenCredentials(identity, token, string.Empty)); } IClientCredentials GetClientCredentialsWithNonExpiringToken() { string token = TokenHelper.CreateSasToken(iothubHostName, DateTime.UtcNow.AddMinutes(10)); var identity = new DeviceIdentity(iothubHostName, deviceId); return(new TokenCredentials(identity, token, string.Empty)); } IAuthenticationMethod authenticationMethod = null; IClientProvider clientProvider = GetMockDeviceClientProviderWithToken((s, a, t) => authenticationMethod = a); var transportSettings = new ITransportSettings[] { new AmqpTransportSettings(TransportType.Amqp_Tcp_Only) }; var receivedStatus = CloudConnectionStatus.ConnectionEstablished; void ConnectionStatusHandler(string id, CloudConnectionStatus status) => receivedStatus = status; var messageConverterProvider = new MessageConverterProvider(new Dictionary <Type, IMessageConverter> { [typeof(TwinCollection)] = Mock.Of <IMessageConverter>() }); var cloudConnection = new CloudConnection(ConnectionStatusHandler, transportSettings, messageConverterProvider, clientProvider, Mock.Of <ICloudListener>(), TokenProvider, DeviceScopeIdentitiesCache, TimeSpan.FromMinutes(60)); IClientCredentials clientCredentialsWithExpiringToken1 = GetClientCredentialsWithExpiringToken(); ICloudProxy cloudProxy1 = await cloudConnection.CreateOrUpdateAsync(clientCredentialsWithExpiringToken1); Assert.True(cloudProxy1.IsActive); Assert.Equal(cloudProxy1, cloudConnection.CloudProxy.OrDefault()); Assert.NotNull(authenticationMethod); var deviceAuthenticationWithTokenRefresh = authenticationMethod as DeviceAuthenticationWithTokenRefresh; Assert.NotNull(deviceAuthenticationWithTokenRefresh); Task <string> getTokenTask = deviceAuthenticationWithTokenRefresh.GetTokenAsync(iothubHostName); Assert.False(getTokenTask.IsCompleted); Assert.Equal(receivedStatus, CloudConnectionStatus.TokenNearExpiry); IClientCredentials clientCredentialsWithExpiringToken2 = GetClientCredentialsWithNonExpiringToken(); ICloudProxy cloudProxy2 = await cloudConnection.CreateOrUpdateAsync(clientCredentialsWithExpiringToken2); // Wait for the task to complete await Task.Delay(TimeSpan.FromSeconds(10)); Assert.True(getTokenTask.IsCompletedSuccessfully); Assert.Equal(cloudProxy2, cloudConnection.CloudProxy.OrDefault()); Assert.True(cloudProxy2.IsActive); Assert.True(cloudProxy1.IsActive); Assert.Equal(cloudProxy1, cloudProxy2); Assert.Equal(getTokenTask.Result, (clientCredentialsWithExpiringToken2 as ITokenCredentials)?.Token); }
internal async Task <TwinInfo> GetTwinInfoWhenCloudOnlineAsync(string id, ICloudProxy cp, bool sendDesiredPropertyUpdate) { TwinCollection diff = null; // Used for returning value to caller TwinInfo cached; using (await this.twinLock.LockAsync()) { IMessage twinMessage = await cp.GetTwinAsync(); Shared.Twin cloudTwin = this.twinConverter.FromMessage(twinMessage); Events.GotTwinFromCloudSuccess(id, cloudTwin.Properties.Desired.Version, cloudTwin.Properties.Reported.Version); var newTwin = new TwinInfo(cloudTwin, null); cached = newTwin; IEntityStore <string, TwinInfo> twinStore = this.TwinStore.Expect(() => new InvalidOperationException("Missing twin store")); await twinStore.PutOrUpdate( id, newTwin, t => { // If the new twin is more recent than the cached twin, update the cached copy. // If not, reject the cloud twin if (t.Twin == null || cloudTwin.Properties.Desired.Version > t.Twin.Properties.Desired.Version || cloudTwin.Properties.Reported.Version > t.Twin.Properties.Reported.Version) { if (t.Twin != null) { Events.UpdateCachedTwin( id, t.Twin.Properties.Desired.Version, cloudTwin.Properties.Desired.Version, t.Twin.Properties.Reported.Version, cloudTwin.Properties.Reported.Version); cached = new TwinInfo(cloudTwin, t.ReportedPropertiesPatch); // If the device is subscribed to desired property updates and we are refreshing twin as a result // of a connection reset or desired property update, send a patch to the downstream device if (sendDesiredPropertyUpdate) { Option <IReadOnlyDictionary <DeviceSubscription, bool> > subscriptions = this.connectionManager.GetSubscriptions(id); subscriptions.ForEach( s => { if (s.TryGetValue(DeviceSubscription.DesiredPropertyUpdates, out bool hasDesiredPropertyUpdatesSubscription) && hasDesiredPropertyUpdatesSubscription) { Events.SendDesiredPropertyUpdateToSubscriber( id, t.Twin.Properties.Desired.Version, cloudTwin.Properties.Desired.Version); diff = new TwinCollection(JsonEx.Diff(t.Twin.Properties.Desired, cloudTwin.Properties.Desired)); } }); } } } else { Events.PreserveCachedTwin( id, t.Twin.Properties.Desired.Version, cloudTwin.Properties.Desired.Version, t.Twin.Properties.Reported.Version, cloudTwin.Properties.Reported.Version); cached = t; } return(cached); }); } if ((diff != null) && (diff.Count != 0)) { Events.SendDiffToDeviceProxy(diff.ToString(), id); IMessage message = this.twinCollectionConverter.ToMessage(diff); await this.SendDesiredPropertiesToDeviceProxy(id, message); } return(cached); }
protected override void Load(ContainerBuilder builder) { // IMessageConverter<IRoutingMessage> builder.Register(c => new RoutingMessageConverter()) .As <Core.IMessageConverter <IRoutingMessage> >() .SingleInstance(); // IRoutingPerfCounter builder.Register( c => { Routing.PerfCounter = NullRoutingPerfCounter.Instance; return(Routing.PerfCounter); }) .As <IRoutingPerfCounter>() .AutoActivate() .SingleInstance(); // IRoutingUserAnalyticsLogger builder.Register( c => { Routing.UserAnalyticsLogger = NullUserAnalyticsLogger.Instance; return(Routing.UserAnalyticsLogger); }) .As <IRoutingUserAnalyticsLogger>() .AutoActivate() .SingleInstance(); // IRoutingUserMetricLogger builder.Register( c => { Routing.UserMetricLogger = NullRoutingUserMetricLogger.Instance; return(Routing.UserMetricLogger); }) .As <IRoutingUserMetricLogger>() .AutoActivate() .SingleInstance(); // IMessageConverter<Message> builder.Register(c => new DeviceClientMessageConverter()) .As <Core.IMessageConverter <Message> >() .SingleInstance(); // IMessageConverter<Twin> builder.Register(c => new TwinMessageConverter()) .As <Core.IMessageConverter <Twin> >() .SingleInstance(); // IMessageConverter<TwinCollection> builder.Register(c => new TwinCollectionMessageConverter()) .As <Core.IMessageConverter <TwinCollection> >() .SingleInstance(); // IMessageConverterProvider builder.Register( c => new MessageConverterProvider(new Dictionary <Type, IMessageConverter>() { { typeof(Message), c.Resolve <Core.IMessageConverter <Message> >() }, { typeof(Twin), c.Resolve <Core.IMessageConverter <Twin> >() }, { typeof(TwinCollection), c.Resolve <Core.IMessageConverter <TwinCollection> >() } })) .As <IMessageConverterProvider>() .SingleInstance(); // IDeviceConnectivityManager builder.Register( c => { IDeviceConnectivityManager deviceConnectivityManager = new DeviceConnectivityManager(this.connectivityCheckFrequency, TimeSpan.FromMinutes(2)); return(deviceConnectivityManager); }) .As <IDeviceConnectivityManager>() .SingleInstance(); // IDeviceClientProvider builder.Register(c => { IClientProvider underlyingClientProvider = new ClientProvider(); IClientProvider connectivityAwareClientProvider = new ConnectivityAwareClientProvider(underlyingClientProvider, c.Resolve <IDeviceConnectivityManager>()); return(connectivityAwareClientProvider); }) .As <IClientProvider>() .SingleInstance(); // Task<ICloudConnectionProvider> builder.Register( async c => { var messageConverterProvider = c.Resolve <IMessageConverterProvider>(); var clientProvider = c.Resolve <IClientProvider>(); var tokenProvider = c.ResolveNamed <ITokenProvider>("EdgeHubClientAuthTokenProvider"); IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await c.Resolve <Task <IDeviceScopeIdentitiesCache> >(); ICloudConnectionProvider cloudConnectionProvider = new CloudConnectionProvider( messageConverterProvider, this.connectionPoolSize, clientProvider, this.upstreamProtocol, tokenProvider, deviceScopeIdentitiesCache, TimeSpan.FromMinutes(60)); return(cloudConnectionProvider); }) .As <Task <ICloudConnectionProvider> >() .SingleInstance(); // Task<ICredentialsStore> builder.Register(async c => { if (this.cacheTokens) { IKeyValueStore <string, string> encryptedStore = await c.ResolveNamed <Task <IKeyValueStore <string, string> > >("EncryptedStore"); return(new TokenCredentialsStore(encryptedStore)); } else { return(new NullCredentialsStore() as ICredentialsStore); } }) .As <Task <ICredentialsStore> >() .SingleInstance(); // Task<IConnectionManager> builder.Register( async c => { ICloudConnectionProvider cloudConnectionProvider = await c.Resolve <Task <ICloudConnectionProvider> >(); IConnectionManager connectionManager = new ConnectionManager(cloudConnectionProvider, this.maxConnectedClients); return(connectionManager); }) .As <Task <IConnectionManager> >() .SingleInstance(); // Task<IEndpointFactory> builder.Register(async c => { var messageConverter = c.Resolve <Core.IMessageConverter <IRoutingMessage> >(); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); return(new EndpointFactory(connectionManager, messageConverter, this.edgeDeviceId) as IEndpointFactory); }) .As <Task <IEndpointFactory> >() .SingleInstance(); // Task<RouteFactory> builder.Register(async c => new EdgeRouteFactory(await c.Resolve <Task <IEndpointFactory> >()) as RouteFactory) .As <Task <RouteFactory> >() .SingleInstance(); // RouterConfig builder.Register(c => new RouterConfig(Enumerable.Empty <Route>())) .As <RouterConfig>() .SingleInstance(); if (!this.isStoreAndForwardEnabled) { // EndpointExecutorConfig builder.Register( c => { RetryStrategy defaultRetryStrategy = new FixedInterval(0, TimeSpan.FromSeconds(1)); TimeSpan defaultRevivePeriod = TimeSpan.FromHours(1); TimeSpan defaultTimeout = TimeSpan.FromSeconds(60); return(new EndpointExecutorConfig(defaultTimeout, defaultRetryStrategy, defaultRevivePeriod, true)); }) .As <EndpointExecutorConfig>() .SingleInstance(); // IEndpointExecutorFactory builder.Register(c => new SyncEndpointExecutorFactory(c.Resolve <EndpointExecutorConfig>())) .As <IEndpointExecutorFactory>() .SingleInstance(); // Task<Router> builder.Register( async c => { var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>(); var routerConfig = c.Resolve <RouterConfig>(); Router router = await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory); return(router); }) .As <Task <Router> >() .SingleInstance(); // Task<ITwinManager> builder.Register(async c => { var messageConverterProvider = c.Resolve <IMessageConverterProvider>(); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); return(TwinManager.CreateTwinManager(connectionManager, messageConverterProvider, Option.None <IStoreProvider>())); }) .As <Task <ITwinManager> >() .SingleInstance(); } else { // EndpointExecutorConfig builder.Register( c => { // Endpoint executor config values - // ExponentialBackoff - minBackoff = 1s, maxBackoff = 60s, delta (used to add randomness to backoff) - 1s (default) // Num of retries = int.MaxValue(we want to keep retrying till the message is sent) // Revive period - period for which the endpoint should be considered dead if it doesn't respond - 1 min (we want to try continuously till the message expires) // Timeout - time for which we want for the ack from the endpoint = 30s // TODO - Should the number of retries be tied to the Store and Forward ttl? Not // doing that right now as that value can be changed at runtime, but these settings // cannot. Need to make the number of retries dynamically configurable for that. TimeSpan minWait = TimeSpan.FromSeconds(1); TimeSpan maxWait = TimeSpan.FromSeconds(60); TimeSpan delta = TimeSpan.FromSeconds(1); int retries = int.MaxValue; RetryStrategy retryStrategy = new ExponentialBackoff(retries, minWait, maxWait, delta); TimeSpan timeout = TimeSpan.FromSeconds(30); TimeSpan revivePeriod = TimeSpan.FromSeconds(30); return(new EndpointExecutorConfig(timeout, retryStrategy, revivePeriod)); }) .As <EndpointExecutorConfig>() .SingleInstance(); // ICheckpointStore builder.Register(c => CheckpointStore.Create(c.Resolve <IDbStoreProvider>())) .As <ICheckpointStore>() .SingleInstance(); // IMessageStore builder.Register( c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var dbStoreProvider = c.Resolve <IDbStoreProvider>(); IStoreProvider storeProvider = new StoreProvider(dbStoreProvider); IMessageStore messageStore = new MessageStore(storeProvider, checkpointStore, TimeSpan.MaxValue); return(messageStore); }) .As <IMessageStore>() .SingleInstance(); // IEndpointExecutorFactory builder.Register( c => { var endpointExecutorConfig = c.Resolve <EndpointExecutorConfig>(); var messageStore = c.Resolve <IMessageStore>(); IEndpointExecutorFactory endpointExecutorFactory = new StoringAsyncEndpointExecutorFactory(endpointExecutorConfig, new AsyncEndpointExecutorOptions(10, TimeSpan.FromSeconds(10)), messageStore); return(endpointExecutorFactory); }) .As <IEndpointExecutorFactory>() .SingleInstance(); // Task<Router> builder.Register( async c => { var checkpointStore = c.Resolve <ICheckpointStore>(); var routerConfig = c.Resolve <RouterConfig>(); var endpointExecutorFactory = c.Resolve <IEndpointExecutorFactory>(); return(await Router.CreateAsync(Guid.NewGuid().ToString(), this.iotHubName, routerConfig, endpointExecutorFactory, checkpointStore)); }) .As <Task <Router> >() .SingleInstance(); // Task<ITwinManager> builder.Register(async c => { var dbStoreProvider = c.Resolve <IDbStoreProvider>(); var messageConverterProvider = c.Resolve <IMessageConverterProvider>(); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); return(TwinManager.CreateTwinManager(connectionManager, messageConverterProvider, Option.Some <IStoreProvider>(new StoreProvider(dbStoreProvider)))); }) .As <Task <ITwinManager> >() .SingleInstance(); } // IClientCredentials "EdgeHubCredentials" builder.Register( c => { var identityFactory = c.Resolve <IClientCredentialsFactory>(); IClientCredentials edgeHubCredentials = this.connectionString.Map(cs => identityFactory.GetWithConnectionString(cs)).GetOrElse( () => identityFactory.GetWithIotEdged(this.edgeDeviceId, this.edgeModuleId)); return(edgeHubCredentials); }) .Named <IClientCredentials>("EdgeHubCredentials") .SingleInstance(); // Task<ICloudProxy> "EdgeHubCloudProxy" builder.Register( async c => { var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); Try <ICloudProxy> cloudProxyTry = await connectionManager.CreateCloudConnectionAsync(edgeHubCredentials); if (!cloudProxyTry.Success) { throw new EdgeHubConnectionException("Edge hub is unable to connect to IoT Hub", cloudProxyTry.Exception); } ICloudProxy cloudProxy = cloudProxyTry.Value; return(cloudProxy); }) .Named <Task <ICloudProxy> >("EdgeHubCloudProxy") .SingleInstance(); // Task<IInvokeMethodHandler> builder.Register(async c => { IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); return(new InvokeMethodHandler(connectionManager) as IInvokeMethodHandler); }) .As <Task <IInvokeMethodHandler> >() .SingleInstance(); // Task<IEdgeHub> builder.Register( async c => { var routingMessageConverter = c.Resolve <Core.IMessageConverter <IRoutingMessage> >(); var routerTask = c.Resolve <Task <Router> >(); var twinManagerTask = c.Resolve <Task <ITwinManager> >(); var invokeMethodHandlerTask = c.Resolve <Task <IInvokeMethodHandler> >(); var connectionManagerTask = c.Resolve <Task <IConnectionManager> >(); Router router = await routerTask; ITwinManager twinManager = await twinManagerTask; IConnectionManager connectionManager = await connectionManagerTask; IInvokeMethodHandler invokeMethodHandler = await invokeMethodHandlerTask; IEdgeHub hub = new RoutingEdgeHub(router, routingMessageConverter, connectionManager, twinManager, this.edgeDeviceId, invokeMethodHandler); return(hub); }) .As <Task <IEdgeHub> >() .SingleInstance(); // Task<ConfigUpdater> builder.Register( async c => { IMessageStore messageStore = this.isStoreAndForwardEnabled ? c.Resolve <IMessageStore>() : null; Router router = await c.Resolve <Task <Router> >(); var configUpdater = new ConfigUpdater(router, messageStore); return(configUpdater); }) .As <Task <ConfigUpdater> >() .SingleInstance(); // Task<IConfigSource> builder.Register( async c => { RouteFactory routeFactory = await c.Resolve <Task <RouteFactory> >(); if (this.useTwinConfig) { var edgeHubCredentials = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials"); var twinCollectionMessageConverter = c.Resolve <Core.IMessageConverter <TwinCollection> >(); var twinMessageConverter = c.Resolve <Core.IMessageConverter <Twin> >(); ITwinManager twinManager = await c.Resolve <Task <ITwinManager> >(); ICloudProxy cloudProxy = await c.ResolveNamed <Task <ICloudProxy> >("EdgeHubCloudProxy"); IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await c.Resolve <Task <IDeviceScopeIdentitiesCache> >(); IConfigSource edgeHubConnection = await EdgeHubConnection.Create( edgeHubCredentials, edgeHub, twinManager, connectionManager, cloudProxy, routeFactory, twinCollectionMessageConverter, twinMessageConverter, this.versionInfo, deviceScopeIdentitiesCache ); return(edgeHubConnection); } else { return(new LocalConfigSource(routeFactory, this.routes, this.storeAndForwardConfiguration)); } }) .As <Task <IConfigSource> >() .SingleInstance(); // Task<IConnectionProvider> builder.Register( async c => { IConnectionManager connectionManager = await c.Resolve <Task <IConnectionManager> >(); IEdgeHub edgeHub = await c.Resolve <Task <IEdgeHub> >(); IConnectionProvider connectionProvider = new ConnectionProvider(connectionManager, edgeHub); return(connectionProvider); }) .As <Task <IConnectionProvider> >() .SingleInstance(); base.Load(builder); }
public static async Task <int> MainAsync(IConfigurationRoot configuration) { string logLevel = configuration.GetValue($"{Logger.RuntimeLogLevelEnvKey}", "info"); Logger.SetLogLevel(logLevel); // Set the LoggerFactory used by the Routing code. if (configuration.GetValue("EnableRoutingLogging", false)) { Routing.Core.Routing.LoggerFactory = Logger.Factory; } X509Certificate2 cert; IEnumerable <X509Certificate2> chain; string edgeHubConnectionString = configuration.GetValue <string>(Constants.IotHubConnectionStringVariableName); // When connection string is not set it is edged mode if (string.IsNullOrEmpty(edgeHubConnectionString)) { var workloadUri = new Uri(configuration.GetValue <string>(Constants.WorkloadUriVariableName)); string edgeHubHostname = configuration.GetValue <string>(Constants.EdgeDeviceHostnameVariableName); string moduleId = configuration.GetValue <string>(Constants.ModuleIdVariableName); string generationId = configuration.GetValue <string>(Constants.ModuleGenerationIdVariableName); DateTime expiration = DateTime.UtcNow.AddDays(Constants.CertificateValidityDays); (cert, chain) = await CertificateHelper.GetServerCertificatesFromEdgelet(workloadUri, Constants.WorkloadApiVersion, moduleId, generationId, edgeHubHostname, expiration); } else { string edgeHubCertPath = configuration.GetValue <string>(Constants.EdgeHubServerCertificateFileKey); cert = new X509Certificate2(edgeHubCertPath); string edgeHubCaChainCertPath = configuration.GetValue <string>(Constants.EdgeHubServerCAChainCertificateFileKey); chain = CertificateHelper.GetServerCACertificatesFromFile(edgeHubCaChainCertPath); } // TODO: set certificate for Startup without the cache ServerCertificateCache.X509Certificate = cert; Hosting hosting = Hosting.Initialize(); IContainer container = hosting.Container; ILogger logger = container.Resolve <ILoggerFactory>().CreateLogger("EdgeHub"); logger.LogInformation("Starting Edge Hub"); VersionInfo versionInfo = VersionInfo.Get(Constants.VersionInfoFileName); if (versionInfo != VersionInfo.Empty) { logger.LogInformation($"Version - {versionInfo.ToString(true)}"); } LogLogo(logger); if (chain != null) { logger.LogInformation("Installing intermediate certificates."); CertificateHelper.InstallCerts( RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StoreName.CertificateAuthority : StoreName.Root, StoreLocation.CurrentUser, chain); } else { logger.LogWarning("Unable to find intermediate certificates."); } // EdgeHub cloud proxy and DeviceConnectivityManager have a circular dependency, // so the cloud proxy has to be set on the DeviceConnectivityManager after both have been initialized. var deviceConnectivityManager = container.Resolve <IDeviceConnectivityManager>(); ICloudProxy cloudProxy = await container.ResolveNamed <Task <ICloudProxy> >("EdgeHubCloudProxy"); (deviceConnectivityManager as DeviceConnectivityManager)?.SetTestCloudProxy(cloudProxy); logger.LogInformation("Initializing configuration"); IConfigSource configSource = await container.Resolve <Task <IConfigSource> >(); ConfigUpdater configUpdater = await container.Resolve <Task <ConfigUpdater> >(); await configUpdater.Init(configSource); (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(ShutdownWaitPeriod, logger); using (IProtocolHead protocolHead = new EdgeHubProtocolHead( new IProtocolHead[] { new HttpProtocolHead(hosting.WebHost), await container.Resolve <Task <MqttProtocolHead> >(), await container.Resolve <Task <AmqpProtocolHead> >() }, logger)) { await protocolHead.StartAsync(); await cts.Token.WhenCanceled(); await Task.WhenAny(protocolHead.CloseAsync(CancellationToken.None), Task.Delay(TimeSpan.FromSeconds(10), CancellationToken.None)); } completed.Set(); handler.ForEach(h => GC.KeepAlive(h)); return(0); }
/// <summary> /// This method does the following - /// 1. Updates the Identity to be used for the cloud connection /// 2. Updates the cloud proxy - /// i. If there is an existing device client and /// a. If is waiting for an updated token, and the Identity has a token, /// then it uses that to give it to the waiting client authentication method. /// b. If not, then it creates a new cloud proxy (and device client) and closes the existing one /// ii. Else, if there is no cloud proxy, then opens a device client and creates a cloud proxy. /// </summary> public async Task <ICloudProxy> UpdateTokenAsync(ITokenCredentials newTokenCredentials) { Preconditions.CheckNotNull(newTokenCredentials, nameof(newTokenCredentials)); using (await this.identityUpdateLock.LockAsync()) { // Disable callbacks while we update the cloud proxy. // TODO - instead of this, make convert Option<ICloudProxy> CloudProxy to Task<Option<ICloudProxy>> GetCloudProxy // which can be awaited when an update is in progress. this.callbacksEnabled = false; try { ITokenProvider tokenProvider = new ClientTokenBasedTokenProvider(newTokenCredentials, this); // First check if there is an existing cloud proxy ICloudProxy proxy = await this.CloudProxy.Map( async cp => { // If the Identity has a token, and we have a tokenGetter, that means // the connection is waiting for a new token. So give it the token and // complete the tokenGetter if (this.tokenGetter.HasValue) { if (TokenHelper.IsTokenExpired(this.Identity.IotHubHostName, newTokenCredentials.Token)) { throw new InvalidOperationException($"Token for client {this.Identity.Id} is expired"); } this.tokenGetter.ForEach( tg => { // First reset the token getter and then set the result. this.tokenGetter = Option.None <TaskCompletionSource <string> >(); tg.SetResult(newTokenCredentials.Token); }); return(cp); } // Else this is a new connection for the same device Id. So open a new connection, // and if that is successful, close the existing one. else { ICloudProxy newCloudProxy = await this.CreateNewCloudProxyAsync(tokenProvider); await cp.CloseAsync(); return(newCloudProxy); } }) // No existing cloud proxy, so just create a new one. .GetOrElse(() => this.CreateNewCloudProxyAsync(tokenProvider)); // Set Identity only after successfully opening cloud proxy // That way, if a we have one existing connection for a deviceA, // and a new connection for deviceA comes in with an invalid key/token, // the existing connection is not affected. this.cloudProxy = Option.Some(proxy); Events.UpdatedCloudConnection(this.Identity); return(proxy); } catch (Exception ex) { Events.CreateException(ex, this.Identity); throw; } finally { this.callbacksEnabled = true; } } }
public async Task TestMultipleOperations() { // Arrange const string Id = "id1"; var identity = Mock.Of <IIdentity>(i => i.Id == Id); var twinMessageConverter = new TwinMessageConverter(); var twinCollectionMessageConverter = new TwinCollectionMessageConverter(); var messageConverterProvider = new MessageConverterProvider( new Dictionary <Type, IMessageConverter> { { typeof(Message), new DeviceClientMessageConverter() }, { typeof(Shared.Twin), twinMessageConverter }, { typeof(TwinCollection), twinCollectionMessageConverter } }); var edgeHubTokenProvider = new Mock <ITokenProvider>(); var clientWatcher = new ClientWatcher(); var clientProvider = new Mock <IClientProvider>(); clientProvider.Setup(c => c.Create(identity, edgeHubTokenProvider.Object, It.IsAny <ITransportSettings[]>(), Option.None <string>())) .Returns(() => new ThrowingClient(clientWatcher, 15)); var deviceScopeIdentitiesCache = new Mock <IDeviceScopeIdentitiesCache>(); deviceScopeIdentitiesCache.Setup(d => d.GetServiceIdentity(Id, false)) .ReturnsAsync( Option.Some( new ServiceIdentity( Id, "dummy", new List <string>(), new ServiceAuthentication(new SymmetricKeyAuthentication("foo", "bar")), ServiceIdentityStatus.Enabled))); var edgeHubIdentity = Mock.Of <IIdentity>(); ConnectionMetadata connectionMetadata = new ConnectionMetadata("edgeProdInfo"); var metadataStore = new Mock <IMetadataStore>(); metadataStore.Setup(p => p.GetMetadata(Id)) .ReturnsAsync(connectionMetadata); var identityProvider = new Mock <IIdentityProvider>(); identityProvider.Setup(i => i.Create(Id)).Returns(identity); var credentialsCache = new Mock <ICredentialsCache>(); var edgeHub = new Mock <IEdgeHub>(); var connectionProvider = new CloudConnectionProvider( messageConverterProvider, 1, clientProvider.Object, Option.None <UpstreamProtocol>(), edgeHubTokenProvider.Object, deviceScopeIdentitiesCache.Object, credentialsCache.Object, edgeHubIdentity, TimeSpan.FromMinutes(10), false, TimeSpan.FromMinutes(10), false, Option.None <IWebProxy>(), metadataStore.Object); connectionProvider.BindEdgeHub(edgeHub.Object); var deviceConnectivityManager = Mock.Of <IDeviceConnectivityManager>(); var connectionManager = new ConnectionManager(connectionProvider, credentialsCache.Object, identityProvider.Object, deviceConnectivityManager); async Task <ICloudProxy> GetCloudProxy(IConnectionManager cm) { // Act Option <ICloudProxy> cloudProxyOption = await cm.GetCloudConnection(Id); // Assert Assert.True(cloudProxyOption.HasValue); ICloudProxy cloudProxy = cloudProxyOption.OrDefault(); Assert.True(cloudProxy.IsActive); return(cloudProxy); } var messagesToSend = new List <IMessage>(); for (int i = 0; i < 60; i++) { var message = new EdgeMessage.Builder(new byte[i]) .SetSystemProperties( new Dictionary <string, string>() { [SystemProperties.MessageId] = i.ToString() }) .Build(); messagesToSend.Add(message); } // Act ICloudProxy cloudProxy1 = await GetCloudProxy(connectionManager); ICloudProxy cloudProxy2 = await GetCloudProxy(connectionManager); ICloudProxy cloudProxy3 = await GetCloudProxy(connectionManager); // Act var tasks = new[] { RunGetTwin(cloudProxy1, 10), RunGetTwin(cloudProxy2, 30), RunGetTwin(cloudProxy3, 10), RunSendMessages(cloudProxy1, messagesToSend.Take(20), 2), RunSendMessages(cloudProxy2, messagesToSend.Skip(20).Take(10)), RunSendMessages(cloudProxy3, messagesToSend.Skip(30).Take(30), 3) }; await Task.WhenAll(tasks); // Assert Assert.Equal(50, clientWatcher.GetTwinCount); List <string> expectedMessageIds = messagesToSend .Select(m => m.SystemProperties[SystemProperties.MessageId]) .OrderBy(s => s) .ToList(); List <string> receivedMessageIds = clientWatcher.ReceivedMessages .Select(m => m.MessageId) .OrderBy(s => s) .ToList(); Assert.Equal(expectedMessageIds.Count, receivedMessageIds.Count); Assert.Equal(expectedMessageIds, receivedMessageIds); }