diff options
-rw-r--r-- | icespider/core/ihttpRequest.cpp | 46 | ||||
-rw-r--r-- | icespider/core/ihttpRequest.h | 26 | ||||
-rw-r--r-- | icespider/core/util.h | 9 | ||||
-rw-r--r-- | icespider/fcgi/cgiRequestBase.cpp | 53 | ||||
-rw-r--r-- | icespider/fcgi/cgiRequestBase.h | 1 | ||||
-rw-r--r-- | icespider/testing/testRequest.cpp | 2 | ||||
-rw-r--r-- | icespider/unittests/testFcgi.cpp | 25 |
7 files changed, 104 insertions, 58 deletions
diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp index ea94fb2..cef738d 100644 --- a/icespider/core/ihttpRequest.cpp +++ b/icespider/core/ihttpRequest.cpp @@ -25,7 +25,7 @@ namespace IceSpider { { try { return Slicer::StreamDeserializerFactory::createNew( - getEnv(E::CONTENT_TYPE) / []() -> std::string { + getEnv(E::CONTENT_TYPE) / []() -> std::string_view { throw Http400_BadRequest(); }, getInputStream()); } @@ -125,8 +125,8 @@ 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_view & name, const std::string_view & value, - const Ice::optional<std::string> & d, const Ice::optional<std::string> & p, bool s, - Ice::optional<time_t> e) + const OptionalString & d, const OptionalString & p, bool s, + std::optional<time_t> e) { std::stringstream o; o << XWwwFormUrlEncoded::urlencode(name) << @@ -146,13 +146,13 @@ namespace IceSpider { } template <typename T> - inline Ice::optional<T> optionalLexicalCast(const Ice::optional<std::string> & p) + inline std::optional<T> optionalLexicalCast(const OptionalString & p) { if (p) return wrapLexicalCast<T>(*p); - return Ice::optional<T>(); + return {}; } - void IHttpRequest::responseRedirect(const std::string_view & url, const Ice::optional<std::string> & statusMsg) const + void IHttpRequest::responseRedirect(const std::string_view & url, const OptionalString & statusMsg) const { setHeader(H::LOCATION, url); response(303, (statusMsg ? *statusMsg : S::MOVED)); @@ -167,29 +167,45 @@ namespace IceSpider { s.second->Serialize(mp); } + std::optional<std::string> + operator!(const std::optional<std::string_view> & sv) + { + // TODO: Fix this mess (only required if operation requires std::string) + if (sv) return { std::string(*sv) }; + return {}; + } + #define getParams(T) \ template<> void IHttpRequest::setCookie<T>(const std::string_view & n, const T & v, \ - const Ice::optional<std::string> & d, const Ice::optional<std::string> & p, \ - bool s, Ice::optional<time_t> e) { \ + const OptionalString & d, const OptionalString & p, \ + bool s, std::optional<time_t> e) { \ auto vs = boost::lexical_cast<std::string>(v); \ setCookie(n, std::string_view(vs), d, p, s, e); \ } \ template<> T IHttpRequest::getURLParam<T>(unsigned int idx) const { \ return wrapLexicalCast<T>(getURLParam(idx)); } \ - template<> Ice::optional<T> IHttpRequest::getQueryStringParam<T>(const std::string_view & key) const { \ + template<> std::optional<T> IHttpRequest::getQueryStringParam<T>(const std::string_view & key) const { \ return optionalLexicalCast<T>(getQueryStringParam(key)); } \ - template<> Ice::optional<T> IHttpRequest::getCookieParam<T>(const std::string_view & key) const { \ + template<> std::optional<T> IHttpRequest::getCookieParam<T>(const std::string_view & key) const { \ return optionalLexicalCast<T>(getCookieParam(key)); } \ - template<> Ice::optional<T> IHttpRequest::getHeaderParam<T>(const std::string_view & key) const { \ + template<> std::optional<T> IHttpRequest::getHeaderParam<T>(const std::string_view & key) const { \ return optionalLexicalCast<T>(getHeaderParam(key)); } - template<> std::string IHttpRequest::getURLParam<std::string>(unsigned int idx) const { + template<> std::string_view IHttpRequest::getURLParam<std::string_view>(unsigned int idx) const { return getURLParam(idx); } - template<> Ice::optional<std::string> IHttpRequest::getQueryStringParam<std::string>(const std::string_view & key) const { \ + template<> OptionalString IHttpRequest::getQueryStringParam<std::string_view>(const std::string_view & key) const { \ return getQueryStringParam(key); } - template<> Ice::optional<std::string> IHttpRequest::getCookieParam<std::string>(const std::string_view & key) const { \ + template<> OptionalString IHttpRequest::getCookieParam<std::string_view>(const std::string_view & key) const { \ return getCookieParam(key); } - template<> Ice::optional<std::string> IHttpRequest::getHeaderParam<std::string>(const std::string_view & key) const { \ + template<> OptionalString IHttpRequest::getHeaderParam<std::string_view>(const std::string_view & key) const { \ return getHeaderParam(key); } + template<> std::string IHttpRequest::getURLParam<std::string>(unsigned int idx) const { + return getURLParam(idx); } + template<> std::optional<std::string> IHttpRequest::getQueryStringParam<std::string>(const std::string_view & key) const { \ + return !getQueryStringParam(key); } + template<> std::optional<std::string> IHttpRequest::getCookieParam<std::string>(const std::string_view & key) const { \ + return !getCookieParam(key); } + template<> std::optional<std::string> IHttpRequest::getHeaderParam<std::string>(const std::string_view & key) const { \ + return !getHeaderParam(key); } getParams(bool); getParams(Ice::Short); diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h index fdc3b5d..aaa9d50 100644 --- a/icespider/core/ihttpRequest.h +++ b/icespider/core/ihttpRequest.h @@ -15,7 +15,7 @@ namespace IceSpider { class IRouteHandler; typedef std::vector<std::string> PathElements; - typedef Ice::optional<std::string> OptionalString; + typedef std::optional<std::string_view> OptionalString; typedef std::pair<MimeType, Slicer::SerializerPtr> ContentTypeSerializer; class DLL_PUBLIC IHttpRequest { @@ -44,37 +44,37 @@ namespace IceSpider { template<typename T> T getURLParam(unsigned int) const; template<typename T> - Ice::optional<T> getBody() const + std::optional<T> getBody() const { return Slicer::DeserializeAnyWith<T>(getDeserializer()); } template<typename T> - Ice::optional<T> getBodyParam(const Ice::optional<IceSpider::StringMap> & map, const std::string_view & key) const + std::optional<T> getBodyParam(const Ice::optional<IceSpider::StringMap> & map, const std::string_view & key) const { if (!map) { - return Ice::optional<T>(); + return {}; } auto i = map->find(key); if (i == map->end()) { - return Ice::optional<T>(); + return {}; } else { return boost::lexical_cast<T>(i->second); } } - void responseRedirect(const std::string_view & url, const Ice::optional<std::string> & = IceUtil::None) const; + void responseRedirect(const std::string_view & url, const OptionalString & = {}) const; void setCookie(const std::string_view &, const std::string_view &, - const Ice::optional<std::string> & = IceUtil::None, const Ice::optional<std::string> & = IceUtil::None, - bool = false, Ice::optional<time_t> = IceUtil::None); + const OptionalString & = {}, const OptionalString & = {}, + bool = false, std::optional<time_t> = {}); template<typename T> - void setCookie(const std::string_view &, const T &, const Ice::optional<std::string> & = IceUtil::None, - const Ice::optional<std::string> & = IceUtil::None, bool = false, Ice::optional<time_t> = IceUtil::None); + void setCookie(const std::string_view &, const T &, const OptionalString & = {}, + const OptionalString & = {}, bool = false, std::optional<time_t> = {}); template<typename T> - Ice::optional<T> getQueryStringParam(const std::string_view & key) const; + std::optional<T> getQueryStringParam(const std::string_view & key) const; template<typename T> - Ice::optional<T> getHeaderParam(const std::string_view & key) const; + std::optional<T> getHeaderParam(const std::string_view & key) const; template<typename T> - Ice::optional<T> getCookieParam(const std::string_view & key) const; + std::optional<T> getCookieParam(const std::string_view & key) const; virtual void response(short, const std::string_view &) const = 0; template<typename T> void response(const IRouteHandler * route, const T & t) const diff --git a/icespider/core/util.h b/icespider/core/util.h index 658a1cc..0edc3d2 100644 --- a/icespider/core/util.h +++ b/icespider/core/util.h @@ -13,6 +13,15 @@ namespace std::experimental::Ice { } } +namespace std { + template <typename T, typename TF> + auto operator/(const std::optional<T> & o, const TF & tf) -> decltype(tf()) + { + if (o) return *o; + return tf(); + } +} + template <typename T> T orelse(const T & a, const T & b) { diff --git a/icespider/fcgi/cgiRequestBase.cpp b/icespider/fcgi/cgiRequestBase.cpp index a42a78f..5bcb746 100644 --- a/icespider/fcgi/cgiRequestBase.cpp +++ b/icespider/fcgi/cgiRequestBase.cpp @@ -3,6 +3,7 @@ #include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/predicate.hpp> #include <util.h> #include <slicer/modelPartsTypes.h> #include <slicer/common.h> @@ -14,7 +15,9 @@ using namespace std::literals; #define CGI_CONST(NAME) static const std::string_view NAME(#NAME) namespace IceSpider { - static const std::string HEADER_PREFIX("HTTP_"); + static const std::string_view amp("&"); + static const std::string_view semi("; "); + static const std::string_view HEADER_PREFIX("HTTP_"); CGI_CONST(REDIRECT_URL); CGI_CONST(SCRIPT_NAME); CGI_CONST(QUERY_STRING); @@ -37,20 +40,13 @@ namespace IceSpider { } } - inline - std::string - operator*(const std::string_view & t) - { - return std::string(t); - } - template<typename in, typename out> inline void mapVars(const std::string_view & vn, const in & envmap, out & map, const std::string_view & sp) { auto qs = envmap.find(vn); if (qs != envmap.end()) { - XWwwFormUrlEncoded::iterateVars(qs->second, [&map](auto k, auto v) { + XWwwFormUrlEncoded::iterateVars(qs->second, [&map](const auto & k, const auto & v) { map.insert({ k, v }); }, sp); } @@ -58,18 +54,18 @@ namespace IceSpider { template<typename Ex, typename Map> const std::string_view & - findFirstOrElse(const Map &, const std::string & msg) + findFirstOrElse(const Map &) { - throw Ex(msg); + throw Ex(); } template<typename Ex, typename Map, typename ... Ks> const std::string_view & - findFirstOrElse(const Map & map, const std::string & msg, const std::string_view & k, const Ks & ... ks) + findFirstOrElse(const Map & map, const std::string_view & k, const Ks & ... ks) { auto i = map.find(k); if (i == map.end()) { - return findFirstOrElse<Ex>(map, msg, ks...); + return findFirstOrElse<Ex>(map, ks...); } return i->second; } @@ -78,15 +74,18 @@ namespace IceSpider { CgiRequestBase::initialize() { namespace ba = boost::algorithm; - auto path = findFirstOrElse<std::runtime_error>(envmap, "Couldn't determine request path"s, - REDIRECT_URL, - SCRIPT_NAME).substr(1); - if (!path.empty()) { + if (auto path = findFirstOrElse<Http400_BadRequest>(envmap, REDIRECT_URL, SCRIPT_NAME).substr(1); + !path.empty()) { ba::split(pathElements, path, ba::is_any_of("/"), ba::token_compress_off); } - mapVars(QUERY_STRING, envmap, qsmap, "&"sv); - mapVars(HTTP_COOKIE, envmap, cookiemap, "; "sv); + mapVars(QUERY_STRING, envmap, qsmap, amp); + mapVars(HTTP_COOKIE, envmap, cookiemap, semi); + for (auto header = envmap.lower_bound(HEADER_PREFIX); + header != envmap.end() && ba::starts_with(header->first, HEADER_PREFIX); + header++) { + hdrmap.insert({ header->first.substr(HEADER_PREFIX.length()), header->second }); + } } AdHocFormatter(VarFmt, "\t%?: [%?]\n"); @@ -119,9 +118,9 @@ namespace IceSpider { { auto i = vm.find(key); if (i == vm.end()) { - return IceUtil::None; + return {}; } - return *i->second; + return i->second; } OptionalString @@ -129,7 +128,7 @@ namespace IceSpider { { auto i = vm.find(key); if (i == vm.end()) { - return IceUtil::None; + return {}; } return i->second; } @@ -151,7 +150,10 @@ namespace IceSpider { { try { auto i = envmap.find(REQUEST_METHOD); - return Slicer::ModelPartForEnum<HttpMethod>::lookup(*i->second); + if (i == envmap.end()) { + throw IceSpider::Http400_BadRequest(); + } + return Slicer::ModelPartForEnum<HttpMethod>::lookup(i->second); } catch (const Slicer::InvalidEnumerationSymbol &) { throw IceSpider::Http405_MethodNotAllowed(); @@ -179,10 +181,7 @@ namespace IceSpider { OptionalString CgiRequestBase::getHeaderParam(const std::string_view & key) const { - // TODO: Fix this mess - std::string ks(key); - boost::algorithm::to_upper(ks); - return optionalLookup(HEADER_PREFIX + ks, envmap); + return optionalLookup(key, hdrmap); } void CgiRequestBase::response(short statusCode, const std::string_view & statusMsg) const diff --git a/icespider/fcgi/cgiRequestBase.h b/icespider/fcgi/cgiRequestBase.h index efe2746..0ceb201 100644 --- a/icespider/fcgi/cgiRequestBase.h +++ b/icespider/fcgi/cgiRequestBase.h @@ -37,6 +37,7 @@ namespace IceSpider { VarMap envmap; StringMap qsmap; StringMap cookiemap; + VarMap hdrmap; PathElements pathElements; }; } diff --git a/icespider/testing/testRequest.cpp b/icespider/testing/testRequest.cpp index 11fd721..1c0d9cb 100644 --- a/icespider/testing/testRequest.cpp +++ b/icespider/testing/testRequest.cpp @@ -62,7 +62,7 @@ namespace IceSpider { { auto i = vars.find(key); if (i == vars.end()) { - return IceUtil::None; + return {}; } return i->second; } diff --git a/icespider/unittests/testFcgi.cpp b/icespider/unittests/testFcgi.cpp index 6d3d7d5..60f2aa7 100644 --- a/icespider/unittests/testFcgi.cpp +++ b/icespider/unittests/testFcgi.cpp @@ -10,7 +10,7 @@ using namespace std::literals; namespace std { template<typename T> - ostream & operator<<(ostream & s, const Ice::optional<T> & o) { + ostream & operator<<(ostream & s, const std::optional<T> & o) { if (o) s << *o; return s; } @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE( NoEnvironment ) BOOST_REQUIRE_THROW({ CharPtrPtrArray env; TestRequest r(this, env); - }, std::runtime_error); + }, IceSpider::Http400_BadRequest); } BOOST_AUTO_TEST_CASE( script_name_root ) @@ -220,6 +220,27 @@ BOOST_AUTO_TEST_CASE( requestmethod_bad ) BOOST_REQUIRE_THROW(r.getRequestMethod(), IceSpider::Http405_MethodNotAllowed); } +BOOST_AUTO_TEST_CASE( requestmethod_missing ) +{ + CharPtrPtrArray env ({ "SCRIPT_NAME=/" }); + TestRequest r(this, env); + BOOST_REQUIRE_THROW(r.getRequestMethod(), IceSpider::Http400_BadRequest); +} + +BOOST_AUTO_TEST_CASE( acceptheader ) +{ + CharPtrPtrArray env ({ "SCRIPT_NAME=/", "HTTP_ACCEPT=text/html" }); + TestRequest r(this, env); + BOOST_REQUIRE_EQUAL("text/html", *r.getHeaderParam("ACCEPT")); +} + +BOOST_AUTO_TEST_CASE( missingheader ) +{ + CharPtrPtrArray env ({ "SCRIPT_NAME=/", "HTTP_ACCEPT=text/html" }); + TestRequest r(this, env); + BOOST_REQUIRE(!r.getHeaderParam("ACCEPT_LANGUAGE")); +} + BOOST_AUTO_TEST_CASE( postxwwwformurlencoded_simple ) { CharPtrPtrArray env ({ "SCRIPT_NAME=/", "REQUEST_METHOD=No", "CONTENT_TYPE=application/x-www-form-urlencoded" }); |