From 45aec70e4b577aaec17720b6082fe6b8ad0e243f Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 18 Mar 2016 15:59:02 +0300 Subject: Get rid of using the 'root exemplar' for request handling --- web/apache/service | 86 ++++++++++++++++++++++++++++++++++---------------- web/apache/service.cxx | 51 +++++++++++++++++++++++------- web/apache/service.txx | 48 ++++++++++------------------ 3 files changed, 114 insertions(+), 71 deletions(-) (limited to 'web') 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 ' 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 ' directive + // specified. The module allowed to handle a request in the scope. + // + allowed, + + // Configuration scope has 'SetHandler |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 (config));} + static context* + context_cast (void* config) noexcept + {return static_cast (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 ()->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 value); + add_option (context*, const char* name, optional 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 void @@ -276,17 +300,23 @@ namespace web template 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; + // The context objects pointed by the key can change during the + // configuration phase. + // + using options = std::map; options options_; - using exemplars = std::map>; + // The context objects pointed by the key can not change during the + // request handling phase. + // + using exemplars = std::map>; 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 #include #include // move() -#include // strlen() +#include // strlen(), strcmp() #include #include @@ -40,7 +40,7 @@ namespace web // file as foo-bar. // const option_descriptions& od (exemplar_.options ()); - unique_ptr directives (new command_rec[od.size () + 1]); + unique_ptr 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 ' in effect for the corresponding scope. + // + *d++ = + { + "SetHandler", + reinterpret_cast (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 (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 ( - 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 value) + add_option (context* ctx, const char* name, optional value) { auto i (option_descriptions_.find (name)); assert (i != option_descriptions_.end ()); @@ -181,12 +207,12 @@ namespace web if (i->second != static_cast (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 (&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 ' 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 (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 (rq, id, lg); + return srv->template handle (rq, cx, lg); } template 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 (exemplar)); + const M* e (dynamic_cast (i->second.get ())); assert (e != nullptr); for (M m (*e);;) -- cgit v1.1