From ec5aef094cbcb9769c50a7e7a6a344036e01f67b Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 22 Jun 2016 22:49:23 +0100 Subject: Very basic, probably buggy, incomplete [f]cgi implementation --- icespider/core/Jamfile.jam | 2 + icespider/core/util.h | 8 ++++ icespider/fcgi/Jamfile.jam | 3 ++ icespider/fcgi/cgiRequest.cpp | 25 ++++++++++ icespider/fcgi/cgiRequest.h | 17 +++++++ icespider/fcgi/cgiRequestBase.cpp | 99 +++++++++++++++++++++++++++++++++++++++ icespider/fcgi/cgiRequestBase.h | 38 +++++++++++++++ icespider/fcgi/fcgiRequest.cpp | 26 ++++++++++ icespider/fcgi/fcgiRequest.h | 23 +++++++++ icespider/fcgi/main.cpp | 18 +++++-- icespider/unittests/Jamfile.jam | 10 ++++ icespider/unittests/testFcgi.cpp | 1 + 12 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 icespider/fcgi/cgiRequest.cpp create mode 100644 icespider/fcgi/cgiRequest.h create mode 100644 icespider/fcgi/cgiRequestBase.cpp create mode 100644 icespider/fcgi/cgiRequestBase.h create mode 100644 icespider/fcgi/fcgiRequest.cpp create mode 100644 icespider/fcgi/fcgiRequest.h create mode 100644 icespider/unittests/testFcgi.cpp diff --git a/icespider/core/Jamfile.jam b/icespider/core/Jamfile.jam index 6a99e84..5dbaf31 100644 --- a/icespider/core/Jamfile.jam +++ b/icespider/core/Jamfile.jam @@ -12,4 +12,6 @@ lib icespider-core : ../common : : . + ../common + ../common ; diff --git a/icespider/core/util.h b/icespider/core/util.h index 075f237..03a01a9 100644 --- a/icespider/core/util.h +++ b/icespider/core/util.h @@ -12,5 +12,13 @@ namespace IceUtil { } } +template +T orelse(const T & a, const T & b) +{ + if (a) return a; + return b; +} + + #endif diff --git a/icespider/fcgi/Jamfile.jam b/icespider/fcgi/Jamfile.jam index eb0c3f2..c607ed1 100644 --- a/icespider/fcgi/Jamfile.jam +++ b/icespider/fcgi/Jamfile.jam @@ -1,7 +1,10 @@ lib fcgi : : fcgi ; +lib fcgi++ : : fcgi++ ; lib icespider-fcgi : [ glob-tree *.cpp : bin ] : fcgi + fcgi++ + ..//core ; diff --git a/icespider/fcgi/cgiRequest.cpp b/icespider/fcgi/cgiRequest.cpp new file mode 100644 index 0000000..abc3bea --- /dev/null +++ b/icespider/fcgi/cgiRequest.cpp @@ -0,0 +1,25 @@ +#include "cgiRequest.h" + +namespace IceSpider { + CgiRequest::CgiRequest(IceSpider::Core * c, int argc, char ** argv, char ** env) : + CgiRequestBase(c, env) + { + for (; argc > 0;) { + addenv(argv[--argc]); + } + initialize(); + } + + std::istream & + CgiRequest::getInputStream() const + { + return std::cin; + } + + std::ostream & + CgiRequest::getOutputStream() const + { + return std::cout; + } +} + diff --git a/icespider/fcgi/cgiRequest.h b/icespider/fcgi/cgiRequest.h new file mode 100644 index 0000000..9af58bb --- /dev/null +++ b/icespider/fcgi/cgiRequest.h @@ -0,0 +1,17 @@ +#ifndef ICESPIDER_CGI_CGIREQUEST_H +#define ICESPIDER_CGI_CGIREQUEST_H + +#include "cgiRequestBase.h" + +namespace IceSpider { + class CgiRequest : public CgiRequestBase { + public: + CgiRequest(IceSpider::Core * c, int argc, char ** argv, char ** env); + + std::istream & getInputStream() const override; + std::ostream & getOutputStream() const override; + }; +} + +#endif + diff --git a/icespider/fcgi/cgiRequestBase.cpp b/icespider/fcgi/cgiRequestBase.cpp new file mode 100644 index 0000000..97a71fc --- /dev/null +++ b/icespider/fcgi/cgiRequestBase.cpp @@ -0,0 +1,99 @@ +#include "cgiRequestBase.h" +#include +#include +#include + +namespace ba = boost::algorithm; + +namespace IceSpider { + CgiRequestBase::CgiRequestBase(IceSpider::Core * c, char ** env) : + IceSpider::IHttpRequest(c) + { + for(char * const * e = env; *e; ++e) { + addenv(*e); + } + } + + void + CgiRequestBase::addenv(char * e) + { + if (auto eq = strchr(e, '=')) { + *eq++ = '\0'; + envmap.insert({ e, Env(eq, strchr(eq, '\0')) }); + } + } + + void + CgiRequestBase::initialize() + { + 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); + if (eq < amp) { + *eq = '\0'; + *amp = '\0'; + qsmap.insert({ eq, Env( eq + 1, amp ) }); + } + else { + *eq = '\0'; + *amp = '\0'; + qsmap.insert({ eq, Env( eq + 1, eq + 1 ) }); + } + start = amp + 1; + } + } + } + + IceUtil::Optional + CgiRequestBase::optionalLookup(const std::string & key, const VarMap & vm) + { + auto i = vm.find(key.c_str()); + if (i == vm.end()) { + return IceUtil::Optional(); + } + return std::string(std::get<0>(i->second), std::get<1>(i->second)); + } + + std::string + CgiRequestBase::getRequestPath() const + { + return optionalLookup("REDIRECT_URL", envmap) / + [this]() { return optionalLookup("SCRIPT_NAME", envmap); } / + [this]() -> std::string { throw std::runtime_error("Couldn't determine request path"); }; + } + + UserIceSpider::HttpMethod + CgiRequestBase::getRequestMethod() const + { + return UserIceSpider::HttpMethod::GET; + } + + IceUtil::Optional + CgiRequestBase::getURLParam(const std::string & key) const + { + return optionalLookup(key, pathmap); + } + + IceUtil::Optional + CgiRequestBase::getQueryStringParam(const std::string & key) const + { + return optionalLookup(key, qsmap); + } + + IceUtil::Optional + CgiRequestBase::getHeaderParam(const std::string & key) const + { + return optionalLookup(("HTTP_" + boost::algorithm::to_upper_copy(key)).c_str(), envmap); + } + + bool + CgiRequestBase::cmp_str::operator()(char const * a, char const * b) const + { + return std::strcmp(a, b) < 0; + } +} + diff --git a/icespider/fcgi/cgiRequestBase.h b/icespider/fcgi/cgiRequestBase.h new file mode 100644 index 0000000..8a6d207 --- /dev/null +++ b/icespider/fcgi/cgiRequestBase.h @@ -0,0 +1,38 @@ +#ifndef ICESPIDER_CGI_CGIREQUESTBASE_H +#define ICESPIDER_CGI_CGIREQUESTBASE_H + +#include +#include +#include +#include + +namespace IceSpider { + class CgiRequestBase : public IceSpider::IHttpRequest { + protected: + struct cmp_str { + bool operator()(char const *a, char const *b) const; + }; + + typedef std::tuple Env; + typedef std::map VarMap; + + CgiRequestBase(IceSpider::Core * c, char ** env); + void addenv(char *); + void initialize(); + + std::string getRequestPath() const override; + UserIceSpider::HttpMethod getRequestMethod() const override; + IceUtil::Optional getURLParam(const std::string & key) const override; + IceUtil::Optional getQueryStringParam(const std::string & key) const override; + IceUtil::Optional getHeaderParam(const std::string & key) const override; + + static IceUtil::Optional optionalLookup(const std::string & key, const VarMap &); + + VarMap envmap; + VarMap qsmap; + VarMap pathmap; + }; +} + +#endif + diff --git a/icespider/fcgi/fcgiRequest.cpp b/icespider/fcgi/fcgiRequest.cpp new file mode 100644 index 0000000..484f12f --- /dev/null +++ b/icespider/fcgi/fcgiRequest.cpp @@ -0,0 +1,26 @@ +#include "fcgiRequest.h" + +namespace IceSpider { + FcgiRequest::FcgiRequest(IceSpider::Core * c, FCGX_Request * r) : + CgiRequestBase(c, r->envp), + inputbuf(r->in), + input(&inputbuf), + outputbuf(r->out), + output(&outputbuf) + { + initialize(); + } + + std::istream & + FcgiRequest::getInputStream() const + { + return input; + } + + std::ostream & + FcgiRequest::getOutputStream() const + { + return output; + } +} + diff --git a/icespider/fcgi/fcgiRequest.h b/icespider/fcgi/fcgiRequest.h new file mode 100644 index 0000000..ec5b65b --- /dev/null +++ b/icespider/fcgi/fcgiRequest.h @@ -0,0 +1,23 @@ +#ifndef ICESPIDER_CGI_FCGIREQUEST_H +#define ICESPIDER_CGI_FCGIREQUEST_H + +#include "cgiRequestBase.h" +#include + +namespace IceSpider { + class FcgiRequest : public CgiRequestBase { + public: + FcgiRequest(IceSpider::Core * c, FCGX_Request * r); + + std::istream & getInputStream() const override; + std::ostream & getOutputStream() const override; + + fcgi_streambuf inputbuf; + mutable std::istream input; + fcgi_streambuf outputbuf; + mutable std::ostream output; + }; +} + +#endif + diff --git a/icespider/fcgi/main.cpp b/icespider/fcgi/main.cpp index 6132769..1d2e68c 100644 --- a/icespider/fcgi/main.cpp +++ b/icespider/fcgi/main.cpp @@ -1,8 +1,14 @@ -#include +#include +#include "fcgiRequest.h" +#include "cgiRequest.h" +using namespace IceSpider; + +DLL_PUBLIC int -main(void) +main(int argc, char ** argv, char ** env) { + IceSpider::Core core; if (!FCGX_IsCGI()) { FCGX_Request request; @@ -10,13 +16,15 @@ main(void) FCGX_InitRequest(&request, 0, 0); while (FCGX_Accept_r(&request) == 0) { - // app.process(IO, &IO, IO); + FcgiRequest r(&core, &request); + core.process(&r); FCGX_Finish_r(&request); } - return 0; } else { - return 1; + CgiRequest r(&core, argc, argv, env); + core.process(&r); } + return 0; } diff --git a/icespider/unittests/Jamfile.jam b/icespider/unittests/Jamfile.jam index b9a861b..d948b29 100644 --- a/icespider/unittests/Jamfile.jam +++ b/icespider/unittests/Jamfile.jam @@ -69,6 +69,16 @@ run ../compile : testApp ; +run + testFcgi.cpp + : + 'QUERY_STRING=noeq&noval=&someval=here&another=here' + 'REDIRECT_URL=/' + : : + ../common//icespider-common + ../core//icespider-core + ../fcgi//icespider-fcgi + : testFcgi ; lib test-api : test-api.ice diff --git a/icespider/unittests/testFcgi.cpp b/icespider/unittests/testFcgi.cpp new file mode 100644 index 0000000..65e2cc3 --- /dev/null +++ b/icespider/unittests/testFcgi.cpp @@ -0,0 +1 @@ +// intentionally blank -- cgit v1.2.3