From 3a905339bc65d7aab592c342340b5c483d6ef7cd Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:05:00 +0100 Subject: [PATCH] [websockets] Encode charge point identifier in URL --- src/chargepoint/ChargePoint.cpp | 5 +- .../centralsystem/CentralSystemProxy.cpp | 3 +- src/websockets/Url.cpp | 62 +++++++++---------- src/websockets/Url.h | 6 +- tests/websockets/test_websockets_url.cpp | 36 ++--------- 5 files changed, 42 insertions(+), 70 deletions(-) diff --git a/src/chargepoint/ChargePoint.cpp b/src/chargepoint/ChargePoint.cpp index 0aa05710..7e1b9241 100644 --- a/src/chargepoint/ChargePoint.cpp +++ b/src/chargepoint/ChargePoint.cpp @@ -35,6 +35,7 @@ along with OpenOCPP. If not, see . #include "TimerPool.h" #include "TransactionManager.h" #include "TriggerMessageManager.h" +#include "Url.h" #include "Version.h" #include "WebsocketFactory.h" #include "WorkerThreadPool.h" @@ -1142,7 +1143,7 @@ bool ChargePoint::doConnect() { connection_url += "/"; } - connection_url += m_stack_config.chargePointIdentifier(); + connection_url += ocpp::websockets::Url::encode(m_stack_config.chargePointIdentifier()); // Check if URL has changed since last connection std::string last_url; @@ -1166,7 +1167,7 @@ bool ChargePoint::doConnect() { auto authentication_key = ocpp::helpers::fromHexString(authorization_key); credentials.user = m_stack_config.chargePointIdentifier(); - credentials.password = std::string(reinterpret_cast(authentication_key.data()), authorization_key.size()); + credentials.password = std::string(reinterpret_cast(authentication_key.data()), authentication_key.size()); credentials.password.resize(authentication_key.size()); } if (security_profile != 1) diff --git a/src/localcontroller/centralsystem/CentralSystemProxy.cpp b/src/localcontroller/centralsystem/CentralSystemProxy.cpp index ab2ecf39..d036bd7a 100644 --- a/src/localcontroller/centralsystem/CentralSystemProxy.cpp +++ b/src/localcontroller/centralsystem/CentralSystemProxy.cpp @@ -18,6 +18,7 @@ along with OpenOCPP. If not, see . #include "CentralSystemProxy.h" #include "ILocalControllerConfig.h" +#include "Url.h" #include "WebsocketFactory.h" #include @@ -86,7 +87,7 @@ bool CentralSystemProxy::connect(const std::string& { full_url << "/"; } - full_url << m_identifier; + full_url << ocpp::websockets::Url::encode(m_identifier); // Connect ret = m_rpc.start(full_url.str(), credentials, connect_timeout, retry_interval, ping_interval); diff --git a/src/websockets/Url.cpp b/src/websockets/Url.cpp index 07ac8a4a..a8265793 100644 --- a/src/websockets/Url.cpp +++ b/src/websockets/Url.cpp @@ -48,10 +48,6 @@ Url::Url(const std::string& url) // Convert path m_path = match[10].str(); - if (!m_path.empty()) - { - m_path = encode(m_path); - } // Convert port std::string sport = match[9].str(); @@ -70,55 +66,53 @@ Url::Url(const std::string& url) m_is_valid = false; } } - - // Rebuild URL - if (m_is_valid) - { - std::stringstream encoded_url; - encoded_url << m_protocol << "://"; - if (!m_username.empty() || !m_password.empty()) - { - encoded_url << m_username; - if (!m_password.empty()) - { - encoded_url << ":" << m_password; - } - encoded_url << "@"; - } - encoded_url << m_address; - if (m_port != 0) - { - encoded_url << ":" << m_port; - } - encoded_url << m_path; - m_url = encoded_url.str(); - } } } /** @brief Destructor */ Url::~Url() { } -/** @brief Encode an URL */ -std::string Url::encode(const std::string& url) const +/** @brief Encode a part of an URL using RFC3986 percent encoding */ +std::string Url::encode(const std::string& url) { + // RFC3986 : Un reserved chars which must not be encoded + static const char unreserved_char[] = {'-', '_', '.', '~'}; + std::stringstream encoded_url; - encoded_url << std::hex; + encoded_url << std::uppercase << std::hex; for (const auto& c : url) { - // Safe characters - if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '/')) + // RFC3986 : Only alphanumeric and unreserved chars may be used + // unencoded within a URL + bool encode = true; + if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9'))) { - // No encoding - encoded_url << c; + encode = false; } else + { + for (size_t i = 0; i < sizeof(unreserved_char) / sizeof(char); i++) + { + if (c == unreserved_char[i]) + { + encode = false; + break; + } + } + } + + if (encode) { // Percent encoding encoded_url << '%'; encoded_url << std::setw(2) << std::setfill('0') << static_cast(c); } + else + { + // No encoding + encoded_url << c; + } } return encoded_url.str(); diff --git a/src/websockets/Url.h b/src/websockets/Url.h index 44657218..3a350a3b 100644 --- a/src/websockets/Url.h +++ b/src/websockets/Url.h @@ -96,6 +96,9 @@ class Url */ const std::string& path() const { return m_path; } + /** @brief Encode a part of an URL using RFC3986 percent encoding */ + static std::string encode(const std::string& url); + private: /** @brief Full URL */ std::string m_url; @@ -113,9 +116,6 @@ class Url unsigned int m_port; /** @brief Path part of the URL */ std::string m_path; - - /** @brief Encode an URL */ - std::string encode(const std::string& url) const; }; } // namespace websockets diff --git a/tests/websockets/test_websockets_url.cpp b/tests/websockets/test_websockets_url.cpp index be4c7bb7..e69f618f 100644 --- a/tests/websockets/test_websockets_url.cpp +++ b/tests/websockets/test_websockets_url.cpp @@ -80,21 +80,6 @@ TEST_SUITE("Nominal - hostname as string") CHECK_EQ(url.path(), "/paf/pouf/"); } - TEST_CASE("URL with port and encoded path") - { - Url url("ftp://pif.com:12345/paf [ pouf / + BIM_bam) = boum ] 10.11.12.13!"); - - CHECK(url.isValid()); - CHECK_EQ(url.url(), - R"(ftp://pif.com:12345/paf%20%5b%20pouf%20/%20%20%2b%20BIM%5fbam%29%20%3d%20boum%20%5d%2010%2e11%2e12%2e13%21)"); - CHECK_EQ(url.protocol(), "ftp"); - CHECK_EQ(url.username(), ""); - CHECK_EQ(url.password(), ""); - CHECK_EQ(url.address(), "pif.com"); - CHECK_EQ(url.port(), 12345); - CHECK_EQ(url.path(), R"(/paf%20%5b%20pouf%20/%20%20%2b%20BIM%5fbam%29%20%3d%20boum%20%5d%2010%2e11%2e12%2e13%21)"); - } - TEST_CASE("URL with username and port") { Url url("ftp://yip76-84@pif.com:12345"); @@ -228,21 +213,6 @@ TEST_SUITE("Nominal - hostname as IP address") CHECK_EQ(url.path(), "/paf/pouf/"); } - TEST_CASE("URL with port and encoded path") - { - Url url("ftp://10.189.70.3:12345/paf [ pouf / + BIM_bam) = boum ] 10.11.12.13!"); - - CHECK(url.isValid()); - CHECK_EQ(url.url(), - R"(ftp://10.189.70.3:12345/paf%20%5b%20pouf%20/%20%20%2b%20BIM%5fbam%29%20%3d%20boum%20%5d%2010%2e11%2e12%2e13%21)"); - CHECK_EQ(url.protocol(), "ftp"); - CHECK_EQ(url.username(), ""); - CHECK_EQ(url.password(), ""); - CHECK_EQ(url.address(), "10.189.70.3"); - CHECK_EQ(url.port(), 12345); - CHECK_EQ(url.path(), R"(/paf%20%5b%20pouf%20/%20%20%2b%20BIM%5fbam%29%20%3d%20boum%20%5d%2010%2e11%2e12%2e13%21)"); - } - TEST_CASE("URL with username and port") { Url url("ftp://yip76-84@10.189.70.3:12345"); @@ -316,6 +286,12 @@ TEST_SUITE("Nominal - hostname as IP address") CHECK_EQ(url1.port(), url2.port()); CHECK_EQ(url1.path(), url2.path()); } + + TEST_CASE("URL percent encoding") + { + std::string url("paf [ pouf / + BIM_bam) = boum ] 10.11.12.13!"); + CHECK_EQ(Url::encode(url), R"(paf%20%5B%20pouf%20%2F%20%20%2B%20BIM_bam%29%20%3D%20boum%20%5D%2010.11.12.13%21)"); + } } TEST_SUITE("Errors")