From 8f3d3956b1e837c726859eb8bbe19dad79c54a42 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 29 Apr 2017 23:55:46 +0300 Subject: Add hxx extension for headers and lib prefix for library dirs --- web/apache/log | 81 ----------- web/apache/log.hxx | 81 +++++++++++ web/apache/request | 190 ------------------------- web/apache/request.cxx | 4 +- web/apache/request.hxx | 190 +++++++++++++++++++++++++ web/apache/service | 330 ------------------------------------------- web/apache/service.cxx | 6 +- web/apache/service.hxx | 330 +++++++++++++++++++++++++++++++++++++++++++ web/apache/stream | 149 -------------------- web/apache/stream.hxx | 149 ++++++++++++++++++++ web/mime-url-encoding | 30 ---- web/mime-url-encoding.cxx | 2 +- web/mime-url-encoding.hxx | 30 ++++ web/module | 267 ----------------------------------- web/module.hxx | 267 +++++++++++++++++++++++++++++++++++ web/xhtml | 351 ---------------------------------------------- web/xhtml-fragment | 48 ------- web/xhtml-fragment.cxx | 4 +- web/xhtml-fragment.hxx | 48 +++++++ web/xhtml.hxx | 351 ++++++++++++++++++++++++++++++++++++++++++++++ 20 files changed, 1454 insertions(+), 1454 deletions(-) delete mode 100644 web/apache/log create mode 100644 web/apache/log.hxx delete mode 100644 web/apache/request create mode 100644 web/apache/request.hxx delete mode 100644 web/apache/service create mode 100644 web/apache/service.hxx delete mode 100644 web/apache/stream create mode 100644 web/apache/stream.hxx delete mode 100644 web/mime-url-encoding create mode 100644 web/mime-url-encoding.hxx delete mode 100644 web/module create mode 100644 web/module.hxx delete mode 100644 web/xhtml delete mode 100644 web/xhtml-fragment create mode 100644 web/xhtml-fragment.hxx create mode 100644 web/xhtml.hxx (limited to 'web') diff --git a/web/apache/log b/web/apache/log deleted file mode 100644 index dda9099..0000000 --- a/web/apache/log +++ /dev/null @@ -1,81 +0,0 @@ -// file : web/apache/log -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_LOG -#define WEB_APACHE_LOG - -#include // request_rec, server_rec -#include -#include // module - -#include // uint64_t -#include // min() - -#include - -namespace web -{ - namespace apache - { - class log: public web::log - { - public: - - log (server_rec* s, const ::module* m) noexcept - : server_ (s), module_ (m) {} - - virtual void - write (const char* msg) {write (APLOG_ERR, msg);} - - // Apache-specific interface. - // - void - write (int level, const char* msg) const noexcept - { - write (nullptr, 0, nullptr, level, msg); - } - - void - write (const char* file, - std::uint64_t line, - const char* func, - int level, - const char* msg) const noexcept - { - if (file && *file) - file = nullptr; // Skip file/line placeholder from log line. - - level = std::min (level, APLOG_TRACE8); - - if (func) - ap_log_error (file, - line, - module_->module_index, - level, - 0, - server_, - "[%s]: %s", - func, - msg); - else - // Skip function name placeholder from log line. - // - ap_log_error (file, - line, - module_->module_index, - level, - 0, - server_, - ": %s", - msg); - } - - private: - server_rec* server_; - const ::module* module_; // Apache module. - }; - } -} - -#endif // WEB_APACHE_LOG diff --git a/web/apache/log.hxx b/web/apache/log.hxx new file mode 100644 index 0000000..4397875 --- /dev/null +++ b/web/apache/log.hxx @@ -0,0 +1,81 @@ +// file : web/apache/log.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_LOG_HXX +#define WEB_APACHE_LOG_HXX + +#include // request_rec, server_rec +#include +#include // module + +#include // uint64_t +#include // min() + +#include + +namespace web +{ + namespace apache + { + class log: public web::log + { + public: + + log (server_rec* s, const ::module* m) noexcept + : server_ (s), module_ (m) {} + + virtual void + write (const char* msg) {write (APLOG_ERR, msg);} + + // Apache-specific interface. + // + void + write (int level, const char* msg) const noexcept + { + write (nullptr, 0, nullptr, level, msg); + } + + void + write (const char* file, + std::uint64_t line, + const char* func, + int level, + const char* msg) const noexcept + { + if (file && *file) + file = nullptr; // Skip file/line placeholder from log line. + + level = std::min (level, APLOG_TRACE8); + + if (func) + ap_log_error (file, + line, + module_->module_index, + level, + 0, + server_, + "[%s]: %s", + func, + msg); + else + // Skip function name placeholder from log line. + // + ap_log_error (file, + line, + module_->module_index, + level, + 0, + server_, + ": %s", + msg); + } + + private: + server_rec* server_; + const ::module* module_; // Apache module. + }; + } +} + +#endif // WEB_APACHE_LOG_HXX diff --git a/web/apache/request b/web/apache/request deleted file mode 100644 index a35c5dc..0000000 --- a/web/apache/request +++ /dev/null @@ -1,190 +0,0 @@ -// file : web/apache/request -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_REQUEST -#define WEB_APACHE_REQUEST - -#include // request_rec, HTTP_*, OK, M_POST - -#include -#include // unique_ptr -#include -#include -#include -#include - -#include -#include - -namespace web -{ - namespace apache - { - // The state of the request processing, reflecting an interaction with - // Apache API (like reading/writing content function calls), with no - // buffering taken into account. Any state different from the initial - // suppose that some irrevocable interaction with Apache API have - // happened, so request processing should be either completed, or - // reported as failed. State values are ordered in a sense that the - // higher value reflects the more advanced stage of processing, so the - // request current state value may not decrease. - // - enum class request_state - { - // Denotes the initial stage of the request handling. At this stage - // the request line and headers are already parsed by Apache. - // - initial, - - // Reading the request content. - // - reading, - - // Adding the response headers (cookies in particular). - // - headers, - - // Writing the response content. - // - writing - }; - - // Extends istreambuf with read limit checking, caching, etc. (see the - // implementation for details). - // - class istreambuf_cache; - - class request: public web::request, - public web::response, - public stream_state - { - friend class service; - - // Can not be inline/default due to the member of - // unique_ptr type. Note that istreambuf_cache type is - // incomplete. - // - request (request_rec* rec) noexcept; - ~request (); - - request_state - state () const noexcept {return state_;} - - // Flush the buffered response content if present. The returned value - // should be passed to Apache API on request handler exit. - // - int - flush (); - - // Prepare for the request re-processing if possible (no unbuffered - // read/write operations have been done). Throw sequence_error - // otherwise. In particular, the preparation can include the response - // content buffer cleanup, the request content buffer rewind. - // - void - rewind (); - - // Get request path. - // - virtual const path_type& - path (); - - // Get request body data stream. - // - virtual std::istream& - content (size_t limit = 0, size_t buffer = 0); - - // Get request parameters. - // - virtual const name_values& - parameters (); - - // Get request cookies. - // - virtual const name_values& - cookies (); - - // Get response status code. - // - status_code - status () const noexcept {return rec_->status;} - - // Set response status code. - // - virtual void - status (status_code status); - - // Set response status code, content type and get body stream. - // - virtual std::ostream& - content (status_code status, - const std::string& type, - bool buffer = true); - - // Add response cookie. - // - virtual void - cookie (const char* name, - const char* value, - const std::chrono::seconds* max_age = nullptr, - const char* path = nullptr, - const char* domain = nullptr, - bool secure = false, - bool buffer = true); - - private: - // Get application/x-www-form-urlencoded form data. If request::content() - // was not called yet (and so limits are not specified) then set both of - // them to 64KB. Rewind the stream afterwards, so it's available for the - // application as well, unless no buffering were requested beforehand. - // - const std::string& - form_data (); - - void - parse_parameters (const char* args); - - // Advance the request processing state. Noop if new state is equal to - // the current one. Throw sequence_error if the new state is less then - // the current one. Can throw invalid_request if HTTP request is - // malformed. - // - void - state (request_state); - - // stream_state members implementation. - // - virtual void - set_read_state () {state (request_state::reading);} - - virtual void - set_write_state () {state (request_state::writing);} - - // Rewind the input stream (that must exist). Throw sequence_error if - // some unbuffered content have already been read. - // - void - rewind_istream (); - - private: - request_rec* rec_; - request_state state_ = request_state::initial; - - path_type path_; - std::unique_ptr parameters_; - std::unique_ptr cookies_; - std::unique_ptr form_data_; - - std::unique_ptr in_buf_; - std::unique_ptr in_; - - std::unique_ptr out_buf_; - std::unique_ptr out_; - }; - } -} - -#include - -#endif // WEB_APACHE_REQUEST diff --git a/web/apache/request.cxx b/web/apache/request.cxx index b4fb080..219f887 100644 --- a/web/apache/request.cxx +++ b/web/apache/request.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // apr_table_*, apr_array_header_t #include // apr_pstrdup() @@ -31,7 +31,7 @@ #include #include -#include +#include using namespace std; using namespace butl; diff --git a/web/apache/request.hxx b/web/apache/request.hxx new file mode 100644 index 0000000..0488fb2 --- /dev/null +++ b/web/apache/request.hxx @@ -0,0 +1,190 @@ +// file : web/apache/request.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_REQUEST_HXX +#define WEB_APACHE_REQUEST_HXX + +#include // request_rec, HTTP_*, OK, M_POST + +#include +#include // unique_ptr +#include +#include +#include +#include + +#include +#include + +namespace web +{ + namespace apache + { + // The state of the request processing, reflecting an interaction with + // Apache API (like reading/writing content function calls), with no + // buffering taken into account. Any state different from the initial + // suppose that some irrevocable interaction with Apache API have + // happened, so request processing should be either completed, or + // reported as failed. State values are ordered in a sense that the + // higher value reflects the more advanced stage of processing, so the + // request current state value may not decrease. + // + enum class request_state + { + // Denotes the initial stage of the request handling. At this stage + // the request line and headers are already parsed by Apache. + // + initial, + + // Reading the request content. + // + reading, + + // Adding the response headers (cookies in particular). + // + headers, + + // Writing the response content. + // + writing + }; + + // Extends istreambuf with read limit checking, caching, etc. (see the + // implementation for details). + // + class istreambuf_cache; + + class request: public web::request, + public web::response, + public stream_state + { + friend class service; + + // Can not be inline/default due to the member of + // unique_ptr type. Note that istreambuf_cache type is + // incomplete. + // + request (request_rec* rec) noexcept; + ~request (); + + request_state + state () const noexcept {return state_;} + + // Flush the buffered response content if present. The returned value + // should be passed to Apache API on request handler exit. + // + int + flush (); + + // Prepare for the request re-processing if possible (no unbuffered + // read/write operations have been done). Throw sequence_error + // otherwise. In particular, the preparation can include the response + // content buffer cleanup, the request content buffer rewind. + // + void + rewind (); + + // Get request path. + // + virtual const path_type& + path (); + + // Get request body data stream. + // + virtual std::istream& + content (size_t limit = 0, size_t buffer = 0); + + // Get request parameters. + // + virtual const name_values& + parameters (); + + // Get request cookies. + // + virtual const name_values& + cookies (); + + // Get response status code. + // + status_code + status () const noexcept {return rec_->status;} + + // Set response status code. + // + virtual void + status (status_code status); + + // Set response status code, content type and get body stream. + // + virtual std::ostream& + content (status_code status, + const std::string& type, + bool buffer = true); + + // Add response cookie. + // + virtual void + cookie (const char* name, + const char* value, + const std::chrono::seconds* max_age = nullptr, + const char* path = nullptr, + const char* domain = nullptr, + bool secure = false, + bool buffer = true); + + private: + // Get application/x-www-form-urlencoded form data. If request::content() + // was not called yet (and so limits are not specified) then set both of + // them to 64KB. Rewind the stream afterwards, so it's available for the + // application as well, unless no buffering were requested beforehand. + // + const std::string& + form_data (); + + void + parse_parameters (const char* args); + + // Advance the request processing state. Noop if new state is equal to + // the current one. Throw sequence_error if the new state is less then + // the current one. Can throw invalid_request if HTTP request is + // malformed. + // + void + state (request_state); + + // stream_state members implementation. + // + virtual void + set_read_state () {state (request_state::reading);} + + virtual void + set_write_state () {state (request_state::writing);} + + // Rewind the input stream (that must exist). Throw sequence_error if + // some unbuffered content have already been read. + // + void + rewind_istream (); + + private: + request_rec* rec_; + request_state state_ = request_state::initial; + + path_type path_; + std::unique_ptr parameters_; + std::unique_ptr cookies_; + std::unique_ptr form_data_; + + std::unique_ptr in_buf_; + std::unique_ptr in_; + + std::unique_ptr out_buf_; + std::unique_ptr out_; + }; + } +} + +#include + +#endif // WEB_APACHE_REQUEST_HXX diff --git a/web/apache/service b/web/apache/service deleted file mode 100644 index 45cf39b..0000000 --- a/web/apache/service +++ /dev/null @@ -1,330 +0,0 @@ -// file : web/apache/service -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_SERVICE -#define WEB_APACHE_SERVICE - -#include // apr_pool_t -#include // APR_HOOK_* - -#include // request_rec, server_rec, HTTP_*, DECLINED -#include // module, cmd_parms, ap_hook_*() - -#include -#include // unique_ptr -#include -#include - -#include -#include -#include - -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 created as a copy of - // the provided exemplar, which is never 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: - // Note that the module exemplar is stored by-reference. - // - template - service (const std::string& name, M& exemplar) - : ::module - { - STANDARD20_MODULE_STUFF, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - ®ister_hooks - }, - name_ (name), - exemplar_ (exemplar) - { - 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 that have - // 'SetHandler ' directive in effect for it. - // - // 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 - // specific module implementation class with just one instance. - // - service*& srv (instance ()); - assert (srv == nullptr); - srv = this; - } - - ~service () - { - delete [] cmds; - } - - private: - template - static service*& - instance () noexcept - { - static service* instance; - return instance; - } - - template - static void - register_hooks (apr_pool_t*) noexcept - { - // The config_finalizer() function is called at the end of Apache - // server configuration parsing. - // - ap_hook_post_config (&config_finalizer, NULL, NULL, APR_HOOK_LAST); - - // The worker_initializer() function is called right after Apache - // worker process is started. Called for every new process spawned. - // - ap_hook_child_init ( - &worker_initializer, NULL, NULL, APR_HOOK_LAST); - - // The request_handler () function is called for each client request. - // - ap_hook_handler (&request_handler, NULL, NULL, APR_HOOK_LAST); - } - - template - static int - config_finalizer (apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec* s) - noexcept - { - instance ()->finalize_config (s); - return OK; - } - - template - static void - worker_initializer (apr_pool_t*, server_rec* s) noexcept - { - auto srv (instance ()); - log l (s, srv); - srv->template init_worker (l); - } - - template - static int - 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 is allowed to handle a request in the scope. - // - allowed, - - // Configuration scope has 'SetHandler |None' - // directive specified. The module is 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, - // 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 - { - // Outer (server) configuration context for the directory - // configuration context, NULL otherwise. - // - context* server = nullptr; - - // 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; - - // Request handling allowability for the corresponding configuration - // scope. - // - request_handling handling = request_handling::inherit; - - // Create the server configuration context. - // - context (): 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): special (s) {} - - // Ensure the object is only destroyed by Apache. - // - ~context () = delete; - }; - - static context* - context_cast (void* config) noexcept - {return static_cast (config);} - - private: - void - init_directives (); - - // 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 - { - instance ()->complement ( - context_cast (enclosed), context_cast (enclosing)); - - return enclosed; - } - - static const char* - parse_option (cmd_parms* parms, void* conf, const char* args) noexcept; - - const char* - add_option (context*, const char* name, optional value); - - void - finalize_config (server_rec*); - - 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* enclosed, context* enclosing); - - template - void - init_worker (log&); - - template - int - handle (request&, const context*, log&) const; - - private: - std::string name_; - module& exemplar_; - option_descriptions option_descriptions_; - - // The context objects pointed to by the key can change during the - // configuration phase. - // - using options = std::map; - options options_; - - // The context objects pointed to by the key can not change during the - // request handling phase. - // - using exemplars = std::map>; - exemplars exemplars_; - - bool options_parsed_ = false; - bool version_logged_ = false; - }; - } -} - -#include - -#endif // WEB_APACHE_SERVICE diff --git a/web/apache/service.cxx b/web/apache/service.cxx index ca8e235..8c3fa82 100644 --- a/web/apache/service.cxx +++ b/web/apache/service.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // apr_palloc() @@ -18,8 +18,8 @@ #include -#include -#include +#include +#include using namespace std; diff --git a/web/apache/service.hxx b/web/apache/service.hxx new file mode 100644 index 0000000..44d064f --- /dev/null +++ b/web/apache/service.hxx @@ -0,0 +1,330 @@ +// file : web/apache/service.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_SERVICE_HXX +#define WEB_APACHE_SERVICE_HXX + +#include // apr_pool_t +#include // APR_HOOK_* + +#include // request_rec, server_rec, HTTP_*, DECLINED +#include // module, cmd_parms, ap_hook_*() + +#include +#include // unique_ptr +#include +#include + +#include +#include +#include + +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 created as a copy of + // the provided exemplar, which is never 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: + // Note that the module exemplar is stored by-reference. + // + template + service (const std::string& name, M& exemplar) + : ::module + { + STANDARD20_MODULE_STUFF, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ®ister_hooks + }, + name_ (name), + exemplar_ (exemplar) + { + 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 that have + // 'SetHandler ' directive in effect for it. + // + // 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 + // specific module implementation class with just one instance. + // + service*& srv (instance ()); + assert (srv == nullptr); + srv = this; + } + + ~service () + { + delete [] cmds; + } + + private: + template + static service*& + instance () noexcept + { + static service* instance; + return instance; + } + + template + static void + register_hooks (apr_pool_t*) noexcept + { + // The config_finalizer() function is called at the end of Apache + // server configuration parsing. + // + ap_hook_post_config (&config_finalizer, NULL, NULL, APR_HOOK_LAST); + + // The worker_initializer() function is called right after Apache + // worker process is started. Called for every new process spawned. + // + ap_hook_child_init ( + &worker_initializer, NULL, NULL, APR_HOOK_LAST); + + // The request_handler () function is called for each client request. + // + ap_hook_handler (&request_handler, NULL, NULL, APR_HOOK_LAST); + } + + template + static int + config_finalizer (apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec* s) + noexcept + { + instance ()->finalize_config (s); + return OK; + } + + template + static void + worker_initializer (apr_pool_t*, server_rec* s) noexcept + { + auto srv (instance ()); + log l (s, srv); + srv->template init_worker (l); + } + + template + static int + 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 is allowed to handle a request in the scope. + // + allowed, + + // Configuration scope has 'SetHandler |None' + // directive specified. The module is 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, + // 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 + { + // Outer (server) configuration context for the directory + // configuration context, NULL otherwise. + // + context* server = nullptr; + + // 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; + + // Request handling allowability for the corresponding configuration + // scope. + // + request_handling handling = request_handling::inherit; + + // Create the server configuration context. + // + context (): 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): special (s) {} + + // Ensure the object is only destroyed by Apache. + // + ~context () = delete; + }; + + static context* + context_cast (void* config) noexcept + {return static_cast (config);} + + private: + void + init_directives (); + + // 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 + { + instance ()->complement ( + context_cast (enclosed), context_cast (enclosing)); + + return enclosed; + } + + static const char* + parse_option (cmd_parms* parms, void* conf, const char* args) noexcept; + + const char* + add_option (context*, const char* name, optional value); + + void + finalize_config (server_rec*); + + 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* enclosed, context* enclosing); + + template + void + init_worker (log&); + + template + int + handle (request&, const context*, log&) const; + + private: + std::string name_; + module& exemplar_; + option_descriptions option_descriptions_; + + // The context objects pointed to by the key can change during the + // configuration phase. + // + using options = std::map; + options options_; + + // The context objects pointed to by the key can not change during the + // request handling phase. + // + using exemplars = std::map>; + exemplars exemplars_; + + bool options_parsed_ = false; + bool version_logged_ = false; + }; + } +} + +#include + +#endif // WEB_APACHE_SERVICE_HXX diff --git a/web/apache/stream b/web/apache/stream deleted file mode 100644 index d4abb4e..0000000 --- a/web/apache/stream +++ /dev/null @@ -1,149 +0,0 @@ -// file : web/apache/stream -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_STREAM -#define WEB_APACHE_STREAM - -#include // request_rec, HTTP_* -#include // ap_*() - -#include // streamsize -#include -#include // memmove(), size_t -#include -#include // min(), max() - -#include // invalid_request - -namespace web -{ - namespace apache - { - // Object of a class implementing this interface is intended for keeping - // the state of communication with the client. - // - struct stream_state - { - // Called by istreambuf functions when content is about to be read from - // the client. Can throw invalid_request or sequence_error. - // - virtual void - set_read_state () = 0; - - // Called by ostreambuf functions when some content is about to be - // written to the client. Can throw invalid_request or sequence_error. - // - virtual void - set_write_state () = 0; - }; - - // Base class for ostreambuf and istreambuf. References request and - // communication state structures. - // - class rbuf: public std::streambuf - { - protected: - rbuf (request_rec* r, stream_state& s): rec_ (r), state_ (s) {} - - protected: - request_rec* rec_; - stream_state& state_; - }; - - class ostreambuf: public rbuf - { - public: - ostreambuf (request_rec* r, stream_state& s): rbuf (r, s) {} - - private: - virtual int_type - overflow (int_type c) - { - if (c != traits_type::eof ()) - { - state_.set_write_state (); - - char chr (c); - - // Throwing allows to distinguish comm failure from other IO error - // conditions. - // - if (ap_rwrite (&chr, sizeof (chr), rec_) == -1) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - } - - return c; - } - - virtual std::streamsize - xsputn (const char* s, std::streamsize num) - { - state_.set_write_state (); - - if (ap_rwrite (s, num, rec_) < 0) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - - return num; - } - - virtual int - sync () - { - if (ap_rflush (rec_) < 0) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - - return 0; - } - }; - - class istreambuf: public rbuf - { - public: - istreambuf (request_rec* r, - stream_state& s, - size_t bufsize = 1024, - size_t putback = 1) - : rbuf (r, s), - bufsize_ (std::max (bufsize, (size_t)1)), - putback_ (std::min (putback, bufsize_ - 1)), - buf_ (bufsize_) - { - char* p (buf_.data () + putback_); - setg (p, p, p); - } - - protected: - virtual int_type - underflow () - { - if (gptr () < egptr ()) - return traits_type::to_int_type (*gptr ()); - - state_.set_read_state (); - - size_t pb (std::min ((size_t)(gptr () - eback ()), putback_)); - std::memmove (buf_.data () + putback_ - pb, gptr () - pb, pb); - - char* p (buf_.data () + putback_); - int rb (ap_get_client_block (rec_, p, bufsize_ - putback_)); - - if (rb == 0) - return traits_type::eof (); - - if (rb < 0) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - - setg (p - pb, p, p + rb); - return traits_type::to_int_type (*gptr ()); - } - - protected: - size_t bufsize_; - size_t putback_; - std::vector buf_; - }; - } -} - -#endif // WEB_APACHE_STREAM diff --git a/web/apache/stream.hxx b/web/apache/stream.hxx new file mode 100644 index 0000000..3bb422d --- /dev/null +++ b/web/apache/stream.hxx @@ -0,0 +1,149 @@ +// file : web/apache/stream.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_STREAM_HXX +#define WEB_APACHE_STREAM_HXX + +#include // request_rec, HTTP_* +#include // ap_*() + +#include // streamsize +#include +#include // memmove(), size_t +#include +#include // min(), max() + +#include // invalid_request + +namespace web +{ + namespace apache + { + // Object of a class implementing this interface is intended for keeping + // the state of communication with the client. + // + struct stream_state + { + // Called by istreambuf functions when content is about to be read from + // the client. Can throw invalid_request or sequence_error. + // + virtual void + set_read_state () = 0; + + // Called by ostreambuf functions when some content is about to be + // written to the client. Can throw invalid_request or sequence_error. + // + virtual void + set_write_state () = 0; + }; + + // Base class for ostreambuf and istreambuf. References request and + // communication state structures. + // + class rbuf: public std::streambuf + { + protected: + rbuf (request_rec* r, stream_state& s): rec_ (r), state_ (s) {} + + protected: + request_rec* rec_; + stream_state& state_; + }; + + class ostreambuf: public rbuf + { + public: + ostreambuf (request_rec* r, stream_state& s): rbuf (r, s) {} + + private: + virtual int_type + overflow (int_type c) + { + if (c != traits_type::eof ()) + { + state_.set_write_state (); + + char chr (c); + + // Throwing allows to distinguish comm failure from other IO error + // conditions. + // + if (ap_rwrite (&chr, sizeof (chr), rec_) == -1) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + } + + return c; + } + + virtual std::streamsize + xsputn (const char* s, std::streamsize num) + { + state_.set_write_state (); + + if (ap_rwrite (s, num, rec_) < 0) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + + return num; + } + + virtual int + sync () + { + if (ap_rflush (rec_) < 0) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + + return 0; + } + }; + + class istreambuf: public rbuf + { + public: + istreambuf (request_rec* r, + stream_state& s, + size_t bufsize = 1024, + size_t putback = 1) + : rbuf (r, s), + bufsize_ (std::max (bufsize, (size_t)1)), + putback_ (std::min (putback, bufsize_ - 1)), + buf_ (bufsize_) + { + char* p (buf_.data () + putback_); + setg (p, p, p); + } + + protected: + virtual int_type + underflow () + { + if (gptr () < egptr ()) + return traits_type::to_int_type (*gptr ()); + + state_.set_read_state (); + + size_t pb (std::min ((size_t)(gptr () - eback ()), putback_)); + std::memmove (buf_.data () + putback_ - pb, gptr () - pb, pb); + + char* p (buf_.data () + putback_); + int rb (ap_get_client_block (rec_, p, bufsize_ - putback_)); + + if (rb == 0) + return traits_type::eof (); + + if (rb < 0) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + + setg (p - pb, p, p + rb); + return traits_type::to_int_type (*gptr ()); + } + + protected: + size_t bufsize_; + size_t putback_; + std::vector buf_; + }; + } +} + +#endif // WEB_APACHE_STREAM_HXX diff --git a/web/mime-url-encoding b/web/mime-url-encoding deleted file mode 100644 index 9db0887..0000000 --- a/web/mime-url-encoding +++ /dev/null @@ -1,30 +0,0 @@ -// file : web/mime-url-encoding -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_MIME_URL_ENCODING -#define WEB_MIME_URL_ENCODING - -#include -#include - -namespace web -{ - // @@ Add the query flag (true by default). If true, then the encoding is - // applied to the URL query part, and so the plus character is used to - // encode the space character. Audit use cases afterwards. - // - void - mime_url_encode (const char* v, std::ostream& o); - - std::string - mime_url_encode (const char* v); - - std::string - mime_url_encode (const std::string& v); - - std::string - mime_url_decode (const char* b, const char* e, bool trim = false); -} - -#endif // WEB_MIME_URL_ENCODING diff --git a/web/mime-url-encoding.cxx b/web/mime-url-encoding.cxx index fa85dd3..e0022bc 100644 --- a/web/mime-url-encoding.cxx +++ b/web/mime-url-encoding.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // hex, uppercase, right #include diff --git a/web/mime-url-encoding.hxx b/web/mime-url-encoding.hxx new file mode 100644 index 0000000..d45f4ca --- /dev/null +++ b/web/mime-url-encoding.hxx @@ -0,0 +1,30 @@ +// file : web/mime-url-encoding.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_MIME_URL_ENCODING_HXX +#define WEB_MIME_URL_ENCODING_HXX + +#include +#include + +namespace web +{ + // @@ Add the query flag (true by default). If true, then the encoding is + // applied to the URL query part, and so the plus character is used to + // encode the space character. Audit use cases afterwards. + // + void + mime_url_encode (const char* v, std::ostream& o); + + std::string + mime_url_encode (const char* v); + + std::string + mime_url_encode (const std::string& v); + + std::string + mime_url_decode (const char* b, const char* e, bool trim = false); +} + +#endif // WEB_MIME_URL_ENCODING_HXX diff --git a/web/module b/web/module deleted file mode 100644 index 1e588a4..0000000 --- a/web/module +++ /dev/null @@ -1,267 +0,0 @@ -// file : web/module -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_MODULE -#define WEB_MODULE - -#include -#include -#include -#include -#include -#include // uint16_t -#include // size_t -#include // move() -#include // runtime_error - -#include -#include - -namespace web -{ - using butl::optional; - - // HTTP status code. - // - // @@ Define some commonly used constants? - // - using status_code = std::uint16_t; - - // This exception is used to signal that the request is invalid - // (4XX codes) rather than that it could not be processed (5XX). - // By default 400 is returned, which means the request is malformed. - // - // If caught by the web server implementation, it will try to return - // the specified status and content to the client, if possible. - // It is, however, may not be possible if some unbuffered content has - // already been written. The behavior in this case is implementation- - // specific and may result in no indication of an error being sent to - // the client. - // - struct invalid_request - { - status_code status; - std::string content; - std::string type; - - //@@ Maybe optional "try again" link? - // - invalid_request (status_code s = 400, - std::string c = "", - std::string t = "text/plain;charset=utf-8") - : status (s), content (std::move (c)), type (std::move (t)) {} - }; - - // Exception indicating HTTP request/response sequencing error. - // For example, trying to change the status code after some - // content has already been written. - // - struct sequence_error: std::runtime_error - { - sequence_error (std::string d): std::runtime_error (std::move (d)) {} - }; - - // Map of module configuration option names to the boolean flag indicating - // whether the value is expected for the option. - // - using option_descriptions = std::map; - - struct name_value - { - // These should eventually become string_view's. - // - std::string name; - optional value; - - name_value () {} - name_value (std::string n, optional v) - : name (std::move (n)), value (std::move (v)) {} - }; - - using name_values = std::vector; - using butl::path; - - class request - { - public: - using path_type = web::path; - - virtual - ~request () = default; - - // Corresponds to abs_path portion of HTTP URL as described in - // "3.2.2 HTTP URL" of http://tools.ietf.org/html/rfc2616. - // Returns '/' if no abs_path is present in URL. - // - virtual const path_type& - path () = 0; - - //@@ Why not pass parameters directly? Lazy parsing? - //@@ Why not have something like operator[] for lookup? Probably - // in name_values. - //@@ Maybe parameter_list() and parameter_map()? - // - // Throw invalid_request if decoding of any name or value fails. - // - virtual const name_values& - parameters () = 0; - - // Throw invalid_request if cookies are malformed. - // - virtual const name_values& - cookies () = 0; - - // Get the stream to read the request content from. If the limit argument - // is zero, then the content limit is left unchanged (unlimited initially). - // Otherwise the requested limit is set, and the invalid_request exception - // with the code 413 (payload too large) will be thrown when the specified - // limit is reached while reading from the stream. If the buffer argument - // is zero, then the buffer size is left unchanged (zero initially). If it - // is impossible to increase the buffer size (because, for example, some - // content is already read unbuffered), then the sequence_error is thrown. - // - // Note that unread input content is discarded when any unbuffered content - // is written, and any attempt to read it will result in the - // sequence_error exception being thrown. - // - virtual std::istream& - content (size_t limit = 0, size_t buffer = 0) = 0; - }; - - class response - { - public: - virtual - ~response () = default; - - // Set status code, content type, and get the stream to write - // the content to. If the buffer argument is true (default), - // then buffer the entire content before sending it as a - // response. This allows us to change the status code in - // case of an error. - // - // Specifically, if there is already content in the buffer - // and the status code is changed, then the old content is - // discarded. If the content was not buffered and the status - // is changed, then the sequence_error exception is thrown. - // If this exception leaves module::handle(), then the - // implementation shall terminate the response in a suitable - // but unspecified manner. In particular, there is no guarantee - // that the user will be notified of an error or observe the - // new status. - // - virtual std::ostream& - content (status_code code = 200, - const std::string& type = "application/xhtml+xml;charset=utf-8", - bool buffer = true) = 0; - - // Set status code without writing any content. On status change, - // discard buffered content or throw sequence_error if content was - // not buffered. - // - virtual void - status (status_code) = 0; - - // Throw sequence_error if some unbuffered content has already - // been written. - // - virtual void - cookie (const char* name, - const char* value, - const std::chrono::seconds* max_age = nullptr, - const char* path = nullptr, - const char* domain = nullptr, - bool secure = false, - bool buffer = true) = 0; - }; - - // A web server logging backend. The module can use it to log - // diagnostics that is meant for the web server operator rather - // than the user. - // - // The module can cast this basic interface to the web server's - // specific implementation that may provide a richer interface. - // - class log - { - public: - virtual - ~log () = default; - - virtual void - write (const char* msg) = 0; - }; - - // The web server creates a new module instance for each request - // by copy-initializing it with the module exemplar. This way we - // achieve two things: we can freely use module data members - // without worrying about multi-threading issues and we - // automatically get started with the initial state for each - // request. If you really need to share some rw-data between - // all the modules, use static data members with appropriate - // locking. See the header in one of the web server - // directories (e.g., apache/) if you need to see the code that - // does this. - // - class module - { - public: - virtual - ~module () = default; - - // Description of configuration options supported by this module. Note: - // should be callable during static initialization. - // - virtual option_descriptions - options () = 0; - - // During startup the web server calls this function on the module - // exemplar to log the module version information. It is up to the web - // server whether to call this function once per module implementation - // type. Therefore, it is expected that this function will log the same - // information for all the module exemplars. - // - virtual void - version (log&) = 0; - - // During startup the web server calls this function on the module - // exemplar passing a list of configuration options. The place these - // configuration options come from is implementation-specific (normally - // a configuration file). The web server guarantees that only options - // listed in the map returned by the options() function above can be - // present. Any exception thrown by this function terminates the web - // server. - // - virtual void - init (const name_values&, log&) = 0; - - // Return false if decline to handle the request. If handling have been - // declined after any unbuffered content has been written, then the - // implementation shall terminate the response in a suitable but - // unspecified manner. - // - // Throw retry if need to retry handling the request. The retry will - // happen on the same instance of the module and the implementation is - // expected to "rewind" the request and response objects to their initial - // state. This is only guaranteed to be possible if the relevant functions - // in the request and response objects were called in buffered mode (the - // buffer argument was true). - // - // Any exception other than retry and invalid_request described above that - // leaves this function is treated by the web server implementation as an - // internal server error (500). Similar to invalid_request, it will try to - // return the status and description (obtained by calling what() on - // std::exception) to the client, if possible. The description is assume - // to be encoded in UTF-8. The implementation may provide a configuration - // option to omit the description from the response, for security/privacy - // reasons. - // - struct retry {}; - - virtual bool - handle (request&, response&, log&) = 0; - }; -} - -#endif // WEB_MODULE diff --git a/web/module.hxx b/web/module.hxx new file mode 100644 index 0000000..1864673 --- /dev/null +++ b/web/module.hxx @@ -0,0 +1,267 @@ +// file : web/module.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_MODULE_HXX +#define WEB_MODULE_HXX + +#include +#include +#include +#include +#include +#include // uint16_t +#include // size_t +#include // move() +#include // runtime_error + +#include +#include + +namespace web +{ + using butl::optional; + + // HTTP status code. + // + // @@ Define some commonly used constants? + // + using status_code = std::uint16_t; + + // This exception is used to signal that the request is invalid + // (4XX codes) rather than that it could not be processed (5XX). + // By default 400 is returned, which means the request is malformed. + // + // If caught by the web server implementation, it will try to return + // the specified status and content to the client, if possible. + // It is, however, may not be possible if some unbuffered content has + // already been written. The behavior in this case is implementation- + // specific and may result in no indication of an error being sent to + // the client. + // + struct invalid_request + { + status_code status; + std::string content; + std::string type; + + //@@ Maybe optional "try again" link? + // + invalid_request (status_code s = 400, + std::string c = "", + std::string t = "text/plain;charset=utf-8") + : status (s), content (std::move (c)), type (std::move (t)) {} + }; + + // Exception indicating HTTP request/response sequencing error. + // For example, trying to change the status code after some + // content has already been written. + // + struct sequence_error: std::runtime_error + { + sequence_error (std::string d): std::runtime_error (std::move (d)) {} + }; + + // Map of module configuration option names to the boolean flag indicating + // whether the value is expected for the option. + // + using option_descriptions = std::map; + + struct name_value + { + // These should eventually become string_view's. + // + std::string name; + optional value; + + name_value () {} + name_value (std::string n, optional v) + : name (std::move (n)), value (std::move (v)) {} + }; + + using name_values = std::vector; + using butl::path; + + class request + { + public: + using path_type = web::path; + + virtual + ~request () = default; + + // Corresponds to abs_path portion of HTTP URL as described in + // "3.2.2 HTTP URL" of http://tools.ietf.org/html/rfc2616. + // Returns '/' if no abs_path is present in URL. + // + virtual const path_type& + path () = 0; + + //@@ Why not pass parameters directly? Lazy parsing? + //@@ Why not have something like operator[] for lookup? Probably + // in name_values. + //@@ Maybe parameter_list() and parameter_map()? + // + // Throw invalid_request if decoding of any name or value fails. + // + virtual const name_values& + parameters () = 0; + + // Throw invalid_request if cookies are malformed. + // + virtual const name_values& + cookies () = 0; + + // Get the stream to read the request content from. If the limit argument + // is zero, then the content limit is left unchanged (unlimited initially). + // Otherwise the requested limit is set, and the invalid_request exception + // with the code 413 (payload too large) will be thrown when the specified + // limit is reached while reading from the stream. If the buffer argument + // is zero, then the buffer size is left unchanged (zero initially). If it + // is impossible to increase the buffer size (because, for example, some + // content is already read unbuffered), then the sequence_error is thrown. + // + // Note that unread input content is discarded when any unbuffered content + // is written, and any attempt to read it will result in the + // sequence_error exception being thrown. + // + virtual std::istream& + content (size_t limit = 0, size_t buffer = 0) = 0; + }; + + class response + { + public: + virtual + ~response () = default; + + // Set status code, content type, and get the stream to write + // the content to. If the buffer argument is true (default), + // then buffer the entire content before sending it as a + // response. This allows us to change the status code in + // case of an error. + // + // Specifically, if there is already content in the buffer + // and the status code is changed, then the old content is + // discarded. If the content was not buffered and the status + // is changed, then the sequence_error exception is thrown. + // If this exception leaves module::handle(), then the + // implementation shall terminate the response in a suitable + // but unspecified manner. In particular, there is no guarantee + // that the user will be notified of an error or observe the + // new status. + // + virtual std::ostream& + content (status_code code = 200, + const std::string& type = "application/xhtml+xml;charset=utf-8", + bool buffer = true) = 0; + + // Set status code without writing any content. On status change, + // discard buffered content or throw sequence_error if content was + // not buffered. + // + virtual void + status (status_code) = 0; + + // Throw sequence_error if some unbuffered content has already + // been written. + // + virtual void + cookie (const char* name, + const char* value, + const std::chrono::seconds* max_age = nullptr, + const char* path = nullptr, + const char* domain = nullptr, + bool secure = false, + bool buffer = true) = 0; + }; + + // A web server logging backend. The module can use it to log + // diagnostics that is meant for the web server operator rather + // than the user. + // + // The module can cast this basic interface to the web server's + // specific implementation that may provide a richer interface. + // + class log + { + public: + virtual + ~log () = default; + + virtual void + write (const char* msg) = 0; + }; + + // The web server creates a new module instance for each request + // by copy-initializing it with the module exemplar. This way we + // achieve two things: we can freely use module data members + // without worrying about multi-threading issues and we + // automatically get started with the initial state for each + // request. If you really need to share some rw-data between + // all the modules, use static data members with appropriate + // locking. See the header in one of the web server + // directories (e.g., apache/) if you need to see the code that + // does this. + // + class module + { + public: + virtual + ~module () = default; + + // Description of configuration options supported by this module. Note: + // should be callable during static initialization. + // + virtual option_descriptions + options () = 0; + + // During startup the web server calls this function on the module + // exemplar to log the module version information. It is up to the web + // server whether to call this function once per module implementation + // type. Therefore, it is expected that this function will log the same + // information for all the module exemplars. + // + virtual void + version (log&) = 0; + + // During startup the web server calls this function on the module + // exemplar passing a list of configuration options. The place these + // configuration options come from is implementation-specific (normally + // a configuration file). The web server guarantees that only options + // listed in the map returned by the options() function above can be + // present. Any exception thrown by this function terminates the web + // server. + // + virtual void + init (const name_values&, log&) = 0; + + // Return false if decline to handle the request. If handling have been + // declined after any unbuffered content has been written, then the + // implementation shall terminate the response in a suitable but + // unspecified manner. + // + // Throw retry if need to retry handling the request. The retry will + // happen on the same instance of the module and the implementation is + // expected to "rewind" the request and response objects to their initial + // state. This is only guaranteed to be possible if the relevant functions + // in the request and response objects were called in buffered mode (the + // buffer argument was true). + // + // Any exception other than retry and invalid_request described above that + // leaves this function is treated by the web server implementation as an + // internal server error (500). Similar to invalid_request, it will try to + // return the status and description (obtained by calling what() on + // std::exception) to the client, if possible. The description is assume + // to be encoded in UTF-8. The implementation may provide a configuration + // option to omit the description from the response, for security/privacy + // reasons. + // + struct retry {}; + + virtual bool + handle (request&, response&, log&) = 0; + }; +} + +#endif // WEB_MODULE_HXX diff --git a/web/xhtml b/web/xhtml deleted file mode 100644 index 58dee7b..0000000 --- a/web/xhtml +++ /dev/null @@ -1,351 +0,0 @@ -// file : web/xhtml -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_XHTML -#define WEB_XHTML - -#include - -namespace web -{ - // "Canonical" XHTML5 vocabulary. - // - // * One-letter tag names and local variable clash problem - // - // a at|an|an anc anch - // b bt|bo|bl bld bold - // i it|it|it itl ital - // p pt|pr|pr par para - // q qt|qu|qt quo quot - // s st|st|st stk strk - // u ut|un|un unl undr - // - // Other options: - // - _a, a_, xa - // - A, I - // - x::i - // - user-defined literals: "a"_e, "/a"_e, "id"_a - // - // Things can actually get much worse, consider: - // - // int i; - // s << i << "text" << ~i; - // - // So perhaps this is the situation where the explicit namespace - // qualification (e.g., x::p) is the only robust option? - // - // - // * Element/attribute name clash problem (e.g., STYLE) - // - // - some attribute/element name decorator (STYLEA, STYLE_A, STYLE_) - // - rename attribute/element (e.g., STYLEDEF or CSSSTYLE[adds TYPE]); - // in case of STYLE we should probably rename the element since - // attribute will be much more frequently used. - // - "scope" attributes inside elements (P::STYLE); somewhat - // burdensome: P(P::STYLE); could then use low-case names - // for attributes - // - "scope" elements inside other elements (HEAD::STYLE); also - // burdensome. - // - // - // * Text wrapping/indentation - // - // For some (inline) elements we want additional indentation: - // - // 1. Indent content on newline (e.g., for