From 60d2c5bbe19b7cb3cf7b3c39dbcb541324519caf Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 8 Oct 2016 06:41:18 +0100 Subject: Use a single implementation for parsing www form url encoded data in post and query string --- icespider/fcgi/cgiRequestBase.cpp | 31 +++-- icespider/fcgi/cgiRequestBase.h | 3 +- icespider/fcgi/xwwwFormUrlEncoded.cpp | 222 +++++++++++++++++++--------------- icespider/fcgi/xwwwFormUrlEncoded.h | 34 ++++++ icespider/unittests/testFcgi.cpp | 9 ++ 5 files changed, 183 insertions(+), 116 deletions(-) create mode 100644 icespider/fcgi/xwwwFormUrlEncoded.h diff --git a/icespider/fcgi/cgiRequestBase.cpp b/icespider/fcgi/cgiRequestBase.cpp index c36e11c..efa9468 100644 --- a/icespider/fcgi/cgiRequestBase.cpp +++ b/icespider/fcgi/cgiRequestBase.cpp @@ -1,4 +1,5 @@ #include "cgiRequestBase.h" +#include "xwwwFormUrlEncoded.h" #include #include #include @@ -40,21 +41,9 @@ namespace IceSpider { auto qs = envmap.find("QUERY_STRING"); if (qs != envmap.end()) { - auto start = std::get<0>(qs->second); - auto end = std::get<1>(qs->second); - while (start < end) { - auto amp = orelse(strchr(start, '&'), end); - auto eq = orelse(strchr(start, '='), end); - *amp = '\0'; - if (eq < amp) { - *eq = '\0'; - qsmap.insert({ start, Env( eq + 1, amp ) }); - } - else { - qsmap.insert({ start, Env( eq + 1, eq + 1 ) }); - } - start = amp + 1; - } + XWwwFormUrlEncoded::iterateVars(std::string(std::get<0>(qs->second), std::get<1>(qs->second)), [this](auto k, auto v) { + qsmap.insert({ k, v }); + }); } } @@ -63,11 +52,21 @@ namespace IceSpider { { auto i = vm.find(key.c_str()); if (i == vm.end()) { - return IceUtil::Optional(); + return IceUtil::None; } return std::string(std::get<0>(i->second), std::get<1>(i->second)); } + OptionalString + CgiRequestBase::optionalLookup(const std::string & key, const StringMap & vm) + { + auto i = vm.find(key.c_str()); + if (i == vm.end()) { + return IceUtil::None; + } + return i->second; + } + const PathElements & CgiRequestBase::getRequestPath() const { diff --git a/icespider/fcgi/cgiRequestBase.h b/icespider/fcgi/cgiRequestBase.h index 97d956a..20d7d05 100644 --- a/icespider/fcgi/cgiRequestBase.h +++ b/icespider/fcgi/cgiRequestBase.h @@ -29,9 +29,10 @@ namespace IceSpider { private: static OptionalString optionalLookup(const std::string & key, const VarMap &); + static OptionalString optionalLookup(const std::string & key, const StringMap &); VarMap envmap; - VarMap qsmap; + StringMap qsmap; PathElements pathElements; }; } diff --git a/icespider/fcgi/xwwwFormUrlEncoded.cpp b/icespider/fcgi/xwwwFormUrlEncoded.cpp index a2ca9ee..2f60367 100644 --- a/icespider/fcgi/xwwwFormUrlEncoded.cpp +++ b/icespider/fcgi/xwwwFormUrlEncoded.cpp @@ -1,6 +1,5 @@ -#include +#include "xwwwFormUrlEncoded.h" #include -#include #include #include @@ -10,116 +9,141 @@ extern long hextable[]; namespace IceSpider { - class XWwwFormUrlEncoded : public Slicer::Deserializer { + XWwwFormUrlEncoded::XWwwFormUrlEncoded(std::istream & in) : + input(std::istreambuf_iterator(in), std::istreambuf_iterator()) + { + } + + void + XWwwFormUrlEncoded::Deserialize(Slicer::ModelPartForRootPtr mp) + { + mp->Create(); + mp->OnEachChild([this](auto, auto mp, auto) { + switch (mp->GetType()) { + case Slicer::mpt_Simple: + this->DeserializeSimple(mp); + break; + case Slicer::mpt_Complex: + this->DeserializeComplex(mp); + break; + case Slicer::mpt_Dictionary: + this->DeserializeDictionary(mp); + break; + default: + throw IceSpider::Http400_BadRequest(); + break; + } + }); + mp->Complete(); + } + + class SetFromString : public Slicer::ValueSource { public: - XWwwFormUrlEncoded(std::istream & in) : - input(std::istreambuf_iterator(in), std::istreambuf_iterator()) + SetFromString(const std::string & v) : s(v) { } - void Deserialize(Slicer::ModelPartForRootPtr mp) override + void set(bool & t) const override { - mp->Create(); - mp->OnEachChild([this](auto, auto mp, auto) { - switch (mp->GetType()) { - case Slicer::mpt_Simple: - this->DeserializeSimple(mp); - break; - case Slicer::mpt_Complex: - this->DeserializeComplex(mp); - break; - case Slicer::mpt_Dictionary: - this->DeserializeDictionary(mp); - break; - default: - throw IceSpider::Http400_BadRequest(); - break; - } - }); - mp->Complete(); + if (s == "true") t = true; + else if (s == "false") t = false; + else throw Http400_BadRequest(); } - private: - class SetFromString : public Slicer::ValueSource { - public: - SetFromString(const std::string & v) : s(v) - { - } - - void set(bool & t) const override - { - if (s == "true") t = true; - else if (s == "false") t = false; - else throw Http400_BadRequest(); - } - - void set(std::string & t) const override - { - t.reserve(s.size()); - for (auto i = s.begin(); i != s.end(); i++) { - if (*i == '+') t += ' '; - else if (*i == '%') { - t += (16 * hextable[(int)*++i]) + hextable[(int)*++i]; - } - else t += *i; - } - } + void set(std::string & t) const override + { + t = s; + } #define SET(T) \ - void set(T & t) const override \ - { \ - t = boost::lexical_cast(s); \ - } - - SET(Ice::Byte); - SET(Ice::Short); - SET(Ice::Int); - SET(Ice::Long); - SET(Ice::Float); - SET(Ice::Double); - const std::string & s; - }; - - typedef boost::function KVh; - void iterateVars(const KVh & h) - { - for (auto pi = ba::make_split_iterator(input, ba::first_finder("&", ba::is_equal())); pi != decltype(pi)(); ++pi) { - auto eq = std::find(pi->begin(), pi->end(), '='); - if (eq == pi->end()) { - h(std::string(pi->begin(), pi->end()), std::string()); - } - else { - h(std::string(pi->begin(), eq), std::string(eq + 1, pi->end())); - } - } + void set(T & t) const override \ + { \ + t = boost::lexical_cast(s); \ } - void DeserializeSimple(Slicer::ModelPartPtr mp) - { - iterateVars([mp](auto, auto v) { - mp->SetValue(new SetFromString(v)); - }); + + SET(Ice::Byte); + SET(Ice::Short); + SET(Ice::Int); + SET(Ice::Long); + SET(Ice::Float); + SET(Ice::Double); + const std::string & s; + }; + + std::string + XWwwFormUrlEncoded::urldecode(std::string::const_iterator i, std::string::const_iterator e) + { + std::string t; + t.reserve(&*e - &*i); + while (i != e) { + if (*i == '+') t += ' '; + else if (*i == '%') { + t += (16 * hextable[(int)*++i]) + hextable[(int)*++i]; } - void DeserializeComplex(Slicer::ModelPartPtr mp) - { - mp->Create(); - iterateVars([mp](auto k, auto v) { - if (auto m = mp->GetChild(k)) { - m->SetValue(new SetFromString(v)); - } - }); - mp->Complete(); + else t += *i; + ++i; + } + return t; + } + + void + XWwwFormUrlEncoded::iterateVars(const KVh & h, ba::split_iterator pi) + { + for (; pi != decltype(pi)(); ++pi) { + auto eq = std::find(pi->begin(), pi->end(), '='); + if (eq == pi->end()) { + h(urldecode(pi->begin(), pi->end()), std::string()); } - void DeserializeDictionary(Slicer::ModelPartPtr mp) - { - iterateVars([mp](auto k, auto v) { - auto p = mp->GetAnonChild(); - p->GetChild("key")->SetValue(new SetFromString(k)); - p->GetChild("value")->SetValue(new SetFromString(v)); - p->Complete(); - }); + else { + h(urldecode(pi->begin(), eq), urldecode(eq + 1, pi->end())); } - const std::string input; - }; + } + } + + void + XWwwFormUrlEncoded::iterateVars(const std::string & input, const KVh & h) + { + if (!input.empty()) { + iterateVars(h, ba::make_split_iterator(input, ba::first_finder("&", ba::is_equal()))); + } + } + + void + XWwwFormUrlEncoded::iterateVars(const KVh & h) + { + iterateVars(input, h); + } + + void + XWwwFormUrlEncoded::DeserializeSimple(Slicer::ModelPartPtr mp) + { + iterateVars([mp](auto, auto v) { + mp->SetValue(new SetFromString(v)); + }); + } + + void + XWwwFormUrlEncoded::DeserializeComplex(Slicer::ModelPartPtr mp) + { + mp->Create(); + iterateVars([mp](auto k, auto v) { + if (auto m = mp->GetChild(k)) { + m->SetValue(new SetFromString(v)); + } + }); + mp->Complete(); + } + + void + XWwwFormUrlEncoded::DeserializeDictionary(Slicer::ModelPartPtr mp) + { + iterateVars([mp](auto k, auto v) { + auto p = mp->GetAnonChild(); + p->GetChild("key")->SetValue(new SetFromString(k)); + p->GetChild("value")->SetValue(new SetFromString(v)); + p->Complete(); + }); + } } NAMEDFACTORY("application/x-www-form-urlencoded", IceSpider::XWwwFormUrlEncoded, Slicer::StreamDeserializerFactory); diff --git a/icespider/fcgi/xwwwFormUrlEncoded.h b/icespider/fcgi/xwwwFormUrlEncoded.h new file mode 100644 index 0000000..fe6aa00 --- /dev/null +++ b/icespider/fcgi/xwwwFormUrlEncoded.h @@ -0,0 +1,34 @@ +#ifndef ICESPIDER_CGI_XWWWFORMURLENCODED_H +#define ICESPIDER_CGI_XWWWFORMURLENCODED_H + +#include +#include + +namespace IceSpider { + class XWwwFormUrlEncoded : public Slicer::Deserializer { + public: + typedef boost::function KVh; + + XWwwFormUrlEncoded(std::istream & in); + + void Deserialize(Slicer::ModelPartForRootPtr mp) override; + static inline void iterateVars(const std::string & input, const KVh & h); + + private: + + static inline void iterateVars(const KVh & h, boost::algorithm::split_iterator pi); + static std::string urldecode(std::string::const_iterator s, std::string::const_iterator); + + void iterateVars(const KVh & h); + + void DeserializeSimple(Slicer::ModelPartPtr mp); + void DeserializeComplex(Slicer::ModelPartPtr mp); + void DeserializeDictionary(Slicer::ModelPartPtr mp); + + const std::string input; + }; + +}; + +#endif + diff --git a/icespider/unittests/testFcgi.cpp b/icespider/unittests/testFcgi.cpp index 94ce092..923c28d 100644 --- a/icespider/unittests/testFcgi.cpp +++ b/icespider/unittests/testFcgi.cpp @@ -151,6 +151,15 @@ BOOST_AUTO_TEST_CASE( query_string_three ) BOOST_REQUIRE_EQUAL("3", *r.getQueryStringParam("three")); } +BOOST_AUTO_TEST_CASE( query_string_urlencoding ) +{ + CharPtrPtrArray env ({ "SCRIPT_NAME=/foo/bar", "QUERY_STRING=url+%65ncoded=%53tring%2e" }); + TestRequest r(this, env); + BOOST_REQUIRE(!r.getQueryStringParam("")); + BOOST_REQUIRE(r.getQueryStringParam("url encoded")); + BOOST_REQUIRE_EQUAL("String.", *r.getQueryStringParam("url encoded")); +} + BOOST_AUTO_TEST_CASE( query_string_three_emptyVal ) { CharPtrPtrArray env ({ "SCRIPT_NAME=/foo/bar", "QUERY_STRING=one=1&two=&three=3" }); -- cgit v1.2.3