diff options
-rw-r--r-- | brep/module | 24 | ||||
-rw-r--r-- | brep/module.cxx | 68 | ||||
-rw-r--r-- | brep/options.cli | 24 | ||||
-rw-r--r-- | brep/search | 10 | ||||
-rw-r--r-- | brep/search.cxx | 37 | ||||
-rw-r--r-- | brep/view | 10 | ||||
-rw-r--r-- | brep/view.cxx | 31 | ||||
-rwxr-xr-x | build.sh | 7 | ||||
-rwxr-xr-x | etc/apachectl | 39 | ||||
-rw-r--r-- | etc/httpd.conf | 34 | ||||
-rw-r--r-- | etc/search.conf | 9 | ||||
-rw-r--r-- | etc/view.conf | 5 | ||||
-rw-r--r-- | services.cxx | 8 | ||||
-rw-r--r-- | web/apache/log | 12 | ||||
-rw-r--r-- | web/apache/service | 127 | ||||
-rw-r--r-- | web/module | 6 |
16 files changed, 350 insertions, 101 deletions
diff --git a/brep/module b/brep/module index 54d41cf..d1399fb 100644 --- a/brep/module +++ b/brep/module @@ -11,6 +11,7 @@ #include <web/module> +#include <brep/options> #include <brep/diagnostics> namespace brep @@ -69,10 +70,6 @@ namespace brep // class module: public web::module { - public: - virtual void - handle (request&, response&) = 0; - // Diagnostics. // protected: @@ -95,18 +92,33 @@ namespace brep module (); module (const module& ); + private: virtual void handle (request&, response&, log&); virtual void - init (const char* path); + handle (request&, response&) = 0; + + virtual void + init (const name_values&, log&); + + // Can be overriden by module implementation which has configuration + // options. + // + virtual void + init (::cli::scanner& s) + { + // Just scan options to ensure there is no misspelled ones. + // + module_options o (s, cli::unknown_mode::fail, cli::unknown_mode::fail); + } // Diagnostics implementation details. // private: log* log_ {nullptr}; // Diagnostics backend provided by the web server. - public: + private: // Extract function name from a __PRETTY_FUNCTION__. // Throw std::invalid_argument if fail to parse. // diff --git a/brep/module.cxx b/brep/module.cxx index a803386..44154d6 100644 --- a/brep/module.cxx +++ b/brep/module.cxx @@ -7,6 +7,7 @@ #include <httpd/httpd.h> #include <httpd/http_log.h> +#include <memory> // unique_ptr #include <string> #include <cstring> // strncmp() #include <stdexcept> @@ -15,6 +16,8 @@ #include <web/module> #include <web/apache/log> +#include <brep/options> + using namespace std; using namespace placeholders; // For std::bind's _1, etc. @@ -52,10 +55,8 @@ namespace brep name = d.name; } - o << name << ": " << sev_str[d.sev] << ": " << d.msg << endl; - - //o << "[" << s[static_cast<int> (d.sev)] << "] [" - // << name << "] " << d.msg << std::endl; + o << name << ": " << sev_str[static_cast<size_t> (d.sev)] << ": " + << d.msg << endl; } } catch (const sequence_error&) @@ -67,9 +68,64 @@ namespace brep } } + // 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 char* path) + init (const name_values& options, log& log) { + log_ = &log; + + int argc = 0; + std::unique_ptr<const char*[]> argv (new const char*[options.size () * 2]); + + for (const auto& nv: options) + { + argv[argc++] = nv.name.c_str (); + argv[argc++] = nv.value.c_str (); + } + + try + { + { + // Read module implementation configuration. + // + cli::argv_file_scanner s (0, + argc, + const_cast<char**> (argv.get ()), + "conf"); + + init (s); + } + + // Read brep::module configuration. + // + cli::argv_file_scanner s (0, + argc, + const_cast<char**> (argv.get ()), + "conf"); + + module_options o (s, + ::cli::unknown_mode::skip, + ::cli::unknown_mode::skip); + + verb_ = o.verb (); + } + catch (const server_error& e) + { + log_write (e.data); + throw runtime_error ("initialization failed"); + } + catch (const cli::exception& e) + { + std::ostringstream o; + e.print (o); + throw runtime_error (o.str ()); + } } module:: @@ -166,7 +222,7 @@ namespace brep al->write (e.loc.file.c_str (), e.loc.line, name.c_str (), - s[static_cast<int> (e.sev)], + s[static_cast<size_t> (e.sev)], e.msg.c_str ()); } } diff --git a/brep/options.cli b/brep/options.cli new file mode 100644 index 0000000..cbd5d3a --- /dev/null +++ b/brep/options.cli @@ -0,0 +1,24 @@ +include <string>; + +namespace brep +{ + class module_options + { + unsigned int verb = 0; + }; + + class db_options + { + std::string db-host = "localhost"; + unsigned short db-port = 3306; + }; + + class search_options: module_options, db_options + { + unsigned int results-on-page = 10; + }; + + class view_options: module_options, db_options + { + }; +} diff --git a/brep/search b/brep/search index 9ea9345..a4edf01 100644 --- a/brep/search +++ b/brep/search @@ -5,7 +5,10 @@ #ifndef BREP_SEARCH #define BREP_SEARCH +#include <memory> // shared_ptr + #include <brep/module> +#include <brep/options> namespace brep { @@ -14,6 +17,13 @@ namespace brep public: virtual void handle (request&, response&); + + virtual void + init (::cli::scanner&); + + private: + + std::shared_ptr<search_options> options_; }; } diff --git a/brep/search.cxx b/brep/search.cxx index 9e6ab4b..15811e3 100644 --- a/brep/search.cxx +++ b/brep/search.cxx @@ -4,6 +4,7 @@ #include <brep/search> +#include <memory> // shared_ptr, make_shared() #include <chrono> #include <ostream> @@ -14,6 +15,23 @@ using namespace std; namespace brep { void search:: + init (::cli::scanner& s) + { + MODULE_DIAG; + + options_ = std::make_shared<search_options> (s, + ::cli::unknown_mode::fail, + ::cli::unknown_mode::fail); + + if (options_->results_on_page () > 30) + fail << "too many search results on page: " + << options_->results_on_page (); + else if (options_->results_on_page () > 10) + warn << options_->results_on_page () + << " search results on page is quite a lot but will try to cope"; + } + + void search:: handle (request& rq, response& rs) { MODULE_DIAG; @@ -24,9 +42,17 @@ namespace brep info << "handling search request from "; // << rq.client_ip (); - ostream& o (rs.content (200, "text/html;charset=utf-8", true)); + ostream& o (rs.content ()); + + o << "<html><head></head><body>"; + + o << "<b>Options:</b>" + << "<br>\ntracing verbosity: " << options_->verb () + << "<br>\ndb endpoint: " << options_->db_host () << ":" + << options_->db_port () + << "<br>\nsearch results on page: " << options_->results_on_page (); - o << "<html><head></head><body><b>Params:</b>"; + o << "<p>\n<b>Params:</b>"; const name_values& ps (rq.parameters ()); @@ -44,13 +70,14 @@ namespace brep o << "<br>\n" << p.name << "=" << p.value; } - o << "<br>\n<b>Cookies:</b>"; + o << "<p>\n<b>Cookies:</b>"; for (const auto& c: rq.cookies ()) { - o << "<br>\n" << c.name << "=" << c.value << " "; + o << "<br>\n" << c.name << "=" << c.value; } - o << "</body></html>"; + o << "<p><a href='view'>View</a>" + << "</body></html>"; } } @@ -5,7 +5,10 @@ #ifndef BREP_VIEW #define BREP_VIEW +#include <memory> // shared_ptr + #include <brep/module> +#include <brep/options> namespace brep { @@ -14,6 +17,13 @@ namespace brep public: virtual void handle (request&, response&); + + virtual void + init (::cli::scanner&); + + private: + + std::shared_ptr<view_options> options_; }; } diff --git a/brep/view.cxx b/brep/view.cxx index 6dafa1b..7ccc159 100644 --- a/brep/view.cxx +++ b/brep/view.cxx @@ -4,12 +4,43 @@ #include <brep/view> +#include <memory> // shared_ptr, make_shared() +#include <ostream> + +#include <web/module> + using namespace std; namespace brep { void view:: + init (::cli::scanner& s) + { + options_ = std::make_shared<view_options> (s, + ::cli::unknown_mode::fail, + ::cli::unknown_mode::fail); + } + + void view:: handle (request& rq, response& rs) { + ostream& o (rs.content (200, "text/html;charset=utf-8", false)); + + o << "<html><head></head><body>"; + + o << "<b>Options:</b>" + << "<br>\ntracing verbosity: " << options_->verb () + << "<br>\ndb endpoint: " << options_->db_host () << ":" + << options_->db_port (); + + o << "<p>\n<b>Cookies:</b>"; + + for (const auto& c: rq.cookies ()) + { + o << "<br>\n" << c.name << "=" << c.value; + } + + o << "<p><a href='search?a=1&b&c=2&d=&&x=a+b'>Search</a>" + << "</body></html>"; } } diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..2a39f48 --- /dev/null +++ b/build.sh @@ -0,0 +1,7 @@ +DEBUG="-g -ggdb -fno-inline" + +cd ./brep; cli --generate-file-scanner --suppress-usage --hxx-suffix "" \ + --option-prefix "" ./options.cli; cd .. + +g++ -shared $DEBUG -std=c++11 -I. -I/usr/include/apr-1 \ + -fPIC -o libbrep.so `find . -name '*.cxx'` diff --git a/etc/apachectl b/etc/apachectl index a879354..1039c50 100755 --- a/etc/apachectl +++ b/etc/apachectl @@ -27,23 +27,28 @@ ARGV="$@" # |||||||||||||||||||| START CONFIGURATION SECTION |||||||||||||||||||| # -------------------- -------------------- -PORT=7180 -LOG_LEVEL=trace1 -ADMIN_EMAIL=admin@cppget.org +export PORT=7180 +export SERVER_NAME="cppget.org:$PORT" +export LOG_LEVEL=trace1 +export ADMIN_EMAIL=admin@cppget.org + +export DB_HOST=localhost +export DB_PORT=7136 # |||||||||||||||||||| END CONFIGURATION SECTION ||||||||||||||||||||||| PROJECT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.. -site_config="$PROJECT_DIR/etc" -workspace="$PROJECT_DIR/var" -www="$PROJECT_DIR/www" +export MODULE_DIR="$PROJECT_DIR" +export WWW_DIR="$PROJECT_DIR/www" +export CONFIG_DIR="$PROJECT_DIR/etc" +export WORKSPACE_DIR="$PROJECT_DIR/var" -mkdir -p "$workspace" +mkdir -p "$WORKSPACE_DIR" # the path to your httpd binary, including options if necessary -HTTPD="/usr/sbin/httpd -d $workspace -f $site_config/httpd.conf" +HTTPD="/usr/sbin/httpd -d $WORKSPACE_DIR -f $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 @@ -73,25 +78,11 @@ if [ "x$ARGV" = "x" ] ; then fi case $ARGV in -start) - $HTTPD -C "Listen $PORT" -C "ServerName cppget.org:$PORT" \ - -C "DocumentRoot $www/htdocs" -C "CoreDumpDirectory $workspace" \ - -C "PidFile $workspace/httpd.pid" \ - -C "LogLevel $LOG_LEVEL" \ - -C "ServerAdmin $ADMIN_EMAIL" \ - -k $ARGV \ - \ - -C "LoadModule search_srv $PROJECT_DIR/libbrep.so" \ - -C "search_conf $site_config/search.conf" \ - \ - -C "LoadModule view_srv $PROJECT_DIR/libbrep.so" \ - -C "view_conf $site_config/view.conf" - +start) $HTTPD -k $ARGV ERROR=$? ;; stop|restart|graceful) - $HTTPD -C "ServerName cppget.org:$PORT" \ - -C "PidFile $workspace/httpd.pid" -k $ARGV + $HTTPD -k $ARGV ERROR=$? ;; startssl|sslstart|start-SSL) diff --git a/etc/httpd.conf b/etc/httpd.conf index 95b887d..b52b7d0 100644 --- a/etc/httpd.conf +++ b/etc/httpd.conf @@ -1,13 +1,20 @@ +Listen ${PORT} +ServerName "${SERVER_NAME}" +ServerAdmin "${ADMIN_EMAIL}" + User apache Group apache -ErrorLog error_log -#ErrorLog "|/usr/sbin/rotatelogs /Users/karen/projects/brep/var/error_log.%Y%m%d 86400" +DocumentRoot "${WWW_DIR}/htdocs" +CoreDumpDirectory "${WORKSPACE_DIR}" +PidFile "${WORKSPACE_DIR}/httpd.pid" +#ErrorLog error_log +ErrorLog "|/usr/sbin/rotatelogs error_log.%Y%m%d 86400" ErrorLogFormat "[%t] [%l] [%m] %M" +LogLevel ${LOG_LEVEL} Timeout 60 - KeepAlive On KeepAliveTimeout 3 @@ -33,7 +40,21 @@ LoadModule authz_host_module /usr/lib64/httpd/modules/mod_authz_host.so LoadModule expires_module /usr/lib64/httpd/modules/mod_expires.so LoadModule dir_module /usr/lib64/httpd/modules/mod_dir.so -TypesConfig /etc/mime.types +LoadModule search_srv ${MODULE_DIR}/libbrep.so + +<IfModule search_srv> + search-db-host ${DB_HOST} + search-db-port ${DB_PORT} + search-conf "${CONFIG_DIR}/search.conf" +</IfModule> + +LoadModule view_srv ${MODULE_DIR}/libbrep.so + +<IfModule view_srv> + view-db-host ${DB_HOST} + view-db-port ${DB_PORT} + view-conf "${CONFIG_DIR}/view.conf" +</IfModule> <LocationMatch ^/search$> SetHandler search @@ -43,8 +64,6 @@ TypesConfig /etc/mime.types SetHandler view </LocationMatch> -DirectoryIndex index.html - ExtendedStatus On <Location /server-status> @@ -59,3 +78,6 @@ ExtendedStatus On Options FollowSymLinks AllowOverride None </Directory> + +DirectoryIndex index.html +TypesConfig /etc/mime.types diff --git a/etc/search.conf b/etc/search.conf new file mode 100644 index 0000000..f742cda --- /dev/null +++ b/etc/search.conf @@ -0,0 +1,9 @@ +# brep::module options + +verb 1 + +# brep::search options + +results-on-page 20 + +#ah 7 diff --git a/etc/view.conf b/etc/view.conf new file mode 100644 index 0000000..7c9b163 --- /dev/null +++ b/etc/view.conf @@ -0,0 +1,5 @@ +# brep::module options + +verb 2 + +#oh 8 diff --git a/services.cxx b/services.cxx index 37b32cf..3d6f372 100644 --- a/services.cxx +++ b/services.cxx @@ -11,7 +11,11 @@ using namespace brep; using web::apache::service; static search search_mod; -service AP_MODULE_DECLARE_DATA search_srv ("search", search_mod); +service AP_MODULE_DECLARE_DATA search_srv ("search", + search_mod, + {"db-host", "db-port", "conf"}); static view view_mod; -service AP_MODULE_DECLARE_DATA view_srv ("view", view_mod); +service AP_MODULE_DECLARE_DATA view_srv ("view", + view_mod, + {"db-host", "db-port", "conf"}); diff --git a/web/apache/log b/web/apache/log index f8c65e9..50e2f7c 100644 --- a/web/apache/log +++ b/web/apache/log @@ -21,7 +21,7 @@ namespace web { public: - log (request_rec* req) noexcept: req_ (req) {} + log (server_rec* server) noexcept: server_ (server) {} virtual void write (const char* msg) {write (APLOG_ERR, msg);} @@ -47,30 +47,30 @@ namespace web level = std::min (level, APLOG_TRACE8); if (func) - ap_log_rerror (file, + ap_log_error (file, line, APLOG_NO_MODULE, level, 0, - req_, + server_, "[%s]: %s", func, msg); else // skip function name placeholder from log line // - ap_log_rerror (file, + ap_log_error (file, line, APLOG_NO_MODULE, level, 0, - req_, + server_, ": %s", msg); } private: - request_rec* req_; + server_rec* server_; }; } } diff --git a/web/apache/service b/web/apache/service index d003767..cd405ee 100644 --- a/web/apache/service +++ b/web/apache/service @@ -13,8 +13,11 @@ #include <httpd/http_config.h> #include <string> +#include <vector> +#include <memory> // unique_ptr #include <cassert> #include <exception> +#include <algorithm> // move() #include <web/module> #include <web/apache/log> @@ -27,10 +30,15 @@ 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) + service (const std::string& name, + M& exemplar, + option_names opts = option_names ()) : ::module { STANDARD20_MODULE_STUFF, @@ -38,36 +46,44 @@ namespace web nullptr, nullptr, nullptr, - directives_, + nullptr, ®ister_hooks<M> }, name_ (name), exemplar_ (exemplar), - conf_ (name + "_conf"), - conf_err_ ("A file containing configuration options for module " + - name_), - - // Defines service configuration directives. At the moment the - // only configuration directive is - // "<module_name>_conf <conf_file_path>". Configuration file path - // specified will be passed as a parameter to - // web::module::init call on exemplar_ object when apache worker - // process is started but prior to accepting client requests. - // - directives_ - { - { - conf_.c_str (), - reinterpret_cast<cmd_func> (config_file), - this, - RSRC_CONF, - TAKE1, - conf_err_.c_str () - } - } + option_names_ (std::move (opts)) // Doesn't look like handle_ member is required at all. // handle_ (&handle_impl<M>) { + // Fill apache module directive definitions. Directives share + // common name space in apache configuration file, so to prevent name + // clash have to form directive name as a combination of module and + // option names: <module name>-<option name>. This why for option + // bar of module foo the corresponding directive will appear in apache + // configuration file as foo-bar. + // + std::unique_ptr<command_rec[]> directives ( + new command_rec[option_names_.size () + 1]); + + command_rec* d = directives.get (); + + for (auto& o: option_names_) + { + o = name_ + "-" + o; + + *d++ = + { + o.c_str (), + reinterpret_cast<cmd_func> (add_option), + this, + RSRC_CONF, + TAKE1, + nullptr + }; + } + + *d = {}; + // instance<M> () is invented to delegate processing from apache // request handler C function to the service non static member // function. This appoach resticts number of service objects per @@ -76,12 +92,29 @@ namespace web service*& srv = instance<M> (); assert (srv == nullptr); srv = this; + + cmds = directives.release (); + } + + ~service () + { + delete [] cmds; } static const char* - config_file (cmd_parms *parms, void *mconfig, const char *w) + add_option (cmd_parms *parms, void *mconfig, const char *value) { - reinterpret_cast<service*> (parms->cmd->cmd_data)->conf_file_ = w; + service& srv = *reinterpret_cast<service*> (parms->cmd->cmd_data); + std::string name (parms->cmd->name + srv.name_.length () + 1); + + for (auto& v: srv.options_) + if (v.name == name) + { + v.value = value; + return 0; + } + + srv.options_.emplace_back (name, value); return 0; } @@ -95,12 +128,12 @@ namespace web template <typename M> static void - register_hooks (apr_pool_t *pool) noexcept + register_hooks (apr_pool_t*) noexcept { // The registered function is called right after apache worker // process is started. Called for every new process spawned. // - ap_hook_child_init (&child_initializer<M>, NULL, NULL, APR_HOOK_LAST); + ap_hook_child_init (&worker_initializer<M>, NULL, NULL, APR_HOOK_LAST); // The registered function is called for each client request. // @@ -109,25 +142,33 @@ namespace web template <typename M> static void - child_initializer (apr_pool_t *pchild, server_rec *s) noexcept + worker_initializer (apr_pool_t*, server_rec* server) noexcept { auto srv = instance<M> (); + log l (server); + + static const std::string func_name ( + "web::apache::service<" + srv->name_ + ">::worker_initializer"); try { - srv->exemplar_.init (srv->conf_file_.c_str ()); + srv->exemplar_.init (srv->options_, l); } catch (const std::exception& e) { - ap_log_error (0, - 0, - APLOG_NO_MODULE, - APLOG_EMERG, - 0, - s, - "[::web::apache::service<%s>::child_initializer]: %s", - srv->name_.c_str (), - e.what ()); + l.write (nullptr, 0, func_name.c_str (), APLOG_EMERG, e.what ()); + + // Terminate the root apache process. + // + ::kill (::getppid (), SIGTERM); + } + catch (...) + { + l.write (nullptr, + 0, + func_name.c_str (), + APLOG_EMERG, + "unknown error"); // Terminate the root apache process. // @@ -148,7 +189,7 @@ namespace web "web::apache::service<" + srv->name_ + ">::request_handler"); request req (r); - log l (r); + log l (r->server); try { @@ -235,10 +276,8 @@ namespace web */ std::string name_; module& exemplar_; - std::string conf_; - std::string conf_err_; - command_rec directives_[2]; - std::string conf_file_; + option_names option_names_; + name_values options_; // void (*handle_) (request&, response&, log&, const module&); }; @@ -116,7 +116,9 @@ namespace web // new status. // virtual std::ostream& - content (status_code, const std::string& type, bool buffer = true) = 0; + content (status_code code = 200, + const std::string& type = "text/html;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 @@ -175,7 +177,7 @@ namespace web // @@ Is log available? Should we pass it? // virtual void - init (const name_values&) = 0; + 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 |