diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index 363d09e8a..bcbf23e2c 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit 363d09e8a3509f663327d6e8414d0b2e052c815d +Subproject commit bcbf23e2c466a5dfa695f111b6eebd9bc9029100 diff --git a/devicedefender/include/aws/iotdevicedefender/DeviceDefender.h b/devicedefender/include/aws/iotdevicedefender/DeviceDefender.h index 6e11038f7..9cbd4146d 100644 --- a/devicedefender/include/aws/iotdevicedefender/DeviceDefender.h +++ b/devicedefender/include/aws/iotdevicedefender/DeviceDefender.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -174,6 +175,12 @@ namespace Aws Crt::Io::EventLoopGroup &eventLoopGroup, const Crt::String &thingName); + ReportTaskBuilder( + Crt::Allocator *allocator, + std::shared_ptr mqtt5Client, + Crt::Io::EventLoopGroup &eventLoopGroup, + const Crt::String &thingName); + /** * Sets the device defender report format, or defaults to AWS_IDDRF_JSON. */ diff --git a/devicedefender/source/DeviceDefender.cpp b/devicedefender/source/DeviceDefender.cpp index 4d83e77bf..50023e78d 100644 --- a/devicedefender/source/DeviceDefender.cpp +++ b/devicedefender/source/DeviceDefender.cpp @@ -300,6 +300,19 @@ namespace Aws m_cancellationUserdata = nullptr; } + ReportTaskBuilder::ReportTaskBuilder( + Crt::Allocator *allocator, + std::shared_ptr mqtt5Client, + Crt::Io::EventLoopGroup &eventLoopGroup, + const Crt::String &thingName) + : ReportTaskBuilder( + allocator, + Crt::Mqtt::MqttConnection::NewConnectionFromMqtt5Client(mqtt5Client), + eventLoopGroup, + thingName) + { + } + ReportTaskBuilder &ReportTaskBuilder::WithReportFormat(ReportFormat reportFormat) noexcept { m_reportFormat = reportFormat; diff --git a/devicedefender/tests/DeviceDefenderMetricsTest.cpp b/devicedefender/tests/DeviceDefenderMetricsTest.cpp index 50ec78c88..f63223575 100644 --- a/devicedefender/tests/DeviceDefenderMetricsTest.cpp +++ b/devicedefender/tests/DeviceDefenderMetricsTest.cpp @@ -6,6 +6,8 @@ #include "aws/crt/Types.h" #include +#include + #include #include #include @@ -32,9 +34,8 @@ static int s_TestDeviceDefenderCustomMetricSuccess(Aws::Crt::Allocator *allocato Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); clientBootstrap.EnableBlockingShutdown(); Aws::Crt::Mqtt::MqttClient mqttClient(clientBootstrap, allocator); - Aws::Crt::Mqtt::MqttClient mqttClientMoved = std::move(mqttClient); - auto mqttConnection = mqttClientMoved.NewConnection("www.example.com", 443, socketOptions, tlsContext); + auto mqttConnection = mqttClient.NewConnection("www.example.com", 443, socketOptions, tlsContext); const Aws::Crt::String thingName("TestThing"); bool callbackSuccess = false; @@ -142,9 +143,8 @@ static int s_TestDeviceDefenderCustomMetricFail(Aws::Crt::Allocator *allocator, Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); clientBootstrap.EnableBlockingShutdown(); Aws::Crt::Mqtt::MqttClient mqttClient(clientBootstrap, allocator); - Aws::Crt::Mqtt::MqttClient mqttClientMoved = std::move(mqttClient); - auto mqttConnection = mqttClientMoved.NewConnection("www.example.com", 443, socketOptions, tlsContext); + auto mqttConnection = mqttClient.NewConnection("www.example.com", 443, socketOptions, tlsContext); const Aws::Crt::String thingName("TestThing"); bool callbackSuccess = false; @@ -197,3 +197,203 @@ static int s_TestDeviceDefenderCustomMetricFail(Aws::Crt::Allocator *allocator, return AWS_ERROR_SUCCESS; } AWS_TEST_CASE(DeviceDefenderCustomMetricFail, s_TestDeviceDefenderCustomMetricFail) + +static int s_TestMqtt5DeviceDefenderCustomMetricSuccess(Aws::Crt::Allocator *allocator, void *ctx) +{ + (void)ctx; + { + Aws::Crt::ApiHandle apiHandle(allocator); + Aws::Iotdevicecommon::DeviceApiHandle deviceApiHandle(allocator); + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); + Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator); + Aws::Crt::Io::SocketOptions socketOptions; + socketOptions.SetConnectTimeoutMs(3000); + Aws::Crt::Io::EventLoopGroup eventLoopGroup(0, allocator); + Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator); + Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); + clientBootstrap.EnableBlockingShutdown(); + + Aws::Crt::Mqtt5::Mqtt5ClientOptions mqtt5Options(allocator); + mqtt5Options.WithBootstrap(&clientBootstrap); + mqtt5Options.WithSocketOptions(socketOptions); + mqtt5Options.WithTlsConnectionOptions(TlsContext.NewConnectionOptions()); + std::shared_ptr packetConnect = + std::make_shared(); + mqtt5Options.WithHostName("www.example.come"); + mqtt5Options.WithPort(443); + + std::shared_ptr mqtt5Client = Mqtt5::Mqtt5Client::NewMqtt5Client(mqtt5Options, allocator); + + const Aws::Crt::String thingName("TestThing"); + bool callbackSuccess = false; + + std::mutex mutex; + std::condition_variable cv; + bool taskStopped = false; + + auto onCancelled = [&](void *a) -> void + { + auto *data = reinterpret_cast(a); + *data = true; + taskStopped = true; + cv.notify_one(); + }; + + Aws::Iotdevicedefenderv1::ReportTaskBuilder taskBuilder(allocator, mqtt5Client, eventLoopGroup, thingName); + taskBuilder.WithTaskPeriodSeconds((uint32_t)1UL) + .WithNetworkConnectionSamplePeriodSeconds((uint32_t)1UL) + .WithTaskCancelledHandler(onCancelled) + .WithTaskCancellationUserData(&callbackSuccess); + + std::shared_ptr task = taskBuilder.Build(); + + // ================ + // Add the custom metrics + std::function local_metric_number_func = [](double *output) + { + *output = 10; + return AWS_OP_SUCCESS; + }; + task->RegisterCustomMetricNumber("CustomNumberOne", std::move(local_metric_number_func)); + std::function global_metric_number_func_ref = global_metric_number_func; + task->RegisterCustomMetricNumber("CustomNumberTwo", std::move(global_metric_number_func_ref)); + + std::function *)> local_metric_number_list_func = + [](Aws::Crt::Vector *output) + { + output->push_back(101); + output->push_back(102); + output->push_back(103); + return AWS_OP_SUCCESS; + }; + task->RegisterCustomMetricNumberList("CustomNumberList", std::move(local_metric_number_list_func)); + + std::function *)> local_metric_str_list_func = + [](Aws::Crt::Vector *output) + { + output->push_back("One Fish"); + output->push_back("Two Fish"); + output->push_back("Red Fish"); + output->push_back("Blue Fish"); + return AWS_OP_SUCCESS; + }; + task->RegisterCustomMetricStringList("CustomStringList", std::move(local_metric_str_list_func)); + + std::function *)> local_metric_ip_list_func = + [](Aws::Crt::Vector *output) + { + output->push_back("192.0.2.0"); + output->push_back("198.51.100.0"); + output->push_back("203.0.113.0"); + output->push_back("233.252.0.0"); + return AWS_OP_SUCCESS; + }; + task->RegisterCustomMetricIpAddressList("CustomIPList", std::move(local_metric_ip_list_func)); + + // ================ + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Ready, (int)task->GetStatus()); + + ASSERT_SUCCESS(task->StartTask()); + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Running, (int)task->GetStatus()); + ASSERT_FAILS(task->StartTask()); + ASSERT_TRUE(aws_last_error() == AWS_ERROR_INVALID_STATE); + task->StopTask(); + + ASSERT_TRUE(task->GetStatus() == Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped); + + { + std::unique_lock lock(mutex); + cv.wait(lock, [&]() { return taskStopped; }); + } + + ASSERT_TRUE(callbackSuccess); + + mqtt5Client->Stop(); + ASSERT_TRUE(mqtt5Client); + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus()); + } + + return AWS_ERROR_SUCCESS; +} +AWS_TEST_CASE(Mqtt5DeviceDefenderCustomMetricSuccess, s_TestMqtt5DeviceDefenderCustomMetricSuccess) + +static int s_TestMqtt5DeviceDefenderCustomMetricFail(Aws::Crt::Allocator *allocator, void *ctx) +{ + (void)ctx; + { + Aws::Crt::ApiHandle apiHandle(allocator); + Aws::Iotdevicecommon::DeviceApiHandle deviceApiHandle(allocator); + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); + Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator); + Aws::Crt::Io::SocketOptions socketOptions; + socketOptions.SetConnectTimeoutMs(3000); + Aws::Crt::Io::EventLoopGroup eventLoopGroup(0, allocator); + Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator); + Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); + clientBootstrap.EnableBlockingShutdown(); + + Aws::Crt::Mqtt5::Mqtt5ClientOptions mqtt5Options(allocator); + mqtt5Options.WithBootstrap(&clientBootstrap); + mqtt5Options.WithSocketOptions(socketOptions); + mqtt5Options.WithTlsConnectionOptions(TlsContext.NewConnectionOptions()); + std::shared_ptr packetConnect = + std::make_shared(); + mqtt5Options.WithHostName("www.example.come"); + mqtt5Options.WithPort(443); + + std::shared_ptr mqtt5Client = Mqtt5::Mqtt5Client::NewMqtt5Client(mqtt5Options, allocator); + + const Aws::Crt::String thingName("TestThing"); + bool callbackSuccess = false; + + std::mutex mutex; + std::condition_variable cv; + bool taskStopped = false; + + auto onCancelled = [&](void *a) -> void + { + auto *data = reinterpret_cast(a); + *data = true; + taskStopped = true; + cv.notify_one(); + }; + + Aws::Iotdevicedefenderv1::ReportTaskBuilder taskBuilder(allocator, mqtt5Client, eventLoopGroup, thingName); + taskBuilder.WithTaskPeriodSeconds((uint32_t)1UL) + .WithNetworkConnectionSamplePeriodSeconds((uint32_t)1UL) + .WithTaskCancelledHandler(onCancelled) + .WithTaskCancellationUserData(&callbackSuccess); + + std::shared_ptr task = taskBuilder.Build(); + + // Add the error custom metric + std::function number_metric_func = [](double *output) + { + *output = 10; + return AWS_OP_ERR; + }; + task->RegisterCustomMetricNumber("CustomNumberOne", std::move(number_metric_func)); + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Ready, (int)task->GetStatus()); + + task->StartTask(); + task->StopTask(); + + ASSERT_TRUE(task->GetStatus() == Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped); + { + std::unique_lock lock(mutex); + cv.wait(lock, [&]() { return taskStopped; }); + } + + ASSERT_TRUE(callbackSuccess); + + mqtt5Client->Stop(); + ASSERT_TRUE(mqtt5Client); + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus()); + } + return AWS_ERROR_SUCCESS; +} +AWS_TEST_CASE(Mqtt5DeviceDefenderCustomMetricFail, s_TestMqtt5DeviceDefenderCustomMetricFail) diff --git a/devicedefender/tests/DeviceDefenderTest.cpp b/devicedefender/tests/DeviceDefenderTest.cpp index 2c3e499b8..26dd59561 100644 --- a/devicedefender/tests/DeviceDefenderTest.cpp +++ b/devicedefender/tests/DeviceDefenderTest.cpp @@ -152,3 +152,147 @@ static int s_TestDeviceDefenderFailedTest(Aws::Crt::Allocator *allocator, void * } AWS_TEST_CASE(DeviceDefenderFailedTest, s_TestDeviceDefenderFailedTest) + +static int s_TestMqtt5DeviceDefenderResourceSafety(Aws::Crt::Allocator *allocator, void *ctx) +{ + (void)ctx; + { + Aws::Crt::ApiHandle apiHandle(allocator); + Aws::Iotdevicecommon::DeviceApiHandle deviceApiHandle(allocator); + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); + + Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator); + ASSERT_TRUE(tlsContext); + + Aws::Crt::Io::SocketOptions socketOptions; + socketOptions.SetConnectTimeoutMs(3000); + + Aws::Crt::Io::EventLoopGroup eventLoopGroup(0, allocator); + ASSERT_TRUE(eventLoopGroup); + + Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator); + ASSERT_TRUE(defaultHostResolver); + + Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); + ASSERT_TRUE(allocator); + clientBootstrap.EnableBlockingShutdown(); + + Aws::Crt::Mqtt5::Mqtt5ClientOptions mqtt5Options(allocator); + mqtt5Options.WithBootstrap(&clientBootstrap); + mqtt5Options.WithSocketOptions(socketOptions); + mqtt5Options.WithTlsConnectionOptions(TlsContext.NewConnectionOptions()); + std::shared_ptr packetConnect = + std::make_shared(); + mqtt5Options.WithHostName("www.example.come"); + mqtt5Options.WithPort(443); + + const Aws::Crt::String thingName("TestThing"); + bool callbackSuccess = false; + + std::mutex mutex; + std::condition_variable cv; + bool taskStopped = false; + + auto onCancelled = [&](void *a) -> void + { + auto *data = reinterpret_cast(a); + *data = true; + taskStopped = true; + cv.notify_one(); + }; + + Aws::Iotdevicedefenderv1::ReportTaskBuilder taskBuilder(allocator, mqtt5Client, eventLoopGroup, thingName); + taskBuilder.WithTaskPeriodSeconds((uint32_t)1UL) + .WithNetworkConnectionSamplePeriodSeconds((uint32_t)1UL) + .WithTaskCancelledHandler(onCancelled) + .WithTaskCancellationUserData(&callbackSuccess); + + std::shared_ptr task = taskBuilder.Build(); + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Ready, (int)task->GetStatus()); + + ASSERT_SUCCESS(task->StartTask()); + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Running, (int)task->GetStatus()); + ASSERT_FAILS(task->StartTask()); + ASSERT_TRUE(aws_last_error() == AWS_ERROR_INVALID_STATE); + task->StopTask(); + + ASSERT_TRUE(task->GetStatus() == Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped); + + { + std::unique_lock lock(mutex); + cv.wait(lock, [&]() { return taskStopped; }); + } + + ASSERT_TRUE(callbackSuccess); + + mqtt5Client->Stop(); + ASSERT_TRUE(mqtt5Client); + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus()); + } + + return AWS_ERROR_SUCCESS; +} + +AWS_TEST_CASE(Mqtt5DeviceDefenderResourceSafety, s_TestMqtt5DeviceDefenderResourceSafety) + +static int s_TestMqtt5DeviceDefenderFailedTest(Aws::Crt::Allocator *allocator, void *ctx) +{ + (void)ctx; + { + Aws::Crt::ApiHandle apiHandle(allocator); + Aws::Iotdevicecommon::DeviceApiHandle deviceApiHandle(allocator); + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(); + + Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator); + ASSERT_TRUE(tlsContext); + + Aws::Crt::Io::SocketOptions socketOptions; + socketOptions.SetConnectTimeoutMs(3000); + + Aws::Crt::Io::EventLoopGroup eventLoopGroup(0, allocator); + ASSERT_TRUE(eventLoopGroup); + + Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator); + ASSERT_TRUE(defaultHostResolver); + + Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator); + ASSERT_TRUE(allocator); + clientBootstrap.EnableBlockingShutdown(); + + Aws::Crt::Mqtt5::Mqtt5ClientOptions mqtt5Options(allocator); + mqtt5Options.WithBootstrap(&clientBootstrap); + mqtt5Options.WithSocketOptions(socketOptions); + mqtt5Options.WithTlsConnectionOptions(TlsContext.NewConnectionOptions()); + std::shared_ptr packetConnect = + std::make_shared(); + mqtt5Options.WithHostName("www.example.come"); + mqtt5Options.WithPort(443); + + std::shared_ptr mqtt5Client = Mqtt5::Mqtt5Client::NewMqtt5Client(mqtt5Options, allocator); + + const Aws::Crt::String thingName("TestThing"); + Aws::Crt::String data("TestData"); + + Aws::Iotdevicedefenderv1::ReportTaskBuilder taskBuilder(allocator, mqtt5Client, eventLoopGroup, thingName); + taskBuilder.WithTaskPeriodSeconds((uint32_t)1UL) + .WithTaskPeriodSeconds((uint32_t)1UL) + .WithReportFormat(Aws::Iotdevicedefenderv1::ReportFormat::AWS_IDDRF_SHORT_JSON); + + std::shared_ptr task = taskBuilder.Build(); + + ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Ready, (int)task->GetStatus()); + + ASSERT_INT_EQUALS(AWS_ERROR_IOTDEVICE_DEFENDER_UNSUPPORTED_REPORT_FORMAT, task->LastError()); + ASSERT_FAILS(task->StartTask()); + ASSERT_TRUE(aws_last_error() == AWS_ERROR_INVALID_STATE); + + mqtt5Client->Stop(); + ASSERT_TRUE(mqtt5Client); + } + + return AWS_ERROR_SUCCESS; +} + +AWS_TEST_CASE(Mqtt5DeviceDefenderFailedTest, s_TestMqtt5DeviceDefenderFailedTest)