aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/apache/request8
-rw-r--r--web/apache/service39
-rw-r--r--web/apache/service.cxx66
-rw-r--r--web/apache/service.txx11
-rw-r--r--web/module47
5 files changed, 112 insertions, 59 deletions
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
&register_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)
{
diff --git a/web/module b/web/module
index 1774884..50cc6be 100644
--- a/web/module
+++ b/web/module
@@ -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;
};
}