From 2f778d2b858361d4e2c9aeff88a184878eebdaa6 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 12 Oct 2016 01:22:23 +0100 Subject: Move XwwwFormUrlEncoded code into core from fcgi. Add functions for encoding strings as well as just decoding them. Add wrapper functions for setting cookies. --- icespider/core/Jamfile.jam | 2 +- icespider/core/hextable.c | 13 +++ icespider/core/ihttpRequest.cpp | 29 ++++++ icespider/core/ihttpRequest.h | 5 + icespider/core/xwwwFormUrlEncoded.cpp | 180 ++++++++++++++++++++++++++++++++++ icespider/core/xwwwFormUrlEncoded.h | 37 +++++++ icespider/fcgi/Jamfile.jam | 2 +- icespider/fcgi/hextable.c | 13 --- icespider/fcgi/xwwwFormUrlEncoded.cpp | 150 ---------------------------- icespider/fcgi/xwwwFormUrlEncoded.h | 34 ------- icespider/unittests/Jamfile.jam | 2 - icespider/unittests/testFcgi.cpp | 7 +- 12 files changed, 271 insertions(+), 203 deletions(-) create mode 100644 icespider/core/hextable.c create mode 100644 icespider/core/xwwwFormUrlEncoded.cpp create mode 100644 icespider/core/xwwwFormUrlEncoded.h delete mode 100644 icespider/fcgi/hextable.c delete mode 100644 icespider/fcgi/xwwwFormUrlEncoded.cpp delete mode 100644 icespider/fcgi/xwwwFormUrlEncoded.h diff --git a/icespider/core/Jamfile.jam b/icespider/core/Jamfile.jam index a0df519..b5712ad 100644 --- a/icespider/core/Jamfile.jam +++ b/icespider/core/Jamfile.jam @@ -4,7 +4,7 @@ lib boost_system ; lib boost_filesystem ; lib icespider-core : - [ glob-tree *.cpp : bin ] + [ glob-tree *.c *.cpp : bin ] : ../common//icespider-common adhocutil diff --git a/icespider/core/hextable.c b/icespider/core/hextable.c new file mode 100644 index 0000000..3086d49 --- /dev/null +++ b/icespider/core/hextable.c @@ -0,0 +1,13 @@ +// GCC doesn't support this sort of initialization in C++, only plain C. +// https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Designated-Inits.html + +const long hextable[] = { + [0 ... '0' - 1] = -1, + ['9' + 1 ... 'A' - 1] = -1, + ['G' ... 'a' - 1] = -1, + ['g' ... 255] = -1, + ['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + ['A'] = 10, 11, 12, 13, 14, 15, + ['a'] = 10, 11, 12, 13, 14, 15 +}; + diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp index 6f68f5f..9142b1b 100644 --- a/icespider/core/ihttpRequest.cpp +++ b/icespider/core/ihttpRequest.cpp @@ -2,7 +2,9 @@ #include "irouteHandler.h" #include "util.h" #include "exceptions.h" +#include "xwwwFormUrlEncoded.h" #include +#include namespace IceSpider { IHttpRequest::IHttpRequest(const Core * c) : @@ -93,6 +95,29 @@ namespace IceSpider { } } + // Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure] + // Sat, 02 May 2009 23:38:25 GMT + void IHttpRequest::setCookie(const std::string & name, const std::string & value, + const IceUtil::Optional & d, const IceUtil::Optional & p, bool s, + IceUtil::Optional e) + { + auto & o = getOutputStream(); + o << "Set-Cookie: " << XWwwFormUrlEncoded::urlencode(name) << + '=' << XWwwFormUrlEncoded::urlencode(value); + if (e) { + char buf[45]; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + gmtime_r(&*e, &tm); + auto l = strftime(buf, sizeof(buf), "; expires=%a, %d %b %Y %T %Z", &tm); + o.write(buf, l); + } + if (d) o << "; domain=" << *d; + if (p) o << "; path=" << *p; + if (s) o << "; secure"; + o << "\r\n"; + } + template inline IceUtil::Optional optionalLexicalCast(const IceUtil::Optional & p) { @@ -109,6 +134,10 @@ namespace IceSpider { #define getParams(T) \ + template<> void IHttpRequest::setCookie(const std::string & n, const T & v, \ + const IceUtil::Optional & d, const IceUtil::Optional & p, \ + bool s, IceUtil::Optional e) { \ + setCookie(n, boost::lexical_cast(v), d, p, s, e); } \ template<> T IHttpRequest::getURLParam(unsigned int idx) const { \ return wrapLexicalCast(getURLParam(idx)); } \ template<> IceUtil::Optional IHttpRequest::getQueryStringParam(const std::string & key) const { \ diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h index 92435f4..6ead437 100644 --- a/icespider/core/ihttpRequest.h +++ b/icespider/core/ihttpRequest.h @@ -57,6 +57,11 @@ namespace IceSpider { return boost::lexical_cast(i->second); } } + void setCookie(const std::string &, const std::string &, + const IceUtil::Optional &, const IceUtil::Optional &, bool, + IceUtil::Optional); + template + void setCookie(const std::string &, const T &, const IceUtil::Optional &, const IceUtil::Optional &, bool, IceUtil::Optional); template IceUtil::Optional getQueryStringParam(const std::string & key) const; template diff --git a/icespider/core/xwwwFormUrlEncoded.cpp b/icespider/core/xwwwFormUrlEncoded.cpp new file mode 100644 index 0000000..8d48107 --- /dev/null +++ b/icespider/core/xwwwFormUrlEncoded.cpp @@ -0,0 +1,180 @@ +#include "xwwwFormUrlEncoded.h" +#include "exceptions.h" +#include +#include + +namespace ba = boost::algorithm; + +extern long hextable[]; + +namespace IceSpider { + + 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: + 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 = 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; + }; + + std::string + XWwwFormUrlEncoded::urlencode(const std::string & s) + { + return urlencode(s.begin(), s.end()); + } + + inline char hexchar(char c) { return c < 10 ? '0' + c : 'a' - 10 + c; } + + std::string + XWwwFormUrlEncoded::urlencode(std::string::const_iterator i, std::string::const_iterator e) + { + std::string t; + t.reserve(std::distance(i, e)); + while (i != e) { + if (*i == ' ') { + t += '+'; + } + else if (!isalnum(*i)) { + t += '%'; + t += hexchar(*i >> 4); + t += hexchar(*i & 0xf); + } + else { + t += *i; + } + ++i; + } + return t; + } + + std::string + XWwwFormUrlEncoded::urldecode(std::string::const_iterator i, std::string::const_iterator e) + { + std::string t; + t.reserve(std::distance(i, e)); + while (i != e) { + if (*i == '+') t += ' '; + else if (*i == '%') { + t += (16 * hextable[(int)*++i]) + hextable[(int)*++i]; + } + 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()); + } + else { + h(urldecode(pi->begin(), eq), urldecode(eq + 1, pi->end())); + } + } + } + + void + XWwwFormUrlEncoded::iterateVars(const std::string & input, const KVh & h, const std::string & split) + { + if (!input.empty()) { + iterateVars(h, ba::make_split_iterator(input, ba::first_finder(split, 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/core/xwwwFormUrlEncoded.h b/icespider/core/xwwwFormUrlEncoded.h new file mode 100644 index 0000000..4f6cdae --- /dev/null +++ b/icespider/core/xwwwFormUrlEncoded.h @@ -0,0 +1,37 @@ +#ifndef ICESPIDER_CGI_XWWWFORMURLENCODED_H +#define ICESPIDER_CGI_XWWWFORMURLENCODED_H + +#include +#include +#include + +namespace IceSpider { + class XWwwFormUrlEncoded : public Slicer::Deserializer { + public: + typedef boost::function KVh; + + XWwwFormUrlEncoded(std::istream & in); + + void Deserialize(Slicer::ModelPartForRootPtr mp) override; + DLL_PUBLIC static void iterateVars(const std::string & input, const KVh & h, const std::string & split); + + DLL_PUBLIC static std::string urldecode(std::string::const_iterator s, std::string::const_iterator); + DLL_PUBLIC static std::string urlencode(std::string::const_iterator s, std::string::const_iterator); + DLL_PUBLIC static std::string urlencode(const std::string & s); + + private: + static inline void iterateVars(const KVh & h, boost::algorithm::split_iterator pi); + + 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/fcgi/Jamfile.jam b/icespider/fcgi/Jamfile.jam index c6c4ca0..c607ed1 100644 --- a/icespider/fcgi/Jamfile.jam +++ b/icespider/fcgi/Jamfile.jam @@ -2,7 +2,7 @@ lib fcgi : : fcgi ; lib fcgi++ : : fcgi++ ; lib icespider-fcgi : - [ glob-tree *.c *.cpp : bin ] + [ glob-tree *.cpp : bin ] : fcgi fcgi++ diff --git a/icespider/fcgi/hextable.c b/icespider/fcgi/hextable.c deleted file mode 100644 index 3086d49..0000000 --- a/icespider/fcgi/hextable.c +++ /dev/null @@ -1,13 +0,0 @@ -// GCC doesn't support this sort of initialization in C++, only plain C. -// https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Designated-Inits.html - -const long hextable[] = { - [0 ... '0' - 1] = -1, - ['9' + 1 ... 'A' - 1] = -1, - ['G' ... 'a' - 1] = -1, - ['g' ... 255] = -1, - ['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - ['A'] = 10, 11, 12, 13, 14, 15, - ['a'] = 10, 11, 12, 13, 14, 15 -}; - diff --git a/icespider/fcgi/xwwwFormUrlEncoded.cpp b/icespider/fcgi/xwwwFormUrlEncoded.cpp deleted file mode 100644 index 345a597..0000000 --- a/icespider/fcgi/xwwwFormUrlEncoded.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "xwwwFormUrlEncoded.h" -#include -#include -#include - -namespace ba = boost::algorithm; - -extern long hextable[]; - -namespace IceSpider { - - 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: - 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 = 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; - }; - - 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]; - } - 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()); - } - else { - h(urldecode(pi->begin(), eq), urldecode(eq + 1, pi->end())); - } - } - } - - void - XWwwFormUrlEncoded::iterateVars(const std::string & input, const KVh & h, const std::string & split) - { - if (!input.empty()) { - iterateVars(h, ba::make_split_iterator(input, ba::first_finder(split, 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 deleted file mode 100644 index 54205e3..0000000 --- a/icespider/fcgi/xwwwFormUrlEncoded.h +++ /dev/null @@ -1,34 +0,0 @@ -#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 void iterateVars(const std::string & input, const KVh & h, const std::string & split); - - 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/Jamfile.jam b/icespider/unittests/Jamfile.jam index ba7e9c2..513f41f 100644 --- a/icespider/unittests/Jamfile.jam +++ b/icespider/unittests/Jamfile.jam @@ -84,8 +84,6 @@ run testFcgi.cpp test-fcgi.ice ../fcgi/cgiRequestBase.cpp - ../fcgi/xwwwFormUrlEncoded.cpp - ../fcgi/hextable.c : : : yes BOOST_TEST_DYN_LINK diff --git a/icespider/unittests/testFcgi.cpp b/icespider/unittests/testFcgi.cpp index 2c93c32..bf8e442 100644 --- a/icespider/unittests/testFcgi.cpp +++ b/icespider/unittests/testFcgi.cpp @@ -13,17 +13,18 @@ class TestRequest : public IceSpider::CgiRequestBase { initialize(); } - // LCOV_EXCL_START we never actually read or write anything here std::ostream & getOutputStream() const override { - return std::cout; + return out; } + // LCOV_EXCL_START we never actually read or write anything here std::istream & getInputStream() const override { return std::cin; } // LCOV_EXCL_STOP + mutable std::stringstream out; }; class TestPayloadRequest : public TestRequest { @@ -271,6 +272,8 @@ BOOST_AUTO_TEST_CASE( cookies ) BOOST_REQUIRE_EQUAL(1234, *r.IceSpider::IHttpRequest::getCookieParam("valueA")); BOOST_REQUIRE_EQUAL("Something with spaces.", *r.IceSpider::IHttpRequest::getCookieParam("value B")); BOOST_REQUIRE(!r.IceSpider::IHttpRequest::getCookieParam("notAThing")); + r.setCookie("some int.", 1234, "www.com", "/dir", true, 1476142378); + BOOST_REQUIRE_EQUAL("Set-Cookie: some+int%2e=1234; expires=Mon, 10 Oct 2016 23:32:58 GMT; domain=www.com; path=/dir; secure\r\n", r.out.str()); } BOOST_AUTO_TEST_SUITE_END(); -- cgit v1.2.3