aboutsummaryrefslogtreecommitdiff
path: root/web/server/apache/service.txx
diff options
context:
space:
mode:
Diffstat (limited to 'web/server/apache/service.txx')
-rw-r--r--web/server/apache/service.txx213
1 files changed, 213 insertions, 0 deletions
diff --git a/web/server/apache/service.txx b/web/server/apache/service.txx
new file mode 100644
index 0000000..9e1037b
--- /dev/null
+++ b/web/server/apache/service.txx
@@ -0,0 +1,213 @@
+// file : web/server/apache/service.txx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <httpd.h> // APEXIT_CHILDSICK
+#include <http_log.h> // APLOG_*
+
+#include <cstdlib> // exit()
+#include <utility> // move()
+#include <exception>
+
+#include <libbutl/utility.hxx> // operator<<(ostream, exception)
+
+namespace web
+{
+ namespace apache
+ {
+ template <typename H>
+ void service::
+ init_worker (log& l)
+ {
+ using namespace std;
+
+ const string func_name (
+ "web::apache::service<" + name_ + ">::init_worker");
+
+ try
+ {
+ const H* exemplar (dynamic_cast<const H*> (&exemplar_));
+ assert (exemplar != nullptr);
+
+ // For each directory configuration context, for which the handler is
+ // allowed to handle a request, create the handler exemplar as a deep
+ // copy of the exemplar_ member, and initialize it with the
+ // context-specific option list.
+ //
+ for (const auto& o: options_)
+ {
+ const context* c (o.first);
+
+ if (c->server != nullptr && // Is a directory configuration context.
+ c->handling == request_handling::allowed)
+ {
+ auto r (
+ exemplars_.emplace (
+ c,
+ unique_ptr<handler> (new H (*exemplar))));
+
+ r.first->second->init (o.second, l);
+ }
+ }
+
+ // Options are not needed anymore. Free up the space.
+ //
+ options_.clear ();
+ }
+ catch (const exception& e)
+ {
+ l.write (nullptr, 0, func_name.c_str (), APLOG_EMERG, e.what ());
+
+ // Terminate the worker apache process. APEXIT_CHILDSICK indicates to
+ // the root process that the worker have exited due to a resource
+ // shortage. In this case the root process limits the rate of forking
+ // until situation is resolved.
+ //
+ // If the root process fails to create any worker process on startup,
+ // the behaviour depends on the Multi-Processing Module enabled. For
+ // mpm_worker_module and mpm_event_module the root process terminates.
+ // For mpm_prefork_module it keeps trying to create the worker process
+ // at one-second intervals.
+ //
+ // If the root process loses all it's workers while running (for
+ // example due to the MaxRequestsPerChild directive), and fails to
+ // create any new ones, it keeps trying to create the worker process
+ // at one-second intervals.
+ //
+ exit (APEXIT_CHILDSICK);
+ }
+ catch (...)
+ {
+ l.write (nullptr,
+ 0,
+ func_name.c_str (),
+ APLOG_EMERG,
+ "unknown error");
+
+ // Terminate the worker apache process.
+ //
+ exit (APEXIT_CHILDSICK);
+ }
+ }
+
+ template <typename H>
+ int service::
+ request_handler (request_rec* r) noexcept
+ {
+ auto srv (instance<H> ());
+ if (!r->handler || srv->name_ != r->handler) return DECLINED;
+
+ assert (r->per_dir_config != nullptr);
+
+ // Obtain the request-associated configuration context.
+ //
+ const context* cx (
+ context_cast (ap_get_module_config (r->per_dir_config, srv)));
+
+ assert (cx != nullptr);
+
+ request rq (r);
+ log lg (r->server, srv);
+ return srv->template handle<H> (rq, cx, lg);
+ }
+
+ template <typename H>
+ int service::
+ handle (request& rq, const context* cx, log& lg) const
+ {
+ using namespace std;
+
+ static const string func_name (
+ "web::apache::service<" + name_ + ">::handle");
+
+ try
+ {
+ auto i (exemplars_.find (cx));
+ assert (i != exemplars_.end ());
+
+ const H* e (dynamic_cast<const H*> (i->second.get ()));
+ assert (e != nullptr);
+
+ for (H h (*e);;)
+ {
+ try
+ {
+ if (static_cast<handler&> (h).handle (rq, rq, lg))
+ return rq.flush ();
+
+ if (rq.state () == request_state::initial)
+ return DECLINED;
+
+ lg.write (nullptr, 0, func_name.c_str (), APLOG_ERR,
+ "handling declined being partially executed");
+ break;
+ }
+ catch (const handler::retry&)
+ {
+ // Retry to handle the request.
+ //
+ rq.rewind ();
+ }
+ }
+ }
+ catch (const invalid_request& e)
+ {
+ if (!e.content.empty () && rq.state () < request_state::writing)
+ {
+ try
+ {
+ rq.content (e.status, e.type) << e.content << endl;
+ return rq.flush ();
+ }
+ catch (const exception& e)
+ {
+ lg.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+ }
+ }
+
+ return e.status;
+ }
+ catch (const exception& e)
+ {
+ lg.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+
+ if (*e.what () && rq.state () < request_state::writing)
+ {
+ try
+ {
+ rq.content (
+ HTTP_INTERNAL_SERVER_ERROR, "text/plain;charset=utf-8")
+ << e << endl;
+
+ return rq.flush ();
+ }
+ catch (const exception& e)
+ {
+ lg.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+ }
+ }
+ }
+ catch (...)
+ {
+ lg.write (nullptr, 0, func_name.c_str (), APLOG_ERR, "unknown error");
+
+ if (rq.state () < request_state::writing)
+ {
+ try
+ {
+ rq.content (
+ HTTP_INTERNAL_SERVER_ERROR, "text/plain;charset=utf-8")
+ << "unknown error" << endl;
+
+ return rq.flush ();
+ }
+ catch (const exception& e)
+ {
+ lg.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+ }
+ }
+ }
+
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+}