From 2414dee60207230bfaac2d9df3565001d72051df Mon Sep 17 00:00:00 2001 From: Jean Flach Date: Wed, 29 Jul 2015 13:09:42 +0200 Subject: [PATCH] Update url parser fixes #9768 --- lib/remote/httputility.cpp | 4 +- lib/remote/url-characters.hpp | 2 +- lib/remote/url.cpp | 127 ++++++++++++++-------------------- lib/remote/url.hpp | 8 ++- test/remote-url.cpp | 14 ++-- 5 files changed, 68 insertions(+), 87 deletions(-) diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index 86ddef21f..a0f0d5f26 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -40,9 +40,9 @@ Dictionary::Ptr HttpUtility::FetchRequestParameters(HttpRequest& request) if (!result) result = new Dictionary(); - typedef std::pair kv_pair; + typedef std::pair > kv_pair; BOOST_FOREACH(const kv_pair& kv, request.RequestUrl->GetQuery()) { - result->Set(kv.first, kv.second); + result->Set(kv.first, Array::FromVector(kv.second)); } return result; diff --git a/lib/remote/url-characters.hpp b/lib/remote/url-characters.hpp index a497655c4..ab2c97939 100644 --- a/lib/remote/url-characters.hpp +++ b/lib/remote/url-characters.hpp @@ -22,7 +22,7 @@ #define ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" #define NUMERIC "0123456789" -#define UNRESERVED ALPHA NUMERIC "-._~" +#define UNRESERVED ALPHA NUMERIC "-._~" "%" #define GEN_DELIMS ":/?#[]@" #define SUB_DELIMS "!$&'()*+,;=" #define RESERVED GEN_DELIMS SUB-DELIMS diff --git a/lib/remote/url.cpp b/lib/remote/url.cpp index 581de9bef..e908d05e7 100644 --- a/lib/remote/url.cpp +++ b/lib/remote/url.cpp @@ -36,9 +36,7 @@ Url::Url(const String& base_url) size_t pHelper = url.Find(":"); - if (pHelper == String::NPos) { - m_Scheme = ""; - } else { + if (pHelper != String::NPos) { if (!ParseScheme(url.SubStr(0, pHelper))) BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid URL Scheme.")); url = url.SubStr(pHelper + 1); @@ -51,9 +49,7 @@ Url::Url(const String& base_url) return; } - if (*(url.Begin() + 1) != '/') - m_Authority = ""; - else { + if (*(url.Begin() + 1) == '/') { pHelper = url.Find("/", 2); if (pHelper == String::NPos) @@ -105,21 +101,32 @@ const std::vector& Url::GetPath(void) const return m_Path; } -const std::map& Url::GetQuery(void) const +const std::map >& Url::GetQuery(void) const { return m_Query; } -Value Url::GetQueryElement(const String& name) const +String Url::GetQueryElement(const String& name) const { - std::map::const_iterator it = m_Query.find(name); + std::map >::const_iterator it = m_Query.find(name); if (it == m_Query.end()) - return Empty; + return String(); - return it->second; + return it->second.back(); } +const std::vector& Url::GetQueryElements(const String& name) const +{ + std::map >::const_iterator it = m_Query.find(name); + + if (it == m_Query.end()) { + static std::vector emptyVector; + return emptyVector; + } + + return it->second; +} String Url::GetFragment(void) const { @@ -128,7 +135,7 @@ String Url::GetFragment(void) const String Url::Format(void) const { - String url = ""; + String url; if (!m_Scheme.IsEmpty()) url += m_Scheme + ":"; @@ -145,9 +152,9 @@ String Url::Format(void) const } } - String param = ""; + String param; if (!m_Query.empty()) { - typedef std::pair kv_pair; + typedef std::pair > kv_pair; BOOST_FOREACH (const kv_pair& kv, m_Query) { String key = Utility::EscapeString(kv.first, ACQUERY, false); @@ -155,28 +162,15 @@ String Url::Format(void) const param = "?"; else param += "&"; + + String temp; + BOOST_FOREACH (const String s, kv.second) { + if (!temp.IsEmpty()) + temp += "&"; - Value val = kv.second; - - if (val.IsEmpty()) - param += key; - else { - if (val.IsObjectType()) { - Array::Ptr arr = val; - String temp = ""; - - ObjectLock olock(arr); - BOOST_FOREACH (const String& sArrIn, arr) { - if (!temp.IsEmpty()) - temp += "&"; - - temp += key + "[]=" + Utility::EscapeString(sArrIn, ACQUERY, false); - } - - param += temp; - } else - param += key + "=" + Utility::EscapeString(kv.second, ACQUERY, false); + temp += key + "[]=" + Utility::EscapeString(s, ACQUERY, false); } + param += temp; } } @@ -200,9 +194,9 @@ bool Url::ParseScheme(const String& scheme) bool Url::ParseAuthority(const String& authority) { - //TODO parse all Authorities m_Authority = authority.SubStr(2); - return (ValidateToken(m_Authority, ACHOST)); + //Just safe the Authority and don't care about the details + return (ValidateToken(m_Authority, ACHOST GEN_DELIMS)); } bool Url::ParsePath(const String& path) @@ -236,56 +230,41 @@ bool Url::ParseQuery(const String& query) BOOST_FOREACH(const String& token, tokens) { size_t pHelper = token.Find("="); + if (pHelper == 0) + // /?foo=bar&=bar == invalid + return false; + String key = token.SubStr(0, pHelper); String value = Empty; - if (pHelper != String::NPos) { - if (pHelper == token.GetLength() - 1) - return false; - - value = token.SubStr(pHelper + 1); - if (!ValidateToken(value, ACQUERY)) - return false; - else - value = Utility::UnescapeString(value); - } else - String key = token; + if (pHelper != token.GetLength()-1) + value = token.SubStr(pHelper+1); - if (key.IsEmpty()) + if (!ValidateToken(value, ACQUERY)) return false; + + value = Utility::UnescapeString(value); pHelper = key.Find("[]"); - if (pHelper != String::NPos) { - - if (key.GetLength() < 3) - return false; - - key = key.SubStr(0, key.GetLength() - 2); - key = Utility::UnescapeString(key); + if (pHelper == 0 || (pHelper != String::NPos && pHelper != key.GetLength()-2)) + return false; - if (!ValidateToken(value, ACQUERY)) - return false; + key = key.SubStr(0, pHelper); + + if (!ValidateToken(key, ACQUERY)) + return false; - std::map::iterator it = m_Query.find(key); + key = Utility::UnescapeString(key); - if (it == m_Query.end()) { - Array::Ptr tmp = new Array(); - tmp->Add(Utility::UnescapeString(value)); - m_Query[key] = tmp; - } else if (m_Query[key].IsObjectType()){ - Array::Ptr arr = it->second; - arr->Add(Utility::UnescapeString(value)); - } else - return false; - } else { - key = Utility::UnescapeString(key); + std::map >::iterator it = m_Query.find(key); - if (m_Query.find(key) == m_Query.end() && ValidateToken(key, ACQUERY)) - m_Query[key] = Utility::UnescapeString(value); - else - return false; - } + if (it == m_Query.end()) { + m_Query[key] = std::vector(); + m_Query[key].push_back(value); + } else + m_Query[key].push_back(value); + } return true; diff --git a/lib/remote/url.hpp b/lib/remote/url.hpp index 35bc63dde..f51444568 100644 --- a/lib/remote/url.hpp +++ b/lib/remote/url.hpp @@ -23,6 +23,7 @@ #include "remote/i2-remote.hpp" #include "base/object.hpp" #include "base/string.hpp" +#include "base/array.hpp" #include "base/value.hpp" #include #include @@ -47,15 +48,16 @@ public: String GetScheme(void) const; String GetAuthority(void) const; const std::vector& GetPath(void) const; - const std::map& GetQuery(void) const; - Value GetQueryElement(const String& name) const; + const std::map >& GetQuery(void) const; + String GetQueryElement(const String& name) const; + const std::vector& GetQueryElements(const String& name) const; String GetFragment(void) const; private: String m_Scheme; String m_Authority; std::vector m_Path; - std::map m_Query; + std::map > m_Query; String m_Fragment; bool ParseScheme(const String& scheme); diff --git a/test/remote-url.cpp b/test/remote-url.cpp index 158df6dbb..1dcc8696c 100644 --- a/test/remote-url.cpp +++ b/test/remote-url.cpp @@ -48,10 +48,9 @@ BOOST_AUTO_TEST_CASE(parameters) BOOST_CHECK(url->GetQueryElement("rair") == "robert"); BOOST_CHECK(url->GetQueryElement("rain") == "karl"); - BOOST_CHECK(url->GetQueryElement("foo").IsObjectType()); - Array::Ptr test = url->GetQueryElement("foo"); - BOOST_CHECK(test->GetLength() == 1); - BOOST_CHECK(test->Get(0) == "bar"); + std::vector test = url->GetQueryElements("foo"); + BOOST_CHECK(test.size() == 1); + BOOST_CHECK(test[0] == "bar"); } BOOST_AUTO_TEST_CASE(format) @@ -71,12 +70,13 @@ BOOST_AUTO_TEST_CASE(format) BOOST_AUTO_TEST_CASE(illegal_legal_strings) { - BOOST_CHECK_THROW(new Url("/?foo=barr&foo[]=bazz"), std::invalid_argument); + BOOST_CHECK(new Url("/?foo=barr&foo[]=bazz")); BOOST_CHECK_THROW(new Url("/?]=gar"), std::invalid_argument); BOOST_CHECK_THROW(new Url("/#?[]"), std::invalid_argument); - BOOST_CHECK_THROW(new Url("/?foo=bar&foo=ba"), std::invalid_argument); + BOOST_CHECK(new Url("/?foo=bar&foo=ba")); BOOST_CHECK_THROW(new Url("/?foo=bar&[]=d"), std::invalid_argument); - BOOST_CHECK_THROW(new Url("/?fo=&bar=garOA"), std::invalid_argument); + BOOST_CHECK(new Url("/?fo=&bar=garOA")); + BOOST_CHECK(new Url("https://127.0.0.1:5665/demo?type=Service&filter=service.state%3E0")); BOOST_CHECK(new Url("/?foo=baz??&\?\?=/?")); BOOST_CHECK(new Url("/")); BOOST_CHECK(new Url("///////")); -- 2.40.0