From dc4c63807e549c07a5d69b929f43c1f807f7bba2 Mon Sep 17 00:00:00 2001 From: c-jimenez <18682655+c-jimenez@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:46:32 +0100 Subject: [PATCH] [websockets] Encode URL when they contains non-ascii or specical chars --- src/websockets/Url.cpp | 54 +++++++++++++++++++- src/websockets/Url.h | 3 ++ tests/websockets/test_websockets_url.cpp | 64 +++++++++++++++++++++--- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/src/websockets/Url.cpp b/src/websockets/Url.cpp index f1cc5d3e..07ac8a4a 100644 --- a/src/websockets/Url.cpp +++ b/src/websockets/Url.cpp @@ -18,7 +18,9 @@ along with OpenOCPP. If not, see . #include "Url.h" +#include #include +#include namespace ocpp { @@ -46,9 +48,9 @@ Url::Url(const std::string& url) // Convert path m_path = match[10].str(); - if (m_path.empty()) + if (!m_path.empty()) { - m_path = "/"; + m_path = encode(m_path); } // Convert port @@ -68,11 +70,59 @@ 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 +{ + std::stringstream encoded_url; + encoded_url << std::hex; + + for (const auto& c : url) + { + // Safe characters + if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) || (c == '/')) + { + // No encoding + encoded_url << c; + } + else + { + // Percent encoding + encoded_url << '%'; + encoded_url << std::setw(2) << std::setfill('0') << static_cast(c); + } + } + + return encoded_url.str(); +} + } // namespace websockets } // namespace ocpp diff --git a/src/websockets/Url.h b/src/websockets/Url.h index 0b6dc8b8..44657218 100644 --- a/src/websockets/Url.h +++ b/src/websockets/Url.h @@ -113,6 +113,9 @@ 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 a173a072..be4c7bb7 100644 --- a/tests/websockets/test_websockets_url.cpp +++ b/tests/websockets/test_websockets_url.cpp @@ -29,12 +29,13 @@ TEST_SUITE("Nominal - hostname as string") Url url("ftp://pif.com"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://pif.com"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); CHECK_EQ(url.address(), "pif.com"); CHECK_EQ(url.port(), 0); - CHECK_EQ(url.path(), "/"); + CHECK_EQ(url.path(), ""); } TEST_CASE("Short URL with port") @@ -42,6 +43,7 @@ TEST_SUITE("Nominal - hostname as string") Url url("ftp://pif.com:12345/"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://pif.com:12345/"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); @@ -50,11 +52,12 @@ TEST_SUITE("Nominal - hostname as string") CHECK_EQ(url.path(), "/"); } - TEST_CASE("URL wth path") + TEST_CASE("URL with path") { Url url("ftp://pif.com/paf/pouf"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://pif.com/paf/pouf"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); @@ -68,6 +71,7 @@ TEST_SUITE("Nominal - hostname as string") Url url("ftp://pif.com:12345/paf/pouf/"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://pif.com:12345/paf/pouf/"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); @@ -76,17 +80,33 @@ 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"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://yip76-84@pif.com:12345"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), "yip76-84"); CHECK_EQ(url.password(), ""); CHECK_EQ(url.address(), "pif.com"); CHECK_EQ(url.port(), 12345); - CHECK_EQ(url.path(), "/"); + CHECK_EQ(url.path(), ""); } TEST_CASE("URL with username, password and port") @@ -94,12 +114,13 @@ TEST_SUITE("Nominal - hostname as string") Url url("ftp://yip76-84:£uiU*^gh#@pif.com:12345"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://yip76-84:£uiU*^gh#@pif.com:12345"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), "yip76-84"); CHECK_EQ(url.password(), "£uiU*^gh#"); CHECK_EQ(url.address(), "pif.com"); CHECK_EQ(url.port(), 12345); - CHECK_EQ(url.path(), "/"); + CHECK_EQ(url.path(), ""); } TEST_CASE("URL with username, password, port and path") @@ -107,6 +128,7 @@ TEST_SUITE("Nominal - hostname as string") Url url("ftp://yip76-84:£uiU*^gh#@pif.com:12345/paf/pouf/"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://yip76-84:£uiU*^gh#@pif.com:12345/paf/pouf/"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), "yip76-84"); CHECK_EQ(url.password(), "£uiU*^gh#"); @@ -121,6 +143,7 @@ TEST_SUITE("Nominal - hostname as string") Url url2(url1); CHECK_EQ(url1.isValid(), url2.isValid()); + CHECK_EQ(url1.url(), url2.url()); CHECK_EQ(url1.protocol(), url2.protocol()); CHECK_EQ(url1.username(), url2.username()); CHECK_EQ(url1.password(), url2.password()); @@ -137,6 +160,7 @@ TEST_SUITE("Nominal - hostname as string") url2 = url1; CHECK_EQ(url1.isValid(), url2.isValid()); + CHECK_EQ(url1.url(), url2.url()); CHECK_EQ(url1.protocol(), url2.protocol()); CHECK_EQ(url1.username(), url2.username()); CHECK_EQ(url1.password(), url2.password()); @@ -153,12 +177,13 @@ TEST_SUITE("Nominal - hostname as IP address") Url url("ftp://10.189.70.3"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://10.189.70.3"); 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(), 0); - CHECK_EQ(url.path(), "/"); + CHECK_EQ(url.path(), ""); } TEST_CASE("Short URL with port") @@ -166,6 +191,7 @@ TEST_SUITE("Nominal - hostname as IP address") Url url("ftp://10.189.70.3:12345/"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://10.189.70.3:12345/"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); @@ -174,11 +200,12 @@ TEST_SUITE("Nominal - hostname as IP address") CHECK_EQ(url.path(), "/"); } - TEST_CASE("URL wth path") + TEST_CASE("URL with path") { Url url("ftp://10.189.70.3/paf/pouf"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://10.189.70.3/paf/pouf"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); @@ -192,6 +219,7 @@ TEST_SUITE("Nominal - hostname as IP address") Url url("ftp://10.189.70.3:12345/paf/pouf/"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://10.189.70.3:12345/paf/pouf/"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), ""); CHECK_EQ(url.password(), ""); @@ -200,17 +228,33 @@ 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"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://yip76-84@10.189.70.3:12345"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), "yip76-84"); CHECK_EQ(url.password(), ""); CHECK_EQ(url.address(), "10.189.70.3"); CHECK_EQ(url.port(), 12345); - CHECK_EQ(url.path(), "/"); + CHECK_EQ(url.path(), ""); } TEST_CASE("URL with username, password and port") @@ -218,12 +262,13 @@ TEST_SUITE("Nominal - hostname as IP address") Url url("ftp://yip76-84:£uiU*^gh#@10.189.70.3:12345"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://yip76-84:£uiU*^gh#@10.189.70.3:12345"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), "yip76-84"); CHECK_EQ(url.password(), "£uiU*^gh#"); CHECK_EQ(url.address(), "10.189.70.3"); CHECK_EQ(url.port(), 12345); - CHECK_EQ(url.path(), "/"); + CHECK_EQ(url.path(), ""); } TEST_CASE("URL with username, password, port and path") @@ -231,6 +276,7 @@ TEST_SUITE("Nominal - hostname as IP address") Url url("ftp://yip76-84:£uiU*^gh#@10.189.70.3:12345/paf/pouf/"); CHECK(url.isValid()); + CHECK_EQ(url.url(), "ftp://yip76-84:£uiU*^gh#@10.189.70.3:12345/paf/pouf/"); CHECK_EQ(url.protocol(), "ftp"); CHECK_EQ(url.username(), "yip76-84"); CHECK_EQ(url.password(), "£uiU*^gh#"); @@ -245,6 +291,7 @@ TEST_SUITE("Nominal - hostname as IP address") Url url2(url1); CHECK_EQ(url1.isValid(), url2.isValid()); + CHECK_EQ(url1.url(), url2.url()); CHECK_EQ(url1.protocol(), url2.protocol()); CHECK_EQ(url1.username(), url2.username()); CHECK_EQ(url1.password(), url2.password()); @@ -261,6 +308,7 @@ TEST_SUITE("Nominal - hostname as IP address") url2 = url1; CHECK_EQ(url1.isValid(), url2.isValid()); + CHECK_EQ(url1.url(), url2.url()); CHECK_EQ(url1.protocol(), url2.protocol()); CHECK_EQ(url1.username(), url2.username()); CHECK_EQ(url1.password(), url2.password());