aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2016-03-18 15:59:02 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2016-03-18 15:59:02 +0300
commit45aec70e4b577aaec17720b6082fe6b8ad0e243f (patch)
tree614c4970d881dbee32663b4cad490261a19fc927
parent0e77c7fd324b99006b01c248a0e3295d2c07cec9 (diff)
Get rid of using the 'root exemplar' for request handling
-rw-r--r--web/apache/service86
-rw-r--r--web/apache/service.cxx51
-rw-r--r--web/apache/service.txx48
3 files changed, 114 insertions, 71 deletions
diff --git a/web/apache/service b/web/apache/service
index 165ff90..c663323 100644
--- a/web/apache/service
+++ b/web/apache/service
@@ -86,7 +86,8 @@ namespace web
// contexts options with the ones from the enclosing servers.
//
// 5. Apache calls worker_initializer() which creates module exemplar
- // for each directory configuration context.
+ // for each directory configuration context having
+ // 'SetHandler <mod_name>' directive in effect for it.
//
// References:
// http://www.apachetutor.org/dev/config
@@ -164,6 +165,34 @@ namespace web
request_handler (request_rec* r) noexcept;
private:
+
+ // Reflects the allowability of the request handling in the specific
+ // configuration scope.
+ //
+ enum class request_handling
+ {
+ // Configuration scope has 'SetHandler <mod_name>' directive
+ // specified. The module allowed to handle a request in the scope.
+ //
+ allowed,
+
+ // Configuration scope has 'SetHandler <other_mod_name>|None'
+ // directive specified. The module disallowed to handle a request in
+ // the scope.
+ //
+ disallowed,
+
+ //
+ // Note that if there are several SetHandler directives specified
+ // in the specific scope, then the latest one takes the precedence.
+
+ // Configuration scope has no SetHandler directive specified. The
+ // request handling allowability is established by the enclosing
+ // scopes.
+ //
+ inherit
+ };
+
// Our representation of the Apache configuration context.
//
// The lifetime of this object is under the control of the Apache API,
@@ -183,7 +212,7 @@ namespace web
// Outer (server) configuration context for the directory
// configuration context, NULL otherwise.
//
- context* server;
+ context* server = nullptr;
// If module directives appear directly in the server configuration
// scope, Apache creates a special directory context for them. This
@@ -192,9 +221,14 @@ namespace web
//
bool special;
+ // Request handling allowability for the corresponding configuration
+ // scope.
+ //
+ request_handling handling = request_handling::inherit;
+
// Create the server configuration context.
//
- context (): server (nullptr), special (false) {}
+ context (): special (false) {}
// Create the directory configuration context. Due to the Apache API
// implementation details it is not possible to detect the enclosing
@@ -202,28 +236,16 @@ namespace web
// creation. As a result, the server member is set by the module's
// parse_option() function.
//
- context (bool s): server (nullptr), special (s) {}
+ context (bool s): special (s) {}
// Ensure the object is only destroyed by Apache.
//
~context () = delete;
};
- // Type of the key for configuration options and module exemplar maps.
- //
- using context_id = const context*;
-
- static bool
- is_null (context_id id) noexcept {return id == nullptr;}
-
- static context_id
- make_context_id (const context* c) noexcept {return c;}
-
- // Convert Apache-provided configuration pointer to the context id.
- //
- static context_id
- make_context_id (void* config) noexcept
- {return make_context_id (static_cast<const context*> (config));}
+ static context*
+ context_cast (void* config) noexcept
+ {return static_cast<context*> (config);}
private:
void
@@ -246,10 +268,8 @@ namespace web
merge_server_context (apr_pool_t*, void* enclosing, void* enclosed)
noexcept
{
- // Complement the enclosed context with options of the enclosing one.
- //
instance<M> ()->complement (
- make_context_id (enclosed), make_context_id (enclosing));
+ context_cast (enclosed), context_cast (enclosing));
return enclosed;
}
@@ -258,8 +278,7 @@ namespace web
parse_option (cmd_parms* parms, void* conf, const char* args) noexcept;
const char*
- add_option (
- context_id id, const char* name, optional<std::string> value);
+ add_option (context*, const char* name, optional<std::string> value);
void
finalize_config (server_rec*);
@@ -267,8 +286,13 @@ namespace web
void
clear_config ();
+ // Complement the enclosed context with options of the enclosing one.
+ // If the 'handling' member of the enclosed context is set to
+ // request_handling::inherit value, assign it a value from the enclosing
+ // context.
+ //
void
- complement (context_id enclosed, context_id enclosing);
+ complement (context* enclosed, context* enclosing);
template <typename M>
void
@@ -276,17 +300,23 @@ namespace web
template <typename M>
int
- handle (request&, context_id, log&) const;
+ handle (request&, const context*, log&) const;
private:
std::string name_;
module& exemplar_;
option_descriptions option_descriptions_;
- using options = std::map<context_id, name_values>;
+ // The context objects pointed by the key can change during the
+ // configuration phase.
+ //
+ using options = std::map<context*, name_values>;
options options_;
- using exemplars = std::map<context_id, std::unique_ptr<module>>;
+ // The context objects pointed by the key can not change during the
+ // request handling phase.
+ //
+ using exemplars = std::map<const context*, std::unique_ptr<module>>;
exemplars exemplars_;
bool options_parsed_ = false;
diff --git a/web/apache/service.cxx b/web/apache/service.cxx
index da44530..6679194 100644
--- a/web/apache/service.cxx
+++ b/web/apache/service.cxx
@@ -13,7 +13,7 @@
#include <string>
#include <cassert>
#include <utility> // move()
-#include <cstring> // strlen()
+#include <cstring> // strlen(), strcmp()
#include <exception>
#include <butl/optional>
@@ -40,7 +40,7 @@ namespace web
// file as foo-bar.
//
const option_descriptions& od (exemplar_.options ());
- unique_ptr<command_rec[]> directives (new command_rec[od.size () + 1]);
+ unique_ptr<command_rec[]> directives (new command_rec[od.size () + 2]);
command_rec* d (directives.get ());
for (const auto& o: od)
@@ -69,6 +69,21 @@ namespace web
};
}
+ // Track if the module allowed to handle a request in the specific
+ // configuration scope. The module exemplar will be created (and
+ // initialized) only for configuration contexts having
+ // 'SetHandler <mod_name>' in effect for the corresponding scope.
+ //
+ *d++ =
+ {
+ "SetHandler",
+ reinterpret_cast<cmd_func> (parse_option),
+ this,
+ RSRC_CONF | ACCESS_CONF,
+ RAW_ARGS,
+ nullptr
+ };
+
*d = {nullptr, nullptr, nullptr, 0, RAW_ARGS, nullptr};
cmds = directives.release ();
}
@@ -129,7 +144,7 @@ namespace web
// Determine the directory and server configuration contexts for the
// option.
//
- context* dir_context (static_cast<context*> (conf));
+ context* dir_context (context_cast (conf));
assert (dir_context != nullptr);
server_rec* server (parms->server);
@@ -137,8 +152,7 @@ namespace web
assert (server->module_config != nullptr);
context* srv_context (
- static_cast<context*> (
- ap_get_module_config (server->module_config, &srv)));
+ context_cast (ap_get_module_config (server->module_config, &srv)));
assert (srv_context != nullptr);
@@ -164,14 +178,26 @@ namespace web
// map. Later the context will be populated with an enclosing server
// context options.
//
- srv.options_.emplace (make_context_id (dir_context), name_values ());
+ srv.options_.emplace (dir_context, name_values ());
+
+ const char* name (parms->cmd->name);
+ if (strcmp (name, "SetHandler") == 0)
+ {
+ // Keep track of a request handling allowability.
+ //
+ srv.options_.emplace (c, name_values ()).first->first->handling =
+ value && *value == srv.name_
+ ? request_handling::allowed
+ : request_handling::disallowed;
+
+ return 0;
+ }
- return srv.add_option (
- make_context_id (c), parms->cmd->name, move (value));
+ return srv.add_option (c, name, move (value));
}
const char* service::
- add_option (context_id id, const char* name, optional<string> value)
+ add_option (context* ctx, const char* name, optional<string> value)
{
auto i (option_descriptions_.find (name));
assert (i != option_descriptions_.end ());
@@ -181,12 +207,12 @@ namespace web
if (i->second != static_cast<bool> (value))
return value ? "unexpected value" : "value expected";
- options_[id].emplace_back (name + name_.length () + 1, move (value));
+ options_[ctx].emplace_back (name + name_.length () + 1, move (value));
return 0;
}
void service::
- complement (context_id enclosed, context_id enclosing)
+ complement (context* enclosed, context* enclosing)
{
auto i (options_.find (enclosing));
@@ -200,6 +226,9 @@ namespace web
name_values& dest (options_[enclosed]);
dest.insert (dest.begin (), src.begin (), src.end ());
}
+
+ if (enclosed->handling == request_handling::inherit)
+ enclosed->handling = enclosing->handling;
}
void service::
diff --git a/web/apache/service.txx b/web/apache/service.txx
index 1d0693a..2e8a3e5 100644
--- a/web/apache/service.txx
+++ b/web/apache/service.txx
@@ -25,24 +25,21 @@ namespace web
const M* exemplar (dynamic_cast<const M*> (&exemplar_));
assert (exemplar != nullptr);
- // For each directory configuration context create the module exemplar
- // as a deep copy of the exemplar_ member and initialize it with the
- // context-specific option list. Note that there can be contexts
- // having no module options specified for them and no options
- // inherited from enclosing contexts. Such contexts will not appear in
- // the options_ map. Meanwhile 'SetHandler <modname>' directive can be
- // in effect for such contexts, and we should be ready to handle
- // requests for them (by using the "root exemplar").
+ // For each directory configuration context, for which the module
+ // allowed to handle a request, create the module 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.
+ if (c->server != nullptr && // Is a directory configuration context.
+ c->handling == request_handling::allowed)
{
auto r (
exemplars_.emplace (
- make_context_id (c),
+ c,
std::unique_ptr<module> (new M (*exemplar))));
r.first->second->init (o.second, l);
@@ -52,13 +49,6 @@ namespace web
// Options are not needed anymore. Free up the space.
//
options_.clear ();
-
- // Initialize the "root exemplar" by default (with no options). It
- // will be used to handle requests for configuration contexts having
- // no options specified, and no options inherited from enclosing
- // contexts.
- //
- exemplar_.init (name_values (), l);
}
catch (const std::exception& e)
{
@@ -105,37 +95,31 @@ namespace web
assert (r->per_dir_config != nullptr);
- // Obtain the request-associated configuration context id.
+ // Obtain the request-associated configuration context.
//
- context_id id (
- make_context_id (ap_get_module_config (r->per_dir_config, srv)));
+ const context* cx (
+ context_cast (ap_get_module_config (r->per_dir_config, srv)));
- assert (!is_null (id));
+ assert (cx != nullptr);
request rq (r);
log lg (r->server, srv);
- return srv->template handle<M> (rq, id, lg);
+ return srv->template handle<M> (rq, cx, lg);
}
template <typename M>
int service::
- handle (request& rq, context_id id, log& lg) const
+ handle (request& rq, const context* cx, log& lg) const
{
static const std::string func_name (
"web::apache::service<" + name_ + ">::handle");
try
{
- auto i (exemplars_.find (id));
-
- // Use the context-specific exemplar if found, otherwise use the
- // default one.
- //
- const module* exemplar (i != exemplars_.end ()
- ? i->second.get ()
- : &exemplar_);
+ auto i (exemplars_.find (cx));
+ assert (i != exemplars_.end ());
- const M* e (dynamic_cast<const M*> (exemplar));
+ const M* e (dynamic_cast<const M*> (i->second.get ()));
assert (e != nullptr);
for (M m (*e);;)