From b72424fca7a6af6ff7921101c450850fef875152 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sun, 6 Mar 2016 13:52:48 +0300 Subject: Support multiple instances of brep in a single Apache instance --- web/apache/service | 189 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 165 insertions(+), 24 deletions(-) (limited to 'web/apache/service') diff --git a/web/apache/service b/web/apache/service index 75c096c..4c0d395 100644 --- a/web/apache/service +++ b/web/apache/service @@ -8,6 +8,8 @@ #include #include // module, ap_hook_*() +#include +#include // unique_ptr #include #include @@ -19,6 +21,22 @@ namespace web { namespace apache { + // Apache has 3 configuration scopes: main server, virtual server, and + // directory (location). It provides configuration scope-aware modules + // with the ability to build a hierarchy of configuration contexts. Later, + // when processing a request, Apache passes the appropriate directory + // configuration context to the request handler. + // + // This Apache service implementation first makes a copy of the provided + // (in the constructor below) module exemplar for each directory context. + // It then initializes each of these "context exemplars" with the (merged) + // set of configuration options. Finally, when handling a request, it + // copies the corresponding "context exemplar" to create the "handling + // instance". Note that the "context exemplars" are create before the + // provided exemplar is initialized. As a result, it is possible to detect + // if the module's copy constructor is used to create a "context exemplar" + // or a "handling instance". + // class service: ::module { public: @@ -41,6 +59,41 @@ namespace web { init_directives (); + // Set configuration context management hooks. + // + // The overall process of building the configuration hierarchy for a + // module is as follows: + // + // 1. Apache creates directory and server configuration contexts for + // scopes containing module-defined directives by calling the + // create_{server,dir}_context() callback functions. For directives + // at the server scope the special directory context is created as + // well. + // + // 2. Apache calls parse_option() function for each module-defined + // directive. The function parses the directives and places the + // resulting options into the corresponding configuration context. + // It also establishes the directory-server contexts relations. + // + // 3. Apache calls merge_server_context() function for each virtual + // server. The function complements virtual server context options + // with the ones from the main server. + // + // 4. Apache calls config_finalizer() which complements the directory + // contexts options with the ones from the enclosing servers. + // + // 5. Apache calls worker_initializer() which creates module exemplar + // for each directory configuration context. + // + // References: + // http://www.apachetutor.org/dev/config + // http://httpd.apache.org/docs/2.4/developer/modguide.html + // http://wiki.apache.org/httpd/ModuleLife + // + create_server_config = &create_server_context; + create_dir_config = &create_dir_context; + merge_server_config = &merge_server_context; + // instance () is invented to delegate processing from apache // request handler C function to the service non static member // function. This appoach resticts number of service objects per @@ -56,6 +109,7 @@ namespace web delete [] cmds; } + private: template static service*& instance () noexcept @@ -88,16 +142,7 @@ namespace web config_finalizer (apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec* s) noexcept { - auto srv (instance ()); - bool& parsed (srv->options_parsed_); - - if (!parsed) - { - log l (s, srv); - srv->exemplar_.version (l); - parsed = true; - } - + instance ()->finalize_config (s); return OK; } @@ -107,44 +152,140 @@ namespace web { auto srv (instance ()); log l (s, srv); - srv->init_worker (l); + srv->template init_worker (l); } template static int - request_handler (request_rec* r) noexcept + request_handler (request_rec* r) noexcept; + + private: + // Our representation of the Apache configuration context. + // + // The lifetime of this object is under the control of the Apache API, + // which treats it as a raw sequence of bytes. In order not to tinker + // with the C-style structures and APR memory pools, we will keep it a + // (C++11) POD type with just the members required to maintain the + // context hierarchy. + // + // We will then use the pointers to these context objects as keys in + // maps to (1) the corresponding application-level option lists during + // the configuration cycle and to (2) the corresponding module exemplar + // during the HTTP request handling phase. We will also use the same + // type for both directory and server configuration contexts. + // + struct context { - auto srv (instance ()); - if (!r->handler || srv->name_ != r->handler) return DECLINED; + // Outer (server) configuration context for the directory + // configuration context, NULL otherwise. + // + context* server; - request req (r); - log l (r->server, srv); - return srv->template handle (req, l); - } + // If module directives appear directly in the server configuration + // scope, Apache creates a special directory context for them. This + // context appears at the same hierarchy level as the user-defined + // directory contexts of the same server scope. + // + bool special; + + // Create the server configuration context. + // + context (): server (nullptr), special (false) {} + + // Create the directory configuration context. Due to the Apache API + // implementation details it is not possible to detect the enclosing + // server configuration context at the time of directory context + // creation. As a result, the server member is set by the module's + // parse_option() function. + // + context (bool s): server (nullptr), 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));} private: void init_directives (); - void - init_worker (log& l) noexcept; + // Create the server configuration context. Called by the Apache API + // whenever a new object of that type is required. + // + static void* + create_server_context (apr_pool_t*, server_rec*) noexcept; + + // Create the server directory configuration context. Called by the + // Apache API whenever a new object of that type is required. + // + static void* + create_dir_context (apr_pool_t*, char* dir) noexcept; + + template + static void* + 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)); + + return enclosed; + } static const char* - parse_option (cmd_parms* parms, void* mconfig, const char* args) noexcept; + parse_option (cmd_parms* parms, void* conf, const char* args) noexcept; const char* - add_option (const char* name, optional value); + add_option (context_id id, const char* name, optional value); + + void + finalize_config (server_rec*); + + void + clear_config (); + + void + complement (context_id enclosed, context_id enclosing); + + template + void + init_worker (log&); template int - handle (request& r, log& l) noexcept; + handle (request&, context_id, log&) const; private: std::string name_; module& exemplar_; option_descriptions option_descriptions_; - name_values options_; + + using options = std::map; + options options_; + + using exemplars = std::map>; + exemplars exemplars_; + bool options_parsed_ = false; + bool version_logged_ = false; }; } } -- cgit v1.1