diff options
42 files changed, 717 insertions, 499 deletions
diff --git a/brep/buildfile b/brep/buildfile index b879b2d..0369098 100644 --- a/brep/buildfile +++ b/brep/buildfile @@ -4,7 +4,10 @@ using cli -.: libso{brep brep-apache} +# @@ The target should actually be mod_brep.so, not libmod_brep.so. Such +# customization is not supported yet. +# +.: libso{brep mod_brep} # brep library build rules. # @@ -17,25 +20,26 @@ brep = cxx{package package-traits package-odb} libso{brep}: $brep $libs libso{brep}: cxx.export.poptions = -I$out_root -I$src_root -# brep-apache library build rules. +# mod_brep library build rules. # import libs += libstudxml%lib{studxml} -brep = cxx{diagnostics module services repository-root package-search \ - package-details package-version-details repository-details \ - shared-database page types-parsers} \ +brep = cxx{services diagnostics module repository-root package-search \ + package-details package-version-details repository-details \ + shared-database page types-parsers} \ cli.cxx{options} web = ../web/apache/cxx{request service} ../web/cxx{mime-url-encoding} -libso{brep-apache}: $brep $web libso{brep} $libs +libso{mod_brep}: $brep $web libso{brep} $libs # Set option prefix to the empty value to handle all unknown request parameters # uniformly with a single catch block. # -cli.options += -I $src_root --include-with-brackets --include-prefix brep \ ---guard-prefix BREP --cxx-prologue "#include <brep/types-parsers>" \ +cli.options += --std c++11 -I $src_root --include-with-brackets \ +--include-prefix brep --guard-prefix BREP \ +--cxx-prologue "#include <brep/types-parsers>" \ --cli-namespace brep::cli --generate-file-scanner --suppress-usage \ ---option-prefix "" +--generate-modifier --generate-description --option-prefix "" cli.cxx{options}: cli{options} diff --git a/brep/module b/brep/module index 295744d..4ac6023 100644 --- a/brep/module +++ b/brep/module @@ -9,6 +9,7 @@ #include <brep/types> #include <brep/utility> + #include <brep/options> #include <brep/diagnostics> @@ -23,6 +24,7 @@ namespace brep using web::status_code; using web::invalid_request; using web::sequence_error; + using web::option_descriptions; using web::name_value; using web::name_values; using web::request; @@ -94,12 +96,24 @@ namespace brep module (); module (const module& ); + static name_values + filter (const name_values&, const option_descriptions&); + + static option_descriptions + convert (const cli::options&); + + static void + append (option_descriptions& dst, const cli::options& src); + + static void + append (option_descriptions& dst, const option_descriptions& src); + // Can be used by module implementation to parse HTTP request parameters. // - class param_scanner: public cli::scanner + class name_value_scanner: public cli::scanner { public: - param_scanner (const name_values&) noexcept; + name_value_scanner (const name_values&) noexcept; virtual bool more (); @@ -120,36 +134,43 @@ namespace brep }; public: - // Can be called normally by the web server or our custom request - // dispatching mechanism. - // - virtual void - handle (request&, response&) = 0; + virtual const cli::options& + cli_options () const = 0; - bool - loaded () const noexcept {return loaded_;} + virtual void + init (cli::scanner&) = 0; - private: + // Can be overriden by custom request dispatcher to initialize sub-modules. + // virtual void - handle (request&, response&, log&); + init (const name_values&); virtual void init (const name_values&, log&); - // Can be overriden by module implementation which has configuration - // options. + virtual bool + handle (request&, response&) = 0; + + // web::module interface. // - virtual void - init (cli::scanner& s) - { - // Just scan options to ensure there is no misspelled ones. - // - options::module o (s, cli::unknown_mode::fail, cli::unknown_mode::fail); - } + public: + // Custom request dispatcher can aggregate its own option descriptions with + // sub-modules option descriptions. In this case it should still call the + // base implementation in order to include the brep::module's options. + // + virtual option_descriptions + options (); + + private: + virtual bool + handle (request&, response&, log&); + + name_values + expand_options (const name_values&); // Diagnostics implementation details. // - private: + protected: log* log_ {nullptr}; // Diagnostics backend provided by the web server. private: diff --git a/brep/module.cxx b/brep/module.cxx index 68177b1..253d807 100644 --- a/brep/module.cxx +++ b/brep/module.cxx @@ -18,6 +18,7 @@ #include <brep/types> #include <brep/utility> + #include <brep/options> using namespace std; @@ -29,7 +30,7 @@ namespace brep // module // - void module:: + bool module:: handle (request& rq, response& rs, log& l) { assert (loaded_); @@ -38,7 +39,7 @@ namespace brep try { - handle (rq, rs); + return handle (rq, rs); } catch (const server_error& e) { @@ -74,24 +75,77 @@ namespace brep // it. } } + + return true; + } + + option_descriptions module:: + convert (const cli::options& o) + { + option_descriptions r; + append (r, o); + return r; } - // Parse options with a cli-generated scanner. Options verb and conf are - // recognized by brep::module::init while others to be interpreted by the - // derived class init method. If there is an option which can not be - // interpreted not by brep::module::init nor by derived class init method - // then web server is terminated with a corresponding error message being - // logged. - // void module:: - init (const name_values& options, log& log) + append (option_descriptions& dst, const cli::options& src) { - assert (!loaded_); + for (const auto& o: src) + { + bool v (!o.flag ()); + auto i (dst.emplace (o.name (), v)); + assert (i.first->second == v); // Consistent option/flag. - log_ = &log; - vector<const char*> argv; + for (const auto& a: o.aliases ()) + { + i = dst.emplace (a, v); + assert (i.first->second == v); + } + } + } + + void module:: + append (option_descriptions& dst, const option_descriptions& src) + { + for (const auto& o: src) + { + auto i (dst.emplace (o)); + assert (i.first->second == o.second); // Consistent option/flag. + } + } + + name_values module:: + filter (const name_values& v, const option_descriptions& d) + { + name_values r; + for (const auto& nv: v) + { + if (d.find (nv.name) != d.end ()) + r.push_back (nv); + } + + return r; + } + + // Convert CLI option descriptions to the general interface of option + // descriptions, extend with brep::module own option descriptions. + // + option_descriptions module:: + options () + { + option_descriptions r ({{"conf", true}}); + append (r, options::module::description ()); + append (r, cli_options ()); + return r; + } - for (const auto& nv: options) + // Expand option list parsing configuration files. + // + name_values module:: + expand_options (const name_values& v) + { + vector<const char*> argv; + for (const auto& nv: v) { argv.push_back (nv.name.c_str ()); @@ -100,29 +154,60 @@ namespace brep } int argc (argv.size ()); + argv_file_scanner s (0, argc, const_cast<char**> (argv.data ()), "conf"); + + name_values r; + const option_descriptions& o (options ()); + + while (s.more ()) + { + string n (s.next ()); + auto i (o.find (n)); + + if (i == o.end ()) + throw unknown_argument (n); + + optional<string> v; + if (i->second) + v = s.next (); + + r.emplace_back (move (n), move (v)); + } + + return r; + } + + // Parse options with a cli-generated scanner. Options verb and conf are + // recognized by brep::module::init while others to be interpreted by the + // derived init(). If there is an option which can not be interpreted + // neither by brep::module nor by the derived class, then the web server + // is terminated with a corresponding error message being logged. Though + // this should not happen if the options() function returned the correct + // set of options. + // + void module:: + init (const name_values& options, log& log) + { + assert (!loaded_); + + log_ = &log; try { - { - // Read module implementation configuration. - // - argv_file_scanner s (0, - argc, - const_cast<char**> (argv.data ()), - "conf"); + name_values opts (expand_options (options)); - init (s); - } + // Read module implementation configuration. + // + init (opts); // Read brep::module configuration. // - argv_file_scanner s (0, - argc, - const_cast<char**> (argv.data ()), - "conf"); + static option_descriptions od (convert (options::module::description ())); + name_values mo (filter (opts, od)); + name_value_scanner s (mo); + options::module o (s, unknown_mode::fail, unknown_mode::fail); - options::module o (s, unknown_mode::skip, unknown_mode::skip); - verb_ = o.verb (); + verb_ = o.log_verbosity (); loaded_ = true; } catch (const server_error& e) @@ -138,6 +223,14 @@ namespace brep } } + void module:: + init (const name_values& options) + { + name_value_scanner s (options); + init (s); + assert (!s.more ()); // Module didn't handle its options. + } + module:: module (): log_writer_ (bind (&module::log_write, this, _1)) {} @@ -208,7 +301,7 @@ namespace brep //@@ Cast log_ to apache::log and write the records. // - auto al (dynamic_cast<::web::apache::log*> (log_)); + auto al (dynamic_cast<web::apache::log*> (log_)); if (al) { @@ -240,23 +333,23 @@ namespace brep } } - // module::param_scanner + // module::name_value_scanner // - module::param_scanner:: - param_scanner (const name_values& nv) noexcept + module::name_value_scanner:: + name_value_scanner (const name_values& nv) noexcept : name_values_ (nv), i_ (nv.begin ()), name_ (true) { } - bool module::param_scanner:: + bool module::name_value_scanner:: more () { return i_ != name_values_.end (); } - const char* module::param_scanner:: + const char* module::name_value_scanner:: peek () { if (i_ != name_values_.end ()) @@ -265,7 +358,7 @@ namespace brep throw eos_reached (); } - const char* module::param_scanner:: + const char* module::name_value_scanner:: next () { if (i_ != name_values_.end ()) @@ -278,7 +371,7 @@ namespace brep throw eos_reached (); } - void module::param_scanner:: + void module::name_value_scanner:: skip () { if (i_ != name_values_.end ()) diff --git a/brep/options.cli b/brep/options.cli index fa05b56..d5041c8 100644 --- a/brep/options.cli +++ b/brep/options.cli @@ -3,6 +3,7 @@ // license : MIT; see accompanying LICENSE file include <brep/types>; + include <brep/options-types>; namespace brep @@ -11,10 +12,12 @@ namespace brep // namespace options { + // Option groups. + // class module { - uint16_t verb; - dir_path root; + uint16_t log-verbosity; + dir_path root = "/"; }; class db @@ -23,28 +26,39 @@ namespace brep uint16_t db-port = 5432; }; - class package_search: module, db + class search + { + uint16_t search-results = 10; + uint16_t pager-pages = 5; + }; + + class package + { + uint16_t description-len = 500; // ~ 80 chars x 6 lines. + uint16_t changes-len = 5000; // ~ 80 chars x 60 lines. + }; + + // Module options. + // + class package_search: module, db, search { - uint16_t results-on-page = 10; - uint16_t pages-in-pager = 5; }; - class package_details: module, db + class package_details: module, db, search, package { - uint16_t results-on-page = 10; - uint16_t pages-in-pager = 5; - uint16_t description-length = 500; // ~ 80 chars x 6 lines. }; - class package_version_details: module, db + class package_version_details: module, db, package { - uint16_t description-length = 500; // ~ 80 chars x 6 lines. - uint16_t changes-length = 5000; // ~ 80 chars x 60 lines. }; class repository_details: module, db { }; + + class repository_root: module + { + }; } // Web module HTTP request parameters. diff --git a/brep/package-details b/brep/package-details index 214a0fe..6766797 100644 --- a/brep/package-details +++ b/brep/package-details @@ -8,6 +8,7 @@ #include <odb/forward.hxx> // database #include <brep/types> + #include <brep/module> #include <brep/options> @@ -15,10 +16,14 @@ namespace brep { class package_details: public module { - private: - virtual void + public: + virtual bool handle (request&, response&); + virtual const cli::options& + cli_options () const {return options::package_details::description ();} + + private: virtual void init (cli::scanner&); diff --git a/brep/package-details.cxx b/brep/package-details.cxx index 4b99feb..30e185e 100644 --- a/brep/package-details.cxx +++ b/brep/package-details.cxx @@ -14,9 +14,10 @@ #include <web/module> #include <web/mime-url-encoding> -#include <brep/page> #include <brep/types> #include <brep/utility> + +#include <brep/page> #include <brep/options> #include <brep/package> #include <brep/package-odb> @@ -33,6 +34,9 @@ init (scanner& s) options_ = make_shared<options::package_details> ( s, unknown_mode::fail, unknown_mode::fail); + if (options_->root ().empty ()) + options_->root (dir_path ("/")); + db_ = shared_database (options_->db_host (), options_->db_port ()); } @@ -51,7 +55,7 @@ search_params (const brep::string& n, const brep::string& q) ")"; } -void brep::package_details:: +bool brep::package_details:: handle (request& rq, response& rs) { using namespace web; @@ -62,11 +66,8 @@ handle (request& rq, response& rs) // The module options object is not changed after being created once per // server process. // - static const size_t res_page (options_->results_on_page ()); - static const dir_path& root ( - options_->root ().empty () - ? dir_path ("/") - : options_->root ()); + static const size_t res_page (options_->search_results ()); + static const dir_path& root (options_->root ()); const string& name (*rq.path ().rbegin ()); const string ename (mime_url_encode (name)); @@ -76,7 +77,7 @@ handle (request& rq, response& rs) try { - param_scanner s (rq.parameters ()); + name_value_scanner s (rq.parameters ()); params = params::package_details ( s, unknown_mode::fail, unknown_mode::fail); @@ -159,7 +160,7 @@ handle (request& rq, response& rs) if (const auto& d = pkg->description) s << (full ? P_DESCRIPTION (*d, id) - : P_DESCRIPTION (*d, options_->description_length (), + : P_DESCRIPTION (*d, options_->description_len (), url (!full, squery, page, id))); s << TABLE(CLASS="proplist", ID="package") @@ -231,9 +232,11 @@ handle (request& rq, response& rs) t.commit (); - s << DIV_PAGER (page, pkg_count, res_page, options_->pages_in_pager (), + s << DIV_PAGER (page, pkg_count, res_page, options_->pager_pages (), url (full, squery)) << ~DIV << ~BODY << ~HTML; + + return true; } diff --git a/brep/package-search b/brep/package-search index a5c441e..63ec6e0 100644 --- a/brep/package-search +++ b/brep/package-search @@ -8,6 +8,7 @@ #include <odb/forward.hxx> // database #include <brep/types> + #include <brep/module> #include <brep/options> @@ -15,10 +16,14 @@ namespace brep { class package_search: public module { - private: - virtual void + public: + virtual bool handle (request&, response&); + virtual const cli::options& + cli_options () const {return options::package_search::description ();} + + private: virtual void init (cli::scanner&); diff --git a/brep/package-search.cxx b/brep/package-search.cxx index f2f922c..62b162b 100644 --- a/brep/package-search.cxx +++ b/brep/package-search.cxx @@ -14,9 +14,10 @@ #include <web/module> #include <web/mime-url-encoding> -#include <brep/page> #include <brep/types> #include <brep/utility> + +#include <brep/page> #include <brep/options> #include <brep/package> #include <brep/package-odb> @@ -33,6 +34,9 @@ init (scanner& s) options_ = make_shared<options::package_search> ( s, unknown_mode::fail, unknown_mode::fail); + if (options_->root ().empty ()) + options_->root (dir_path ("/")); + db_ = shared_database (options_->db_host (), options_->db_port ()); } @@ -48,7 +52,7 @@ search_param (const brep::string& q) ")"; } -void brep::package_search:: +bool brep::package_search:: handle (request& rq, response& rs) { using namespace web::xhtml; @@ -58,17 +62,14 @@ handle (request& rq, response& rs) // The module options object is not changed after being created once per // server process. // - static const size_t res_page (options_->results_on_page ()); - static const dir_path& root ( - options_->root ().empty () - ? dir_path ("/") - : options_->root ()); + static const size_t res_page (options_->search_results ()); + static const dir_path& root (options_->root ()); params::package_search params; try { - param_scanner s (rq.parameters ()); + name_value_scanner s (rq.parameters ()); params = params::package_search (s, unknown_mode::fail, unknown_mode::fail); } catch (const unknown_argument& e) @@ -137,9 +138,11 @@ handle (request& rq, response& rs) t.commit (); - s << DIV_PAGER (page, pkg_count, res_page, options_->pages_in_pager (), + s << DIV_PAGER (page, pkg_count, res_page, options_->pager_pages (), root.string () + squery_param) << ~DIV << ~BODY << ~HTML; + + return true; } diff --git a/brep/package-version-details b/brep/package-version-details index 36b7420..8057097 100644 --- a/brep/package-version-details +++ b/brep/package-version-details @@ -8,6 +8,7 @@ #include <odb/forward.hxx> // database #include <brep/types> + #include <brep/module> #include <brep/options> @@ -15,10 +16,17 @@ namespace brep { class package_version_details: public module { - private: - virtual void + public: + virtual bool handle (request&, response&); + virtual const cli::options& + cli_options () const + { + return options::package_version_details::description (); + } + + private: virtual void init (cli::scanner&); diff --git a/brep/package-version-details.cxx b/brep/package-version-details.cxx index a0ad7d4..8f6cd7d 100644 --- a/brep/package-version-details.cxx +++ b/brep/package-version-details.cxx @@ -16,9 +16,10 @@ #include <web/module> #include <web/mime-url-encoding> -#include <brep/page> #include <brep/types> #include <brep/utility> + +#include <brep/page> #include <brep/options> #include <brep/package> #include <brep/package-odb> @@ -36,10 +37,13 @@ init (scanner& s) options_ = make_shared<options::package_version_details> ( s, unknown_mode::fail, unknown_mode::fail); + if (options_->root ().empty ()) + options_->root (dir_path ("/")); + db_ = shared_database (options_->db_host (), options_->db_port ()); } -void brep::package_version_details:: +bool brep::package_version_details:: handle (request& rq, response& rs) { using namespace web; @@ -50,10 +54,7 @@ handle (request& rq, response& rs) // The module options object is not changed after being created once per // server process. // - static const dir_path& root ( - options_->root ().empty () - ? dir_path ("/") - : options_->root ()); + static const dir_path& root (options_->root ()); auto i (rq.path ().rbegin ()); version ver; @@ -77,7 +78,7 @@ handle (request& rq, response& rs) try { - param_scanner s (rq.parameters ()); + name_value_scanner s (rq.parameters ()); params = params::package_version_details ( s, unknown_mode::fail, unknown_mode::fail); @@ -151,7 +152,7 @@ handle (request& rq, response& rs) if (const auto& d = pkg->description) s << (full ? P_DESCRIPTION (*d, id) - : P_DESCRIPTION (*d, options_->description_length (), + : P_DESCRIPTION (*d, options_->description_len (), url (!full, id))); assert (pkg->location); @@ -301,10 +302,11 @@ handle (request& rq, response& rs) s << H3 << "Changes" << ~H3 << (full ? PRE_CHANGES (ch) - : PRE_CHANGES (ch, options_->description_length (), - url (!full, "changes"))); + : PRE_CHANGES (ch, options_->changes_len (), url (!full, "changes"))); s << ~DIV << ~BODY << ~HTML; + + return true; } diff --git a/brep/package.cxx b/brep/package.cxx index ecaa369..c221fe8 100644 --- a/brep/package.cxx +++ b/brep/package.cxx @@ -10,6 +10,7 @@ #include <brep/types> #include <brep/utility> + #include <brep/package-odb> using namespace std; @@ -8,6 +8,7 @@ #include <xml/forward> #include <brep/types> + #include <brep/package> namespace brep diff --git a/brep/page.cxx b/brep/page.cxx index 780d759..638a72b 100644 --- a/brep/page.cxx +++ b/brep/page.cxx @@ -17,6 +17,7 @@ #include <brep/types> #include <brep/utility> + #include <brep/package> #include <brep/package-odb> diff --git a/brep/repository-details b/brep/repository-details index fb41e32..7efc6a6 100644 --- a/brep/repository-details +++ b/brep/repository-details @@ -8,6 +8,7 @@ #include <odb/forward.hxx> // database #include <brep/types> + #include <brep/module> #include <brep/options> @@ -15,10 +16,14 @@ namespace brep { class repository_details: public module { - private: - virtual void + public: + virtual bool handle (request&, response&); + virtual const cli::options& + cli_options () const {return options::repository_details::description ();} + + private: virtual void init (cli::scanner&); diff --git a/brep/repository-details.cxx b/brep/repository-details.cxx index eb2885c..ee34dc2 100644 --- a/brep/repository-details.cxx +++ b/brep/repository-details.cxx @@ -13,9 +13,10 @@ #include <web/module> #include <web/mime-url-encoding> -#include <brep/page> #include <brep/types> #include <brep/utility> + +#include <brep/page> #include <brep/options> #include <brep/package> #include <brep/package-odb> @@ -32,10 +33,13 @@ init (scanner& s) options_ = make_shared<options::repository_details> ( s, unknown_mode::fail, unknown_mode::fail); + if (options_->root ().empty ()) + options_->root (dir_path ("/")); + db_ = shared_database (options_->db_host (), options_->db_port ()); } -void brep::repository_details:: +bool brep::repository_details:: handle (request& rq, response& rs) { using namespace web::xhtml; @@ -45,16 +49,13 @@ handle (request& rq, response& rs) // The module options object is not changed after being created once per // server process. // - static const dir_path& root ( - options_->root ().empty () - ? dir_path ("/") - : options_->root ()); + static const dir_path& root (options_->root ()); // Make sure no parameters passed. // try { - param_scanner s (rq.parameters ()); + name_value_scanner s (rq.parameters ()); params::repository_details (s, unknown_mode::fail, unknown_mode::fail); } catch (const unknown_argument& e) @@ -106,4 +107,6 @@ handle (request& rq, response& rs) s << ~DIV << ~BODY << ~HTML; + + return true; } diff --git a/brep/repository-root b/brep/repository-root index e5fba78..2397764 100644 --- a/brep/repository-root +++ b/brep/repository-root @@ -5,26 +5,45 @@ #ifndef BREP_REPOSITORY_ROOT #define BREP_REPOSITORY_ROOT +#include <brep/types> + #include <brep/module> +#include <brep/options> namespace brep { class package_search; + class package_details; + class package_version_details; class repository_details; class repository_root: public module { public: - repository_root (package_search& ps, repository_details& rd) - : package_search_ (ps), repository_details_ (rd) {} + repository_root (); private: - virtual void + virtual bool handle (request&, response&); + virtual const cli::options& + cli_options () const {return options::repository_root::description ();} + + virtual option_descriptions + options (); + + virtual void + init (const name_values&); + + virtual void + init (cli::scanner&); + private: - package_search& package_search_; - repository_details& repository_details_; + shared_ptr<package_search> package_search_; + shared_ptr<package_details> package_details_; + shared_ptr<package_version_details> package_version_details_; + shared_ptr<repository_details> repository_details_; + shared_ptr<options::repository_root> options_; }; } diff --git a/brep/repository-root.cxx b/brep/repository-root.cxx index 180d61f..7af76b2 100644 --- a/brep/repository-root.cxx +++ b/brep/repository-root.cxx @@ -4,17 +4,22 @@ #include <brep/repository-root> -#include <map> -#include <functional> +#include <sstream> #include <web/module> #include <brep/types> #include <brep/utility> + +#include <brep/module> +#include <brep/options> #include <brep/package-search> +#include <brep/package-details> #include <brep/repository-details> +#include <brep/package-version-details> using namespace std; +using namespace brep::cli; namespace brep { @@ -45,75 +50,141 @@ namespace brep // repository_root // + repository_root:: + repository_root () + : package_search_ (make_shared<package_search> ()), + package_details_ (make_shared<package_details> ()), + package_version_details_ (make_shared<package_version_details> ()), + repository_details_ (make_shared<repository_details> ()) + { + } + + // Return amalgamation of repository_root and all its sub-modules option + // descriptions. + // + option_descriptions repository_root:: + options () + { + option_descriptions r (module::options ()); + append (r, package_search_->options ()); + append (r, package_details_->options ()); + append (r, package_version_details_->options ()); + append (r, repository_details_->options ()); + return r; + } + + // Initialize sub-modules and parse own configuration options. + // void repository_root:: - handle (request& rq, response& rs) + init (const name_values& v) { - MODULE_DIAG; + auto sub_init ([this, &v](module& m) + { + m.init (filter (v, m.options ()), *log_); + }); - // Dispatch request handling to the appropriate module depending on the - // function name passed as a first HTTP request parameter. The parameter - // should have no value specified. If no function name is passed, - // the default handler is selected. Example: cppget.org/?about + // Initialize sub-modules. // + sub_init (*package_search_); + sub_init (*package_details_); + sub_init (*package_version_details_); + sub_init (*repository_details_); - string func; - name_values params (rq.parameters ()); - - // Obtain the function name. + // Parse own configuration options. // - if (!params.empty () && !params.front ().value) - { - func = move (params.front ().name); + module::init ( + filter (v, convert (options::repository_root::description ()))); + } - // Cleanup not to confuse the selected handler with the unknown parameter. - // - params.erase (params.begin ()); - } + void repository_root:: + init (scanner& s) + { + MODULE_DIAG; - // To handle the request a new module instance is created as a copy of - // the corresponsing exemplar. - // - using module_ptr = unique_ptr<module>; + options_ = make_shared<options::repository_root> ( + s, unknown_mode::fail, unknown_mode::fail); - // Function name to module factory map. - // - const map<string, function<module_ptr()>> - handlers ({ - { - "about", - [this]() -> module_ptr - {return module_ptr (new repository_details (repository_details_));} - }, - { - string (), // The default handler. - [this]() -> module_ptr - {return module_ptr (new package_search (package_search_));} - }}); + if (options_->root ().empty ()) + options_->root (dir_path ("/")); + } - // Find proper handler. - // - auto i (handlers.find (func)); - if (i == handlers.end ()) - throw invalid_request (400, "unknown function"); + bool repository_root:: + handle (request& rq, response& rs) + { + MODULE_DIAG; - module_ptr m (i->second ()); - if (m->loaded ()) + static const dir_path& root (options_->root ()); + + const path& rpath (rq.path ()); + if (!rpath.sub (root)) + return false; + + const path& lpath (rpath.leaf (root)); + + // @@ An exception thrown by the selected module handle () function call + // will be attributed to the repository-root service while being logged. + // Could intercept exception handling to add some sub-module attribution, + // but let's not complicate the code for the time being. + // + if (lpath.empty ()) { - // Delegate request handling. - // - // @@ An exception thrown by the handler will be attributed to the - // repository-root service while being logged. Could intercept - // exception handling to fix that, but let's not complicate the - // code for the time being. + // Dispatch request handling to the repository_details or the + // package_search module depending on the function name passed as a + // first HTTP request parameter. The parameter should have no value + // specified. Example: cppget.org/?about // - // - request_proxy rqp (rq, params); - m->handle (rqp, rs); + const name_values& params (rq.parameters ()); + if (!params.empty () && !params.front ().value) + { + if (params.front ().name == "about") + { + // Cleanup not to confuse the selected module with the unknown + // parameter. + // + name_values p (params); + p.erase (p.begin ()); + + request_proxy rp (rq, p); + repository_details m (*repository_details_); + return m.handle (rp, rs); + } + + throw invalid_request (400, "unknown function"); + } + else + { + package_search m (*package_search_); + return m.handle (rq, rs); + } } else - // The module is not loaded, presumably being disabled in the web server - // configuration file. + { + // Dispatch request handling to the package_details or the + // package_version_details module depending on the HTTP request URL path. + // + auto i (lpath.begin ()); + assert (i != lpath.end ()); + + const string& n (*i++); // Package name. + + // Check if this is a package name and not a brep static content files + // (CSS) directory name or a repository directory name. // - throw invalid_request (404, "handler not available"); + if (n != "@" && n.find_first_not_of ("0123456789") != string::npos) + { + if (i == lpath.end ()) + { + package_details m (*package_details_); + return m.handle (rq, rs); + } + else if (++i == lpath.end ()) + { + package_version_details m (*package_version_details_); + return m.handle (rq, rs); + } + } + } + + return false; } } diff --git a/brep/services.cxx b/brep/services.cxx index b214a09..dcac22c 100644 --- a/brep/services.cxx +++ b/brep/services.cxx @@ -6,41 +6,7 @@ #include <web/apache/service> -#include <brep/package-search> -#include <brep/package-details> #include <brep/repository-root> -#include <brep/repository-details> -#include <brep/package-version-details> -using namespace brep; -using web::apache::service; - -static package_search package_search_mod; -service AP_MODULE_DECLARE_DATA package_search_srv ( - "package-search", - package_search_mod, - {"root", "db-host", "db-port", "conf"}); - -static package_details package_details_mod; -service AP_MODULE_DECLARE_DATA package_details_srv ( - "package-details", - package_details_mod, - {"root", "db-host", "db-port", "conf"}); - -static package_version_details package_version_details_mod; -service AP_MODULE_DECLARE_DATA package_version_details_srv ( - "package-version-details", - package_version_details_mod, - {"root", "db-host", "db-port", "conf"}); - -static repository_details repository_details_mod; -service AP_MODULE_DECLARE_DATA repository_details_srv ( - "repository-details", - repository_details_mod, - {"root", "db-host", "db-port", "conf"}); - -static repository_root repository_root_mod ( - package_search_mod, repository_details_mod); -service AP_MODULE_DECLARE_DATA repository_root_srv ( - "repository-root", - repository_root_mod); +static brep::repository_root mod; +web::apache::service AP_MODULE_DECLARE_DATA brep_module ("brep", mod); diff --git a/brep/shared-database.cxx b/brep/shared-database.cxx index c1d128f..db6b811 100644 --- a/brep/shared-database.cxx +++ b/brep/shared-database.cxx @@ -19,10 +19,9 @@ namespace brep using odb::pgsql::database; static weak_ptr<database> db; - // In C++11, function-static variable initialization is - // guaranteed to be thread-safe, thought this doesn't - // seem to be enough in our case (because we are re- - // initializing the weak pointer). + // In C++11, function-static variable initialization is guaranteed to be + // thread-safe, thought this doesn't seem to be enough in our case + // (because we are re-initializing the weak pointer). // if (shared_ptr<database> d = db.lock ()) { diff --git a/brep/types-parsers b/brep/types-parsers index dfe86a6..3165518 100644 --- a/brep/types-parsers +++ b/brep/types-parsers @@ -9,6 +9,7 @@ #define BREP_TYPES_PARSERS #include <brep/types> + #include <brep/options-types> namespace brep diff --git a/brep/types-parsers.cxx b/brep/types-parsers.cxx index f7f1c1b..39c9b42 100644 --- a/brep/types-parsers.cxx +++ b/brep/types-parsers.cxx @@ -5,6 +5,7 @@ #include <brep/types-parsers> #include <brep/types> + #include <brep/options> using namespace std; @@ -24,24 +24,24 @@ g++ -shared $DEBUG -std=c++11 -I.. -I../../libbpkg \ -I../../libbutl -L../../libbpkg/bpkg -L../../libbutl/butl \ -fPIC -o libbrep.so $s -lbpkg -lbutl -lodb-pgsql -lodb -echo "cli brep-apache options" +echo "cli mod_brep options" cli --include-with-brackets --include-prefix brep --hxx-suffix "" \ --guard-prefix BREP --cxx-prologue "#include <brep/types-parsers>" \ --cli-namespace brep::cli --generate-file-scanner --suppress-usage \ - --option-prefix "" ./options.cli + --generate-modifier --generate-description --option-prefix "" ./options.cli -echo "g++ libbrep-apache.so" +echo "g++ libmod_brep.so" -s="package-search.cxx package-details.cxx package-version-details.cxx \ -repository-details.cxx repository-root.cxx module.cxx page.cxx services.cxx \ -options.cxx shared-database.cxx diagnostics.cxx \ +s="options.cxx services.cxx package-search.cxx package-details.cxx \ +package-version-details.cxx repository-details.cxx repository-root.cxx \ +module.cxx page.cxx shared-database.cxx diagnostics.cxx \ ../web/apache/request.cxx ../web/apache/service.cxx \ ../web/mime-url-encoding.cxx" g++ -shared $DEBUG -std=c++11 -I/usr/include/apr-1 -I/usr/include/httpd \ -I.. -I../../libbpkg -I../../libbutl -L. -L../../libbpkg/bpkg \ - -fPIC -o libbrep-apache.so $s -lbrep -lbpkg -lodb-pgsql -lodb -lstudxml + -fPIC -o libmod_brep.so $s -lbrep -lbpkg -lodb-pgsql -lodb -lstudxml cd ../loader diff --git a/etc/brep.conf b/etc/brep.conf new file mode 100644 index 0000000..6f806e8 --- /dev/null +++ b/etc/brep.conf @@ -0,0 +1,49 @@ +# The brep.conf file can be included once into Apache configuration file in +# the <VirtualHost> section or in the main server context. +# +LoadModule brep_module modules/mod_brep.so +LoadModule alias_module modules/mod_alias.so + +# Alternatively, if loading the modules in another place, make sure they +# are loaded. +# +<IfModule !brep_module> + Error "mod_brep is not loaded" +</IfModule> + +<IfModule !alias_module> + Error "mod_alias is not loaded" +</IfModule> + +# To use a repository root other than /pkg/, replace all occurrences of +# /pkg/ with the desired alternative root (use '/' for webserver root). +# +brep-root /pkg/ +brep-db-host localhost +brep-db-port 5432 +brep-search-results 10 +brep-pager-pages 5 +brep-description-len 500 +brep-changes-len 5000 +brep-log-verbosity 0 + +# To override brep module options place them into the separate configuration +# file, suppressing the "brep-" prefix. +# +#brep-conf /path/to/brep-site.conf + +SetHandler brep + +# Location of the brep static content (CSS files). +# +Alias /pkg/@/ /usr/share/brep/static/ + +<Directory "/usr/share/brep/static"> + Require all granted +</Directory> + +# Serve repository files from the repository root path. For example: +# +# http://example.org/pkg/1/... -> /path/to/repo/1/... +# +#AliasMatch ^/pkg/(\d+)/(.+) /path/to/repo/$1/$2 diff --git a/etc/apachectl b/etc/dev/apachectl index 4c67c1f..4d45570 100755 --- a/etc/apachectl +++ b/etc/dev/apachectl @@ -20,14 +20,16 @@ # 8 - configuration syntax error # # When multiple arguments are given, only the error from the _last_ -# one is reported. Run "apachectl help" for usage info +# one is reported. Run "apachectl help" for usage info. +# . `dirname $0`/config ARGV="$@" export AP_PORT -export AP_SERVER_NAME +export AP_GLOBAL_SERVER_NAME +export AP_BREP_SERVER_NAME export AP_ROOT export AP_ADMIN_EMAIL export AP_LOG_LEVEL @@ -36,41 +38,55 @@ export AP_DB_PORT export AP_MODULE_DIR export AP_WWW_DIR export AP_CONFIG_DIR +export AP_LOG_DIR export AP_WORKSPACE_DIR export AP_REPOSITORY_DIR -if test -n "$AP_LIB_DIRS"; then - export LD_LIBRARY_PATH=$AP_LIB_DIRS:$LD_LIBRARY_PATH +if [ -n "$AP_LIB_DIRS" ] ; then + export LD_LIBRARY_PATH=$AP_LIB_DIRS:$LD_LIBRARY_PATH fi mkdir -p "$AP_WORKSPACE_DIR" mkdir -p "$AP_LOG_DIR" mkdir -p "$AP_REPOSITORY_DIR" -# the path to your httpd binary, including options if necessary - -HTTPD="/usr/sbin/httpd -d $AP_LOG_DIR -f $AP_CONFIG_DIR/httpd.conf" +cat "$AP_CONFIG_DIR/../brep.conf" | \ +sed -e 's%modules\(/mod_brep.so\)%${AP_MODULE_DIR}\1%g' \ + -e 's%/pkg/%${AP_ROOT}%g' \ + -e 's%^\(brep-db-port\s*\).*%\1${AP_DB_PORT}%g' \ + -e 's%^\(brep-db-host\s*\).*%\1${AP_DB_HOST}%g' \ + -e 's%^#\(brep-conf\s*\)/path/to%\1${AP_CONFIG_DIR}%g' \ + -e 's%/usr/share/brep/static%${AP_WWW_DIR}%g' \ + -e 's%^#\(AliasMatch.*\)/path/to/repo%\1${AP_REPOSITORY_DIR}%g' \ +> "$AP_CONFIG_DIR/brep.conf" + +# The path to your httpd binary, including options if necessary. +# +HTTPD="/usr/sbin/httpd -f $AP_CONFIG_DIR/httpd.conf" -# a command that outputs a formatted text version of the HTML at the -# url given on the command line. Designed for lynx, however other -# programs may work. +# The command that outputs a formatted text version of the HTML at the url +# given on the command line. Designed for lynx, however other programs may work. +# LYNX="lynx -dump" -# the URL to your server's mod_status status page. If you do not -# have one, then status and fullstatus will not work. +# The URL to your server's mod_status status page. If you do not have one, +# then status and fullstatus will not work. +# STATUSURL="http://localhost:$AP_PORT/server-status" -# Set this variable to a command that increases the maximum -# number of file descriptors allowed per child process. This is -# critical for configurations that use many file descriptors, -# such as mass vhosting, or a multithreaded server. +# Set this variable to a command that increases the maximum number of file +# descriptors allowed per child process. This is critical for configurations +# that use many file descriptors, such as mass vhosting, or a multithreaded +# server. +# ULIMIT_MAX_FILES="ulimit -S -n `ulimit -H -n`" # -------------------- -------------------- # |||||||||||||||||||| END CONFIGURATION SECTION |||||||||||||||||||| # Set the maximum number of file descriptors allowed per child process. +# if [ "x$ULIMIT_MAX_FILES" != "x" ] ; then - $ULIMIT_MAX_FILES + $ULIMIT_MAX_FILES fi ERROR=0 diff --git a/etc/dev/brep-site.conf b/etc/dev/brep-site.conf new file mode 100644 index 0000000..09f020c --- /dev/null +++ b/etc/dev/brep-site.conf @@ -0,0 +1,9 @@ +# file : etc/brep-site.conf +# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file +# + +search-results 2 +description-len 100 +changes-len 100 +log-verbosity 1 diff --git a/etc/config b/etc/dev/config index c7edd56..cdd0b53 100644 --- a/etc/config +++ b/etc/dev/config @@ -2,7 +2,7 @@ # SCRIPT_DIR=`dirname $0` CONFIG_DIR=`cd $SCRIPT_DIR; pwd` -PROJECT_DIR="$CONFIG_DIR/.." +PROJECT_DIR="$CONFIG_DIR/../.." WORKSPACE_DIR="$PROJECT_DIR/var" LIB_DIRS="$PROJECT_DIR/brep:$PROJECT_DIR/../libbutl/butl:$PROJECT_DIR/../libbpkg/bpkg" @@ -17,9 +17,9 @@ PG_WORKSPACE_DIR="$WORKSPACE_DIR/run/pgsql" # Apache settings (used in apachectl) # AP_PORT=8080 -AP_SERVER_NAME="cppget.org:$AP_PORT" -AP_ROOT="" # Value examples: "", "/foo", "/foo/bar". -AP_ADMIN_EMAIL=admin@cppget.org +AP_GLOBAL_SERVER_NAME="localhost" +AP_BREP_SERVER_NAME="dev.cppget.org" +AP_ROOT="/" # Value examples: "/", "/foo/", "/foo/bar/". AP_LOG_LEVEL=trace1 AP_DB_HOST="$PG_WORKSPACE_DIR" AP_DB_PORT=$PG_PORT @@ -30,11 +30,3 @@ AP_CONFIG_DIR="$CONFIG_DIR" AP_LOG_DIR="$WORKSPACE_DIR/log/httpd" AP_WORKSPACE_DIR="$WORKSPACE_DIR/run/httpd" AP_REPOSITORY_DIR="$WORKSPACE_DIR/www" - -# brep-loader settings (used in loader) -# -LD_DB_HOST="$PG_WORKSPACE_DIR" -LD_DB_PORT=$PG_PORT -LD_REPOSITORIES="$CONFIG_DIR/repositories.conf" -LD_LIB_DIRS="$LIB_DIRS" -LD_EXE_DIRS="$PROJECT_DIR/loader" diff --git a/etc/dev/httpd.conf b/etc/dev/httpd.conf new file mode 100644 index 0000000..5943d70 --- /dev/null +++ b/etc/dev/httpd.conf @@ -0,0 +1,51 @@ +Listen ${AP_PORT} +ServerName ${AP_GLOBAL_SERVER_NAME}:${AP_PORT} + +CoreDumpDirectory "${AP_WORKSPACE_DIR}" +PidFile "${AP_WORKSPACE_DIR}/httpd.pid" + +ErrorLog "|/usr/sbin/rotatelogs ${AP_LOG_DIR}/error_log.%Y%m%d 86400" +ErrorLogFormat "[%t] [%l] [%m] %M" +LogLevel ${AP_LOG_LEVEL} + +Timeout 60 +KeepAlive On +KeepAliveTimeout 3 + +ThreadLimit 1000 +ServerLimit 2 +StartServers 1 +MaxClients 1000 +MinSpareThreads 400 +MaxSpareThreads 600 +ThreadsPerChild 500 +MaxRequestsPerChild 0 + +LoadModule mpm_worker_module modules/mod_mpm_worker.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule filter_module modules/mod_filter.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule status_module modules/mod_status.so +LoadModule mime_module modules/mod_mime.so +LoadModule deflate_module modules/mod_deflate.so + +<VirtualHost *:${AP_PORT}> + ServerName ${AP_GLOBAL_SERVER_NAME} +</VirtualHost> + +<VirtualHost *:${AP_PORT}> + ServerName ${AP_BREP_SERVER_NAME} + Include ${AP_CONFIG_DIR}/brep.conf +</VirtualHost> + +ExtendedStatus On + +<Location /server-status> + SetHandler server-status + Require local +</Location> + +TypesConfig /etc/mime.types +AddOutputFilterByType DEFLATE application/xhtml+xml +AddOutputFilterByType DEFLATE text/css diff --git a/etc/pgctl b/etc/dev/pgctl index d1f676f..d1f676f 100755 --- a/etc/pgctl +++ b/etc/dev/pgctl diff --git a/etc/httpd.conf b/etc/httpd.conf deleted file mode 100644 index dc56d9b..0000000 --- a/etc/httpd.conf +++ /dev/null @@ -1,113 +0,0 @@ -Listen ${AP_PORT} -ServerName "${AP_SERVER_NAME}" -ServerAdmin "${AP_ADMIN_EMAIL}" - -User apache -Group apache - -CoreDumpDirectory "${AP_WORKSPACE_DIR}" -PidFile "${AP_WORKSPACE_DIR}/httpd.pid" - -ErrorLog "|/usr/sbin/rotatelogs error_log.%Y%m%d 86400" -ErrorLogFormat "[%t] [%l] [%m] %M" -LogLevel ${AP_LOG_LEVEL} - -Timeout 60 -KeepAlive On -KeepAliveTimeout 3 - -ThreadLimit 1000 -ServerLimit 2 -StartServers 1 -MaxClients 1000 -MinSpareThreads 400 -MaxSpareThreads 600 -ThreadsPerChild 500 -MaxRequestsPerChild 0 - -LoadModule mpm_worker_module /usr/lib64/httpd/modules/mod_mpm_worker.so -LoadModule unixd_module /usr/lib64/httpd/modules/mod_unixd.so -LoadModule filter_module /usr/lib64/httpd/modules/mod_filter.so -LoadModule authz_core_module /usr/lib64/httpd/modules/mod_authz_core.so -LoadModule authz_host_module /usr/lib64/httpd/modules/mod_authz_host.so -LoadModule status_module /usr/lib64/httpd/modules/mod_status.so -LoadModule mime_module /usr/lib64/httpd/modules/mod_mime.so -LoadModule deflate_module /usr/lib64/httpd/modules/mod_deflate.so -LoadModule alias_module /usr/lib64/httpd/modules/mod_alias.so - -LoadModule repository_root_srv ${AP_MODULE_DIR}/libbrep-apache.so - -LoadModule package_search_srv ${AP_MODULE_DIR}/libbrep-apache.so - -<IfModule package_search_srv> - package-search-root "${AP_ROOT}" - package-search-db-host ${AP_DB_HOST} - package-search-db-port ${AP_DB_PORT} - package-search-conf ${AP_CONFIG_DIR}/package-search.conf -</IfModule> - -LoadModule package_details_srv ${AP_MODULE_DIR}/libbrep-apache.so - -<IfModule package_details_srv> - package-details-root "${AP_ROOT}" - package-details-db-host ${AP_DB_HOST} - package-details-db-port ${AP_DB_PORT} - package-details-conf ${AP_CONFIG_DIR}/package-details.conf -</IfModule> - -LoadModule package_version_details_srv ${AP_MODULE_DIR}/libbrep-apache.so - -<IfModule package_version_details_srv> - package-version-details-root "${AP_ROOT}" - package-version-details-db-host ${AP_DB_HOST} - package-version-details-db-port ${AP_DB_PORT} - package-version-details-conf ${AP_CONFIG_DIR}/package-version-details.conf -</IfModule> - -LoadModule repository_details_srv ${AP_MODULE_DIR}/libbrep-apache.so - -<IfModule repository_details_srv> - repository-details-root "${AP_ROOT}" - repository-details-db-host ${AP_DB_HOST} - repository-details-db-port ${AP_DB_PORT} - repository-details-conf ${AP_CONFIG_DIR}/repository-details.conf -</IfModule> - -<LocationMatch ^${AP_ROOT}/?$> - SetHandler repository-root -</LocationMatch> - -<LocationMatch ^${AP_ROOT}/[^/]+$> - SetHandler package-details -</LocationMatch> - -<LocationMatch ^${AP_ROOT}/[^/]+/[^/]+$> - SetHandler package-version-details -</LocationMatch> - -# Disable package-version-details and package-details handlers on static files -# and repository content locations. -# Location examples: /@, /@/common.css, /1, /1/math/stable. -# -<LocationMatch ^${AP_ROOT}/(@|\d+)(/.*)?$> - SetHandler None -</LocationMatch> - -# Static files locations. -# -AliasMatch ^${AP_ROOT}/@/(.+) ${AP_WWW_DIR}/$1 - -# Repository files locations. -# -AliasMatch ^${AP_ROOT}/(\d+)/(.+) ${AP_REPOSITORY_DIR}/$1/$2 - -ExtendedStatus On - -<Location /server-status> - SetHandler server-status - Require local -</Location> - -TypesConfig /etc/mime.types -AddOutputFilterByType DEFLATE application/xhtml+xml -AddOutputFilterByType DEFLATE text/css diff --git a/etc/loader b/etc/loader deleted file mode 100755 index 1ae3b48..0000000 --- a/etc/loader +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# file : etc/loader -# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file -# -# Designed to simplify running brep-loader utility. - -. `dirname $0`/config - -if test -n "$LD_LIB_DIRS"; then - export LD_LIBRARY_PATH=$LD_LIB_DIRS:$LD_LIBRARY_PATH -fi - -if test -n "$LD_EXE_DIRS"; then - export PATH=$LD_EXE_DIRS:$PATH -fi - -brep-loader --db-host "$LD_DB_HOST" --db-port $PG_PORT "$LD_REPOSITORIES" diff --git a/etc/package-details.conf b/etc/package-details.conf deleted file mode 100644 index 66a7c17..0000000 --- a/etc/package-details.conf +++ /dev/null @@ -1,15 +0,0 @@ -# file : etc/package-details.conf -# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file -# -# brep::module options -# -verb 1 - -# brep::package_details options -# -# @@ Set to 10. -results-on-page 2 -pages-in-pager 5 -# @@ Set to 500 (~ 80 chars x 6 lines). -description-length 100 diff --git a/etc/package-search.conf b/etc/package-search.conf deleted file mode 100644 index 4c1484f..0000000 --- a/etc/package-search.conf +++ /dev/null @@ -1,13 +0,0 @@ -# file : etc/package-search.conf -# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file -# -# brep::module options -# -verb 1 - -# brep::package_search options -# -# @@ Set to 10. -results-on-page 2 -pages-in-pager 5 diff --git a/etc/package-version-details.conf b/etc/package-version-details.conf deleted file mode 100644 index 865e162..0000000 --- a/etc/package-version-details.conf +++ /dev/null @@ -1,14 +0,0 @@ -# file : etc/package-version-details.conf -# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file -# -# brep::module options -# -verb 1 - -# brep::package_version_details options -# -# @@ Set to 500 (~ 80 chars x 6 lines). -description-length 100 -# @@ Set to 5000 (~ 80 chars x 60 lines). -changes-length 100 diff --git a/etc/repository-details.conf b/etc/repository-details.conf deleted file mode 100644 index e173cfb..0000000 --- a/etc/repository-details.conf +++ /dev/null @@ -1,7 +0,0 @@ -# file : etc/repository-details.conf -# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file -# -# brep::module options -# -verb 1 diff --git a/loader/loader.cxx b/loader/loader.cxx index 402194c..3083366 100644 --- a/loader/loader.cxx +++ b/loader/loader.cxx @@ -23,6 +23,7 @@ #include <brep/types> #include <brep/utility> + #include <brep/package> #include <brep/package-odb> diff --git a/tests/loader/driver.cxx b/tests/loader/driver.cxx index 94a0b86..7ca1e6e 100644 --- a/tests/loader/driver.cxx +++ b/tests/loader/driver.cxx @@ -16,6 +16,7 @@ #include <brep/types> #include <brep/utility> + #include <brep/package> #include <brep/package-odb> diff --git a/web/apache/request b/web/apache/request index 88b38a9..8861dac 100644 --- a/web/apache/request +++ b/web/apache/request @@ -43,6 +43,11 @@ namespace web int flush (); + // Return true if content have been sent to the client, false otherwise. + // + bool + get_write_state () const noexcept {return write_state_;} + // Get request path. // virtual const path_type& @@ -98,9 +103,6 @@ namespace web void parse_parameters (const char* args); - bool - get_write_state () const noexcept {return write_state_;} - virtual void set_write_state () { diff --git a/web/apache/service b/web/apache/service index 33d5a0a..7eef81a 100644 --- a/web/apache/service +++ b/web/apache/service @@ -8,9 +8,7 @@ #include <httpd.h> #include <string> -#include <vector> #include <cassert> -#include <utility> // move() #include <web/module> #include <web/apache/log> @@ -23,14 +21,10 @@ namespace web class service: ::module { public: - using option_names = std::vector<std::string>; - // Note that the module exemplar is stored by-reference. // template <typename M> - service (const std::string& name, - M& exemplar, - option_names opts = option_names ()) + service (const std::string& name, M& exemplar) : ::module { STANDARD20_MODULE_STUFF, @@ -42,8 +36,7 @@ namespace web ®ister_hooks<M> }, name_ (name), - exemplar_ (exemplar), - option_names_ (std::move (opts)) + exemplar_ (exemplar) { init_directives (); @@ -74,17 +67,31 @@ namespace web static void register_hooks (apr_pool_t*) noexcept { - // The registered function is called right after apache worker - // process is started. Called for every new process spawned. + // The config_finalizer() function is called at the end of Apache + // server configuration parsing. + // + ap_hook_post_config (&config_finalizer<M>, 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<M>, NULL, NULL, APR_HOOK_LAST); - // The registered function is called for each client request. + // The request_handler () function is called for each client request. // ap_hook_handler (&request_handler<M>, NULL, NULL, APR_HOOK_LAST); } template <typename M> + static int + config_finalizer (apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec*) + noexcept + { + instance<M> ()->options_parsed_ = true; + return OK; + } + + template <typename M> static void worker_initializer (apr_pool_t*, server_rec* server) noexcept { @@ -112,7 +119,10 @@ namespace web init_worker (log& l) noexcept; static const char* - add_option (cmd_parms* parms, void* mconfig, const char* args) noexcept; + parse_option (cmd_parms* parms, void* mconfig, const char* args) noexcept; + + const char* + add_option (const char* name, optional<std::string> value); template <typename M> int handle (request& r, log& l) noexcept; @@ -120,8 +130,9 @@ namespace web private: std::string name_; module& exemplar_; - option_names option_names_; + option_descriptions option_descriptions_; name_values options_; + bool options_parsed_ = false; }; } } diff --git a/web/apache/service.cxx b/web/apache/service.cxx index 3bd60f6..1741af3 100644 --- a/web/apache/service.cxx +++ b/web/apache/service.cxx @@ -10,12 +10,15 @@ #include <httpd.h> #include <http_config.h> -#include <memory> // unique_ptr +#include <memory> // unique_ptr #include <string> #include <cassert> -#include <cstring> // strlen() +#include <utility> // move() +#include <cstring> // strlen() #include <exception> +#include <web/module> + using namespace std; namespace web @@ -34,19 +37,19 @@ namespace web // bar of module foo the corresponding directive will appear in apache // configuration file as foo-bar. // - unique_ptr<command_rec[]> directives ( - new command_rec[option_names_.size () + 1]); - + const option_descriptions& od (exemplar_.options ()); + unique_ptr<command_rec[]> directives (new command_rec[od.size () + 1]); command_rec* d (directives.get ()); - for (auto& o: option_names_) + for (const auto& o: od) { - o = name_ + "-" + o; + auto i (option_descriptions_.emplace (name_ + "-" + o.first, o.second)); + assert (i.second); *d++ = { - o.c_str (), - reinterpret_cast<cmd_func> (add_option), + i.first->first.c_str (), + reinterpret_cast<cmd_func> (parse_option), this, RSRC_CONF, // Move away from TAKE1 to be able to handle empty string and @@ -58,36 +61,51 @@ namespace web } *d = {nullptr, nullptr, nullptr, 0, RAW_ARGS, nullptr}; - cmds = directives.release (); } const char* service:: - add_option (cmd_parms* parms, void*, const char* args) noexcept + parse_option (cmd_parms* parms, void*, const char* args) noexcept { + // @@ Current implementation does not consider configuration context + // (server config, virtual host, directory) for directive parsing, nor + // for request handling. + // service& srv (*reinterpret_cast<service*> (parms->cmd->cmd_data)); - string name (parms->cmd->name + srv.name_.length () + 1); - optional<string> value; - // 'args' is an optionally double-quoted string. Use double quotes to - // distinguish empty string from no-value case. + if (srv.options_parsed_) + // Apache is inside the second pass of its messy initialization cycle + // (more details at http://wiki.apache.org/httpd/ModuleLife). Just + // ignore it. + // + return 0; + + // 'args' is an optionally double-quoted string. It uses double quotes + // to distinguish empty string from no-value case. // assert (args != nullptr); + + optional<string> value; if (auto l = strlen (args)) value = l >= 2 && args[0] == '"' && args[l - 1] == '"' ? string (args + 1, l - 2) : args; - for (auto& v: srv.options_) - { - if (v.name == name) - { - v.value = value; - return 0; - } - } + return srv.add_option (parms->cmd->name, move (value)); + } + + const char* service:: + add_option (const char* name, optional<string> value) + { + auto i (option_descriptions_.find (name)); + assert (i != option_descriptions_.end ()); + + // Check that option value presense is expected. + // + if (i->second != static_cast<bool> (value)) + return value ? "unexpected value" : "value expected"; - srv.options_.emplace_back (name, value); + options_.emplace_back (name + name_.length () + 1, move (value)); return 0; } diff --git a/web/apache/service.txx b/web/apache/service.txx index 179980c..25a7435 100644 --- a/web/apache/service.txx +++ b/web/apache/service.txx @@ -20,8 +20,15 @@ namespace web try { M m (static_cast<const M&> (exemplar_)); - static_cast<module&> (m).handle (r, r, l); - return r.flush (); + + if (static_cast<module&> (m).handle (r, r, l)) + return r.flush (); + + if (!r.get_write_state ()) + return DECLINED; + + l.write (nullptr, 0, func_name.c_str (), APLOG_ERR, + "handling declined while unbuffered content has been written"); } catch (const invalid_request& e) { @@ -5,6 +5,7 @@ #ifndef WEB_MODULE #define WEB_MODULE +#include <map> #include <string> #include <vector> #include <iosfwd> @@ -18,6 +19,8 @@ namespace web { + using butl::optional; + // HTTP status code. // // @@ Define some commonly used constants? @@ -58,7 +61,10 @@ namespace web sequence_error (std::string d): std::runtime_error (std::move (d)) {} }; - using butl::optional; + // Map of module configuration option names to the boolean flag indicating + // whether the value is expected for the option. + // + using option_descriptions = std::map<std::string, bool>; struct name_value { @@ -182,27 +188,36 @@ namespace web class module { public: - // During startup the web server calls this function on the - // module exemplar passing a list of configuration name-value - // pairs. The place these configuration pairs come from is - // implementation-specific (normally a configuration file). - // Any exception thrown by this function terminates the web + // 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 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; - // Any exception other than 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. + // 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. Any exception other than 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. // - virtual void + virtual bool handle (request&, response&, log&) = 0; }; } |