mirror of
https://github.com/espressif/esp-mqtt.git
synced 2026-06-05 21:04:46 +00:00
32df7e27fc
Add support of percent-encoded characters when passing the username and password in URI Closes https://github.com/espressif/esp-mqtt/issues/294
188 lines
7.6 KiB
C++
188 lines
7.6 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <algorithm>
|
|
#include <exception>
|
|
#include <memory>
|
|
#include <net/if.h>
|
|
#include <random>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
#include "esp_transport.h"
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers.hpp>
|
|
#include <rapidcheck.h>
|
|
|
|
#include "mqtt_client.h"
|
|
#include "test_log_intercept.hpp"
|
|
#include "test_log_matchers.hpp"
|
|
extern "C" {
|
|
#include "esp_log.h"
|
|
#include "Mockesp_event.h"
|
|
#include "Mockesp_transport.h"
|
|
#include "Mockesp_transport_ssl.h"
|
|
#include "Mockesp_transport_tcp.h"
|
|
#include "Mockesp_transport_ws.h"
|
|
#include "Mockevent_groups.h"
|
|
#include "Mockqueue.h"
|
|
#include "Mocktask.h"
|
|
#if __has_include ("Mockidf_additions.h")
|
|
/* Some functions were moved from "task.h" to "idf_additions.h" */
|
|
#include "Mockidf_additions.h"
|
|
#endif
|
|
#include "Mockesp_timer.h"
|
|
/*
|
|
* The following functions are not directly called but the generation of them
|
|
* from cmock is broken, so we need to define them here.
|
|
*/
|
|
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
|
|
{
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
|
|
auto random_string(std::size_t n)
|
|
{
|
|
static constexpr std::string_view char_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456790";
|
|
std::string str;
|
|
std::sample(char_set.begin(), char_set.end(), std::back_inserter(str), n,
|
|
std::mt19937 {std::random_device{}()});
|
|
return str;
|
|
}
|
|
|
|
using unique_mqtt_client = std::unique_ptr < std::remove_pointer_t<esp_mqtt_client_handle_t>,
|
|
decltype([](esp_mqtt_client_handle_t client)
|
|
{
|
|
esp_mqtt_client_destroy(client);
|
|
}) >;
|
|
|
|
SCENARIO("MQTT Client Operation")
|
|
{
|
|
// Set expectations for the mocked calls.
|
|
int mtx = 0;
|
|
int transport_list = 0;
|
|
int transport = 0;
|
|
int event_group = 0;
|
|
esp_timer_get_time_IgnoreAndReturn(0);
|
|
xQueueTakeMutexRecursive_IgnoreAndReturn(true);
|
|
xQueueGiveMutexRecursive_IgnoreAndReturn(true);
|
|
xQueueCreateMutex_ExpectAnyArgsAndReturn(
|
|
reinterpret_cast<QueueHandle_t>(&mtx));
|
|
xEventGroupCreate_IgnoreAndReturn(reinterpret_cast<EventGroupHandle_t>(&event_group));
|
|
esp_transport_list_init_IgnoreAndReturn(reinterpret_cast<esp_transport_list_handle_t>(&transport_list));
|
|
esp_transport_tcp_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
|
esp_transport_ssl_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
|
esp_transport_ws_init_IgnoreAndReturn(reinterpret_cast<esp_transport_handle_t>(&transport));
|
|
esp_transport_ws_set_subprotocol_IgnoreAndReturn(ESP_OK);
|
|
esp_transport_list_add_IgnoreAndReturn(ESP_OK);
|
|
esp_transport_set_default_port_IgnoreAndReturn(ESP_OK);
|
|
esp_event_loop_create_IgnoreAndReturn(ESP_OK);
|
|
esp_transport_list_destroy_IgnoreAndReturn(ESP_OK);
|
|
esp_transport_destroy_IgnoreAndReturn(ESP_OK);
|
|
vEventGroupDelete_Ignore();
|
|
vQueueDelete_Ignore();
|
|
GIVEN("An a minimal config") {
|
|
esp_mqtt_client_config_t config{};
|
|
config.broker.address.uri = "mqtt://1.1.1.1";
|
|
xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE);
|
|
SECTION("Client with minimal config") {
|
|
auto client = unique_mqtt_client{esp_mqtt_client_init(&config)};
|
|
REQUIRE(client != nullptr);
|
|
SECTION("User will set a new uri") {
|
|
SECTION("User set a correct URI") {
|
|
auto res = esp_mqtt_client_set_uri(client.get(), "mqtt://example.com/valid");
|
|
REQUIRE(res == ESP_OK);
|
|
}
|
|
SECTION("Incorrect URI from user") {
|
|
auto res = esp_mqtt_client_set_uri(client.get(), "invalid string that is not a url");
|
|
REQUIRE(res == ESP_FAIL);
|
|
}
|
|
}
|
|
SECTION("Any well-formed URI is accepted") {
|
|
static constexpr std::array<const char *, 4> schemes = {
|
|
"mqtt", "mqtts", "ws", "wss"
|
|
};
|
|
auto host_char = rc::gen::element('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
|
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
|
|
's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
|
|
rc::check("esp_mqtt_client_set_uri accepts well-formed URIs",
|
|
[&] {
|
|
auto scheme = *rc::gen::elementOf(schemes);
|
|
auto host = *rc::gen::container<std::string>(
|
|
*rc::gen::inRange<int>(1, 33), host_char).as("host");
|
|
auto path = *rc::gen::container<std::string>(
|
|
*rc::gen::inRange<int>(0, 17), host_char).as("path");
|
|
auto port = *rc::gen::maybe(rc::gen::inRange<uint16_t>(1, 65535)).as("port");
|
|
std::string uri = scheme;
|
|
uri += "://";
|
|
uri += host;
|
|
|
|
if (port) {
|
|
uri += ":";
|
|
uri += std::to_string(*port);
|
|
}
|
|
|
|
if (!path.empty())
|
|
{
|
|
uri += "/";
|
|
uri += path;
|
|
}
|
|
|
|
RC_ASSERT(esp_mqtt_client_set_uri(client.get(), uri.c_str()) == ESP_OK);
|
|
});
|
|
}
|
|
SECTION("User set interface to use") {
|
|
struct ifreq if_name = {};
|
|
strncpy(if_name.ifr_name, "custom", IFNAMSIZ - 1);
|
|
if_name.ifr_name[IFNAMSIZ - 1] = '\0';;
|
|
config.network.if_name = &if_name;
|
|
SECTION("Client is not started") {
|
|
REQUIRE(esp_mqtt_set_config(client.get(), &config) == ESP_OK);
|
|
}
|
|
}
|
|
SECTION("After Start Client Is Cleanly destroyed") {
|
|
esp_log_level_set("mqtt_client", ESP_LOG_DEBUG);
|
|
test::esp_log::Capture log;
|
|
REQUIRE(esp_mqtt_client_start(client.get()) == ESP_OK);
|
|
using namespace test::esp_log::matchers;
|
|
REQUIRE_THAT(log, HasMessageIn("mqtt_client", "Core selection"));
|
|
// Only need to start the client, destroy is called automatically at the end of
|
|
// scope
|
|
}
|
|
}
|
|
SECTION("Client with all allocating configuration set") {
|
|
auto host = random_string(20);
|
|
auto path = random_string(10);
|
|
auto username = random_string(10);
|
|
auto client_id = random_string(10);
|
|
auto password = random_string(10);
|
|
auto lw_topic = random_string(10);
|
|
auto lw_msg = random_string(10);
|
|
config.broker = {.address = {
|
|
.hostname = host.data(),
|
|
.path = path.data()
|
|
}
|
|
};
|
|
config.credentials = {
|
|
.username = username.data(),
|
|
.client_id = client_id.data(),
|
|
.authentication = {
|
|
.password = password.data()
|
|
}
|
|
};
|
|
config.session = {
|
|
.last_will {
|
|
.topic = lw_topic.data(),
|
|
.msg = lw_msg.data()
|
|
}
|
|
};
|
|
auto client = unique_mqtt_client{esp_mqtt_client_init(&config)};
|
|
REQUIRE(client != nullptr);
|
|
}
|
|
}
|
|
}
|