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")