diff options
Diffstat (limited to 'web/apache/service')
-rw-r--r-- | web/apache/service | 189 |
1 files changed, 165 insertions, 24 deletions
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 <httpd.h> #include <http_config.h> // module, ap_hook_*() +#include <map> +#include <memory> // unique_ptr #include <string> #include <cassert> @@ -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<M>; + // instance<M> () 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 <typename M> 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<M> ()); - bool& parsed (srv->options_parsed_); - - if (!parsed) - { - log l (s, srv); - srv->exemplar_.version (l); - parsed = true; - } - + instance<M> ()->finalize_config (s); return OK; } @@ -107,44 +152,140 @@ namespace web { auto srv (instance<M> ()); log l (s, srv); - srv->init_worker (l); + srv->template init_worker<M> (l); } template <typename M> 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<M> ()); - 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<M> (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<const context*> (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 <typename M> + static void* + 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)); + + 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<std::string> value); + add_option (context_id id, const char* name, optional<std::string> value); + + void + finalize_config (server_rec*); + + void + clear_config (); + + void + complement (context_id enclosed, context_id enclosing); + + template <typename M> + void + init_worker (log&); template <typename M> 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<context_id, name_values>; + options options_; + + using exemplars = std::map<context_id, std::unique_ptr<module>>; + exemplars exemplars_; + bool options_parsed_ = false; + bool version_logged_ = false; }; } } |