summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--icespider/core/core.cpp30
-rw-r--r--icespider/core/core.h12
-rw-r--r--icespider/core/ihttpRequest.h5
-rw-r--r--icespider/fcgi/cgiRequestBase.cpp36
-rw-r--r--icespider/fcgi/cgiRequestBase.h5
-rw-r--r--icespider/testing/testRequest.cpp67
-rw-r--r--icespider/testing/testRequest.h22
-rw-r--r--icespider/unittests/testApp.cpp59
8 files changed, 218 insertions, 18 deletions
diff --git a/icespider/core/core.cpp b/icespider/core/core.cpp
index 9d187c9..3b3ec06 100644
--- a/icespider/core/core.cpp
+++ b/icespider/core/core.cpp
@@ -6,6 +6,7 @@
#include <factory.impl.h>
INSTANTIATEFACTORY(IceSpider::Plugin, Ice::CommunicatorPtr, Ice::PropertiesPtr);
+INSTANTIATEPLUGINOF(IceSpider::ErrorHandler);
namespace IceSpider {
const boost::filesystem::path Core::defaultConfig("config/ice.properties");
@@ -69,13 +70,40 @@ namespace IceSpider {
request->response(he.code, he.message);
}
catch (const std::exception & e) {
- request->response(500, e.what());
+ handleError(request, e);
}
catch (...) {
request->response(500, "Unknown internal server error");
}
}
+ void
+ Core::handleError(IHttpRequest * request, const std::exception & exception) const
+ {
+ auto errorHandlers = AdHoc::PluginManager::getDefault()->getAll<ErrorHandler>();
+ for (const auto & eh : errorHandlers) {
+ try {
+ switch (eh->implementation()->handleError(request, exception)) {
+ case ErrorHandlerResult_Handled:
+ return;
+ case ErrorHandlerResult_Unhandled:
+ continue;
+ case ErrorHandlerResult_Modified:
+ process(request, nullptr);
+ return;
+ }
+ }
+ catch (const HttpException & he) {
+ request->response(he.code, he.message);
+ return;
+ }
+ catch (...) {
+ std::cerr << "Error handler failed" << std::endl;
+ }
+ }
+ request->response(500, exception.what());
+ }
+
Ice::ObjectPrx
Core::getProxy(const char * type) const
{
diff --git a/icespider/core/core.h b/icespider/core/core.h
index b1ea594..a5de08a 100644
--- a/icespider/core/core.h
+++ b/icespider/core/core.h
@@ -18,6 +18,7 @@ namespace IceSpider {
virtual const IRouteHandler * findRoute(const IHttpRequest *) const = 0;
void process(IHttpRequest *, const IRouteHandler * = nullptr) const;
+ void handleError(IHttpRequest *, const std::exception &) const;
Ice::ObjectPrx getProxy(const char * type) const;
@@ -49,6 +50,17 @@ namespace IceSpider {
class DLL_PUBLIC Plugin : public virtual Ice::Object {
};
typedef AdHoc::Factory<Plugin, Ice::CommunicatorPtr, Ice::PropertiesPtr> PluginFactory;
+
+ enum ErrorHandlerResult {
+ ErrorHandlerResult_Unhandled,
+ ErrorHandlerResult_Handled,
+ ErrorHandlerResult_Modified,
+ };
+ class DLL_PUBLIC ErrorHandler : public AdHoc::AbstractPluginImplementation {
+ public:
+ virtual ErrorHandlerResult handleError(IHttpRequest * IHttpRequest, const std::exception &) const = 0;
+ };
+ typedef AdHoc::PluginOf<ErrorHandler> ErrorHandlerPlugin;
}
#endif
diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h
index d92d549..b22406d 100644
--- a/icespider/core/ihttpRequest.h
+++ b/icespider/core/ihttpRequest.h
@@ -24,6 +24,7 @@ namespace IceSpider {
Ice::Context getContext() const;
virtual const PathElements & getRequestPath() const = 0;
+ virtual PathElements & getRequestPath() = 0;
virtual HttpMethod getRequestMethod() const = 0;
const std::string & getURLParam(unsigned int) const;
@@ -31,6 +32,10 @@ namespace IceSpider {
virtual OptionalString getHeaderParam(const std::string &) const = 0;
virtual OptionalString getCookieParam(const std::string &) const = 0;
virtual OptionalString getEnv(const std::string &) const = 0;
+ virtual void setQueryStringParam(const std::string &, const OptionalString &) = 0;
+ virtual void setHeaderParam(const std::string &, const OptionalString &) = 0;
+ virtual void setCookieParam(const std::string &, const OptionalString &) = 0;
+ virtual void setEnv(const std::string &, const OptionalString &) = 0;
virtual Slicer::DeserializerPtr getDeserializer() const;
virtual ContentTypeSerializer getSerializer(const IRouteHandler *) const;
virtual std::istream & getInputStream() const = 0;
diff --git a/icespider/fcgi/cgiRequestBase.cpp b/icespider/fcgi/cgiRequestBase.cpp
index e820e05..f99bbf1 100644
--- a/icespider/fcgi/cgiRequestBase.cpp
+++ b/icespider/fcgi/cgiRequestBase.cpp
@@ -85,6 +85,12 @@ namespace IceSpider {
return pathElements;
}
+ PathElements &
+ CgiRequestBase::getRequestPath()
+ {
+ return pathElements;
+ }
+
HttpMethod
CgiRequestBase::getRequestMethod() const
{
@@ -121,6 +127,36 @@ namespace IceSpider {
return optionalLookup(("HTTP_" + boost::algorithm::to_upper_copy(key)).c_str(), envmap);
}
+ void
+ CgiRequestBase::setQueryStringParam(const std::string & key, const OptionalString & val)
+ {
+ if (val)
+ qsmap[key] = val;
+ else
+ qsmap.erase(key);
+ }
+
+ void
+ CgiRequestBase::setHeaderParam(const std::string &, const OptionalString &)
+ {
+ throw std::runtime_error("Changing the CGI environment is not supported.");
+ }
+
+ void
+ CgiRequestBase::setCookieParam(const std::string & key, const OptionalString & val)
+ {
+ if (val)
+ cookiemap[key] = val;
+ else
+ cookiemap.erase(key);
+ }
+
+ void
+ CgiRequestBase::setEnv(const std::string &, const OptionalString &)
+ {
+ throw std::runtime_error("Changing the CGI environment is not supported.");
+ }
+
void CgiRequestBase::response(short statusCode, const std::string & statusMsg) const
{
getOutputStream()
diff --git a/icespider/fcgi/cgiRequestBase.h b/icespider/fcgi/cgiRequestBase.h
index ec3d4dd..a7cc27d 100644
--- a/icespider/fcgi/cgiRequestBase.h
+++ b/icespider/fcgi/cgiRequestBase.h
@@ -22,11 +22,16 @@ namespace IceSpider {
typedef std::map<const char *, Env, cmp_str> VarMap;
const PathElements & getRequestPath() const override;
+ PathElements & getRequestPath() override;
HttpMethod getRequestMethod() const override;
OptionalString getQueryStringParam(const std::string & key) const override;
OptionalString getHeaderParam(const std::string & key) const override;
OptionalString getCookieParam(const std::string & key) const override;
OptionalString getEnv(const std::string & key) const override;
+ void setQueryStringParam(const std::string &, const OptionalString &) override;
+ void setHeaderParam(const std::string &, const OptionalString &) override;
+ void setCookieParam(const std::string &, const OptionalString &) override;
+ void setEnv(const std::string &, const OptionalString &) override;
void response(short, const std::string &) const override;
void setHeader(const std::string &, const std::string &) const override;
diff --git a/icespider/testing/testRequest.cpp b/icespider/testing/testRequest.cpp
index 1645445..daecc49 100644
--- a/icespider/testing/testRequest.cpp
+++ b/icespider/testing/testRequest.cpp
@@ -14,40 +14,89 @@ namespace IceSpider {
}
}
- const std::vector<std::string> &
+ const PathElements &
TestRequest::getRequestPath() const
{
return url;
}
+ PathElements &
+ TestRequest::getRequestPath()
+ {
+ return url;
+ }
+
HttpMethod
TestRequest::getRequestMethod() const
{
return method;
}
- IceUtil::Optional<std::string>
+ OptionalString
TestRequest::getEnv(const std::string & key) const
{
- return env.find(key) == env.end() ? IceUtil::Optional<std::string>() : env.find(key)->second;
+ return get(key, env);
}
- IceUtil::Optional<std::string>
+ OptionalString
TestRequest::getQueryStringParam(const std::string & key) const
{
- return qs.find(key) == qs.end() ? IceUtil::Optional<std::string>() : qs.find(key)->second;
+ return get(key, qs);
}
- IceUtil::Optional<std::string>
+ OptionalString
TestRequest::getCookieParam(const std::string & key) const
{
- return cookies.find(key) == cookies.end() ? IceUtil::Optional<std::string>() : cookies.find(key)->second;
+ return get(key, cookies);
}
- IceUtil::Optional<std::string>
+ OptionalString
TestRequest::getHeaderParam(const std::string & key) const
{
- return hdr.find(key) == hdr.end() ? IceUtil::Optional<std::string>() : hdr.find(key)->second;
+ return get(key, hdr);
+ }
+
+ OptionalString
+ TestRequest::get(const std::string & key, const MapVars & vars) const
+ {
+ auto i = vars.find(key);
+ if (i == vars.end()) {
+ return IceUtil::None;
+ }
+ return i->second;
+ }
+
+ void
+ TestRequest::setQueryStringParam(const std::string & key, const OptionalString & val)
+ {
+ set(key, val, qs);
+ }
+
+ void
+ TestRequest::setHeaderParam(const std::string & key, const OptionalString & val)
+ {
+ set(key, val, hdr);
+ }
+
+ void
+ TestRequest::setCookieParam(const std::string & key, const OptionalString & val)
+ {
+ set(key, val, cookies);
+ }
+
+ void
+ TestRequest::setEnv(const std::string & key, const OptionalString & val)
+ {
+ set(key, val, env);
+ }
+
+ void
+ TestRequest::set(const std::string & key, const OptionalString & val, MapVars & vars)
+ {
+ if (val)
+ vars[key] = *val;
+ else
+ vars.erase(key);
}
std::istream &
diff --git a/icespider/testing/testRequest.h b/icespider/testing/testRequest.h
index 3fd40f0..3278930 100644
--- a/icespider/testing/testRequest.h
+++ b/icespider/testing/testRequest.h
@@ -8,16 +8,20 @@ namespace IceSpider {
class DLL_PUBLIC TestRequest : public IHttpRequest {
public:
typedef std::map<std::string, std::string> MapVars;
- typedef std::vector<std::string> UrlVars;
TestRequest(const Core * c, HttpMethod m, const std::string & p);
- const std::vector<std::string> & getRequestPath() const override;
+ const PathElements & getRequestPath() const override;
+ PathElements & getRequestPath() override;
HttpMethod getRequestMethod() const override;
- IceUtil::Optional<std::string> getEnv(const std::string & key) const override;
- IceUtil::Optional<std::string> getQueryStringParam(const std::string & key) const override;
- IceUtil::Optional<std::string> getCookieParam(const std::string & key) const override;
- IceUtil::Optional<std::string> getHeaderParam(const std::string & key) const override;
+ OptionalString getEnv(const std::string & key) const override;
+ OptionalString getQueryStringParam(const std::string & key) const override;
+ OptionalString getCookieParam(const std::string & key) const override;
+ OptionalString getHeaderParam(const std::string & key) const override;
+ void setQueryStringParam(const std::string &, const OptionalString &) override;
+ void setHeaderParam(const std::string &, const OptionalString &) override;
+ void setCookieParam(const std::string &, const OptionalString &) override;
+ void setEnv(const std::string &, const OptionalString &) override;
std::istream & getInputStream() const override;
std::ostream & getOutputStream() const override;
void response(short statusCode, const std::string & statusMsg) const override;
@@ -25,7 +29,7 @@ namespace IceSpider {
const MapVars & getResponseHeaders();
- UrlVars url;
+ PathElements url;
MapVars qs;
MapVars cookies;
MapVars hdr;
@@ -34,6 +38,10 @@ namespace IceSpider {
mutable std::stringstream output;
const HttpMethod method;
+ protected:
+ OptionalString get(const std::string &, const MapVars &) const;
+ void set(const std::string &, const OptionalString &, MapVars &);
+
private:
MapVars responseHeaders;
};
diff --git a/icespider/unittests/testApp.cpp b/icespider/unittests/testApp.cpp
index 000f983..7a60875 100644
--- a/icespider/unittests/testApp.cpp
+++ b/icespider/unittests/testApp.cpp
@@ -154,6 +154,9 @@ class TestSerice : public TestIceSpider::TestApi {
if (s == "error") {
throw TestIceSpider::Ex("test error");
}
+ else if (s.length() == 3) {
+ throw TestIceSpider::Ex(s);
+ }
BOOST_REQUIRE_EQUAL(s, "some value");
}
@@ -491,7 +494,31 @@ BOOST_AUTO_TEST_CASE( testCookies )
BOOST_REQUIRE_EQUAL(h["Status"], "200 OK");
}
-BOOST_AUTO_TEST_CASE( testErrorHandler )
+class DummyErrorHandler : public IceSpider::ErrorHandler {
+ public:
+ IceSpider::ErrorHandlerResult
+ handleError(IceSpider::IHttpRequest * request, const std::exception & ex) const
+ {
+ if (const auto * tex = dynamic_cast<const TestIceSpider::Ex *>(&ex)) {
+ if (tex->message == "404") {
+ throw IceSpider::Http404_NotFound();
+ }
+ if (tex->message == "304") {
+ request->getRequestPath().front() = "some value";
+ return IceSpider::ErrorHandlerResult_Modified;
+ }
+ if (tex->message == "400") {
+ request->response(400, "Handled");
+ return IceSpider::ErrorHandlerResult_Handled;
+ }
+ }
+ return IceSpider::ErrorHandlerResult_Unhandled;
+ }
+};
+
+PLUGIN(DummyErrorHandler, IceSpider::ErrorHandler);
+
+BOOST_AUTO_TEST_CASE( testErrorHandler_Unhandled )
{
TestRequest requestDeleteItem(this, HttpMethod::DELETE, "/error");
process(&requestDeleteItem);
@@ -501,5 +528,35 @@ BOOST_AUTO_TEST_CASE( testErrorHandler )
BOOST_REQUIRE(requestDeleteItem.output.eof());
}
+BOOST_AUTO_TEST_CASE( testErrorHandler_Handled1 )
+{
+ TestRequest requestDeleteItem(this, HttpMethod::DELETE, "/404");
+ process(&requestDeleteItem);
+ auto h = requestDeleteItem.getResponseHeaders();
+ BOOST_REQUIRE_EQUAL(h["Status"], "404 Not found");
+ requestDeleteItem.output.get();
+ BOOST_REQUIRE(requestDeleteItem.output.eof());
+}
+
+BOOST_AUTO_TEST_CASE( testErrorHandler_Handled2 )
+{
+ TestRequest requestDeleteItem(this, HttpMethod::DELETE, "/400");
+ process(&requestDeleteItem);
+ auto h = requestDeleteItem.getResponseHeaders();
+ BOOST_REQUIRE_EQUAL(h["Status"], "400 Handled");
+ requestDeleteItem.output.get();
+ BOOST_REQUIRE(requestDeleteItem.output.eof());
+}
+
+BOOST_AUTO_TEST_CASE( testErrorHandler_Handled3 )
+{
+ TestRequest requestDeleteItem(this, HttpMethod::DELETE, "/304");
+ process(&requestDeleteItem);
+ auto h = requestDeleteItem.getResponseHeaders();
+ BOOST_REQUIRE_EQUAL(h["Status"], "200 OK");
+ requestDeleteItem.output.get();
+ BOOST_REQUIRE(requestDeleteItem.output.eof());
+}
+
BOOST_AUTO_TEST_SUITE_END();