aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-30 14:25:29 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-30 14:25:29 +0200
commit07780b06aa7b0fe049cc412309cf87e7fb10a0ef (patch)
treeb6d0f1ee50223f5cbbc4b8c5d0dc4f69ff7f2352
parent259a92ac4e1ac50e4c029f54265b735f6214b49d (diff)
Implement module configuration with an option list
-rw-r--r--brep/module24
-rw-r--r--brep/module.cxx68
-rw-r--r--brep/options.cli24
-rw-r--r--brep/search10
-rw-r--r--brep/search.cxx37
-rw-r--r--brep/view10
-rw-r--r--brep/view.cxx31
-rwxr-xr-xbuild.sh7
-rwxr-xr-xetc/apachectl39
-rw-r--r--etc/httpd.conf34
-rw-r--r--etc/search.conf9
-rw-r--r--etc/view.conf5
-rw-r--r--services.cxx8
-rw-r--r--web/apache/log12
-rw-r--r--web/apache/service127
-rw-r--r--web/module6
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>";
}
}
diff --git a/brep/view b/brep/view
index 819eff3..d97f9cf 100644
--- a/brep/view
+++ b/brep/view
@@ -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,
&register_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&);
};
diff --git a/web/module b/web/module
index 22f9fcc..8657c51 100644
--- a/web/module
+++ b/web/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