Skip to content

Commit

Permalink
Merge pull request #179 from c-jimenez/fix/url_encoding
Browse files Browse the repository at this point in the history
[websockets] Encode URL when they contains non-ascii or special chars
  • Loading branch information
c-jimenez authored Feb 9, 2024
2 parents f474c39 + dc4c638 commit 8ea4218
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 10 deletions.
54 changes: 52 additions & 2 deletions src/websockets/Url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ along with OpenOCPP. If not, see <http://www.gnu.org/licenses/>.

#include "Url.h"

#include <iomanip>
#include <regex>
#include <sstream>

namespace ocpp
{
Expand Down Expand Up @@ -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
Expand All @@ -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<int>(c);
}
}

return encoded_url.str();
}

} // namespace websockets
} // namespace ocpp
3 changes: 3 additions & 0 deletions src/websockets/Url.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 56 additions & 8 deletions tests/websockets/test_websockets_url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,21 @@ 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")
{
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(), "");
Expand All @@ -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(), "");
Expand All @@ -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(), "");
Expand All @@ -76,37 +80,55 @@ 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")
{
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")
{
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#");
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -153,19 +177,21 @@ 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")
{
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(), "");
Expand All @@ -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(), "");
Expand All @@ -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(), "");
Expand All @@ -200,37 +228,55 @@ 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")
{
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")
{
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#");
Expand All @@ -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());
Expand All @@ -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());
Expand Down

0 comments on commit 8ea4218

Please sign in to comment.