aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--brep/module10
-rw-r--r--brep/module.cxx153
-rw-r--r--brep/search.cxx7
-rwxr-xr-xetc/apachectl8
-rw-r--r--services.cxx10
-rw-r--r--web/apache/log12
-rw-r--r--web/apache/request119
-rw-r--r--web/apache/request.cxx73
-rw-r--r--web/apache/request.ixx45
-rw-r--r--web/apache/service167
-rw-r--r--web/apache/stream87
-rw-r--r--web/module23
12 files changed, 402 insertions, 312 deletions
diff --git a/brep/module b/brep/module
index d976559..54d41cf 100644
--- a/brep/module
+++ b/brep/module
@@ -30,7 +30,7 @@ namespace brep
using web::response;
using web::log;
- // And this exception indicated a server error (5XX). In particular,
+ // This exception indicated a server error (5XX). In particular,
// it is thrown by the fail diagnostics stream and is caught by the
// module implementation where it is both logged as an error and
// returned to the user with the 5XX status code.
@@ -98,19 +98,23 @@ namespace brep
virtual void
handle (request&, response&, log&);
+ virtual void
+ init (const char* path);
+
// Diagnostics implementation details.
//
private:
log* log_ {nullptr}; // Diagnostics backend provided by the web server.
+ public:
// Extract function name from a __PRETTY_FUNCTION__.
// Throw std::invalid_argument if fail to parse.
//
static std::string
- func_name (const std::string& pretty_name);
+ func_name (const char* pretty_name);
void
- log_write (diag_data&&) const;
+ log_write (const diag_data&) const;
protected:
const diag_epilogue log_writer_;
diff --git a/brep/module.cxx b/brep/module.cxx
index 5937028..4ce635a 100644
--- a/brep/module.cxx
+++ b/brep/module.cxx
@@ -4,15 +4,15 @@
#include <brep/module>
-#include <stdexcept>
+#include <httpd/httpd.h>
+#include <httpd/http_log.h>
+
#include <string>
-#include <functional> // bind()
#include <cstring> // strncmp()
-
-#include <httpd/httpd.h>
+#include <stdexcept>
+#include <functional> // bind()
#include <web/module>
-
#include <web/apache/log>
using namespace std;
@@ -24,57 +24,56 @@ namespace brep
handle (request& rq, response& rs, log& l)
{
log_ = &l;
- const basic_mark error (severity::error, log_writer_, __PRETTY_FUNCTION__);
try
{
handle (rq, rs);
}
- catch (const invalid_request& e)
+ catch (const server_error& e)
{
- if (e.description.empty ())
- {
- rs.status (e.status);
- }
- else
+ log_write (e.data);
+
+ try
{
- try
- {
- rs.content (e.status, "text/html;charset=utf-8") << e.description;
- }
- catch (const sequence_error& se)
+ static const char* s[] = {"err", "warn", "info", "trace"};
+ std::ostream& o = rs.content (500, "text/plain;charset=utf-8");
+
+ for (const auto& d: e.data)
{
- error << se.what ();
- rs.status (e.status);
+ string name;
+
+ try
+ {
+ name = func_name (d.name);
+ }
+ catch (const invalid_argument&)
+ {
+ // Log "pretty" function description, see in log file & fix.
+ name = d.name;
+ }
+
+ o << "[" << s[static_cast<int> (d.sev)] << "] ["
+ << name << "] " << d.msg << std::endl;
}
}
- }
- catch (server_error& e) // Non-const because of move() below.
- {
- log_write (move (e.data));
- rs.status (HTTP_INTERNAL_SERVER_ERROR);
- }
- catch (const exception& e)
- {
- error << e.what ();
- rs.status (HTTP_INTERNAL_SERVER_ERROR);
- }
- catch (...)
- {
- error << "unknown error";
- rs.status (HTTP_INTERNAL_SERVER_ERROR);
+ catch (const sequence_error&)
+ {
+ }
}
}
+ void module::
+ init (const char* path)
+ {
+ }
+
module::
module (): log_writer_ (bind (&module::log_write, this, _1)) {}
// Custom copy constructor is required to initialize log_writer_ properly.
//
- // @@ Won't log_writer_ be left empty by this implementation?
- //
module::
- module (const module& m): module (), verb_ (m.verb_) {}
+ module (const module& m): module () {verb_ = m.verb_;}
// For function func declared like this:
// using B = std::string (*)(int);
@@ -85,65 +84,53 @@ namespace brep
// ,std::string (* (*)(wchar_t))(int)) const)(int, int))(int)
//
string module::
- func_name (const string& pretty_name)
+ func_name (const char* pretty_name)
{
- string::size_type b (0);
- string::size_type e (pretty_name.find (' '));
+ const char* e = strchr (pretty_name, ')');
- // Position b at beginning of supposed function name,
- //
- if (e != string::npos && !strncmp (pretty_name.c_str (), "virtual ", 8))
+ if (e && e > pretty_name)
{
- // Skip keyword virtual.
+ // Position e at last matching '(' which is the beginning of the
+ // argument list..
//
- b = pretty_name.find_first_not_of (' ', e);
- e = pretty_name.find (' ', b);
- }
+ size_t d (1);
- if (pretty_name.find ('(', b) > e)
- {
- // Not a constructor nor destructor. Skip type or *.
- //
- b = pretty_name.find_first_not_of (' ', e);
- }
-
- if (b != string::npos)
- {
- // Position e at the last character of supposed function name.
- //
- e = pretty_name.find_last_of (')');
-
- if (e != string::npos && e > b)
+ do
{
- size_t d (1);
-
- while (--e > b && d)
+ switch (*--e)
{
- switch (pretty_name[e])
- {
- case ')': ++d; break;
- case '(': --d; break;
- }
+ case ')': ++d; break;
+ case '(': --d; break;
}
+ }
+ while (d && e > pretty_name);
- if (!d)
+ if (!d && e > pretty_name)
+ {
+ // Position e at the character following the function name.
+ //
+ while (e > pretty_name &&
+ (*e != '(' || *(e - 1) == ' ' || *(e - 1) == ')'))
+ --e;
+
+ if (e > pretty_name)
{
- return pretty_name[b] == '(' && pretty_name[e] == ')' ?
- // Not a name yet, go deeper.
- //
- func_name (string(pretty_name, b + 1, e - b - 1)) :
- // Got the name.
- //
- string (pretty_name, b, e - b + 1);
+ // Position b at the beginning of the qualified function name.
+ //
+ const char* b (e);
+ while (--b > pretty_name && *b != ' ');
+ if (*b == ' ') ++b;
+
+ return string (b, e - b);
}
}
}
- throw invalid_argument ("");
+ throw invalid_argument ("::brep::module::func_name");
}
void module::
- log_write (diag_data&& d) const
+ log_write (const diag_data& d) const
{
if (log_ == nullptr)
return; // No backend yet.
@@ -157,7 +144,7 @@ namespace brep
//
static int s[] = {APLOG_ERR, APLOG_WARNING, APLOG_INFO, APLOG_TRACE1};
- for (const auto& e : d)
+ for (const auto& e: d)
{
string name;
@@ -171,11 +158,11 @@ namespace brep
name = e.name;
}
- al->write (e.loc.file.c_str(),
+ al->write (e.loc.file.c_str (),
e.loc.line,
- name.c_str(),
+ name.c_str (),
s[static_cast<int> (e.sev)],
- e.msg.c_str());
+ e.msg.c_str ());
}
}
diff --git a/brep/search.cxx b/brep/search.cxx
index 93690e4..9e6ab4b 100644
--- a/brep/search.cxx
+++ b/brep/search.cxx
@@ -4,9 +4,8 @@
#include <brep/search>
-#include <ostream>
-#include <iostream>
#include <chrono>
+#include <ostream>
#include <web/module>
@@ -25,7 +24,7 @@ namespace brep
info << "handling search request from "; // << rq.client_ip ();
- ostream& o (rs.content (200, "text/html;charset=utf-8", false));
+ ostream& o (rs.content (200, "text/html;charset=utf-8", true));
o << "<html><head></head><body><b>Params:</b>";
@@ -47,7 +46,7 @@ namespace brep
o << "<br>\n<b>Cookies:</b>";
- for (const auto& c : rq.cookies ())
+ for (const auto& c: rq.cookies ())
{
o << "<br>\n" << c.name << "=" << c.value << " ";
}
diff --git a/etc/apachectl b/etc/apachectl
index d9c1ee9..a879354 100755
--- a/etc/apachectl
+++ b/etc/apachectl
@@ -78,10 +78,14 @@ start)
-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 "ServerAdmin $ADMIN_EMAIL" \
- -k $ARGV
+ -C "view_conf $site_config/view.conf"
ERROR=$?
;;
diff --git a/services.cxx b/services.cxx
index 2da5bca..37b32cf 100644
--- a/services.cxx
+++ b/services.cxx
@@ -2,16 +2,16 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
-#include <web/apache/service>
-
-#include <brep/search>
#include <brep/view>
+#include <brep/search>
+
+#include <web/apache/service>
using namespace brep;
using web::apache::service;
-static const search search_mod;
+static search search_mod;
service AP_MODULE_DECLARE_DATA search_srv ("search", search_mod);
-static const view view_mod;
+static view view_mod;
service AP_MODULE_DECLARE_DATA view_srv ("view", view_mod);
diff --git a/web/apache/log b/web/apache/log
index 151efb4..f8c65e9 100644
--- a/web/apache/log
+++ b/web/apache/log
@@ -5,12 +5,12 @@
#ifndef WEB_APACHE_LOG
#define WEB_APACHE_LOG
-#include <algorithm> // min()
-#include <cstdint> // uint64_t
-
#include <httpd/httpd.h> // request_rec
#include <httpd/http_log.h>
+#include <cstdint> // uint64_t
+#include <algorithm> // min()
+
#include <web/module>
namespace web
@@ -21,7 +21,7 @@ namespace web
{
public:
- log (request_rec* req) noexcept : req_ (req) {}
+ log (request_rec* req) noexcept: req_ (req) {}
virtual void
write (const char* msg) {write (APLOG_ERR, msg);}
@@ -29,7 +29,7 @@ namespace web
// Apache-specific interface.
//
void
- write (int level, const char* msg)
+ write (int level, const char* msg) const noexcept
{
write (nullptr, 0, nullptr, level, msg);
}
@@ -39,7 +39,7 @@ namespace web
std::uint64_t line,
const char* func,
int level,
- const char* msg)
+ const char* msg) const noexcept
{
if (file && *file)
file = nullptr; // skip file/line placeholder from log line.
diff --git a/web/apache/request b/web/apache/request
index ab5c765..4071bd1 100644
--- a/web/apache/request
+++ b/web/apache/request
@@ -5,24 +5,24 @@
#ifndef WEB_APACHE_REQUEST
#define WEB_APACHE_REQUEST
-#include <stdexcept>
-#include <exception>
-#include <string>
-#include <ios>
-#include <istream>
-#include <ostream>
-#include <streambuf>
-#include <memory> // unique_ptr
-#include <algorithm> // move
-#include <chrono>
-#include <cassert>
-
#include <apr_strings.h>
#include <httpd/httpd.h>
#include <httpd/http_core.h>
#include <httpd/util_script.h>
+#include <ios>
+#include <chrono>
+#include <memory> // unique_ptr
+#include <string>
+#include <cassert>
+#include <istream>
+#include <ostream>
+#include <streambuf>
+#include <stdexcept>
+#include <exception>
+#include <algorithm> // move
+
#include <web/module>
#include <web/apache/stream>
@@ -30,11 +30,13 @@ namespace web
{
namespace apache
{
- class request : public web::request, public web::response
+ class request: public web::request,
+ public web::response,
+ public write_state
{
friend class service;
- request (request_rec* rec) noexcept: rec_ (rec) {}
+ request (request_rec* rec) noexcept: rec_ (rec) {rec_->status = HTTP_OK;}
// Flush of buffered content.
//
@@ -44,16 +46,13 @@ namespace web
// Get request body data stream.
//
virtual std::istream&
- data ()
+ content ()
{
- if (write_flag ())
- {
- throw sequence_error ("::web::apache::request::data");
- }
-
if (!in_)
{
- std::unique_ptr<std::streambuf> in_buf (new istreambuf (rec_));
+ std::unique_ptr<std::streambuf> in_buf (
+ new istreambuf (rec_, *this));
+
in_.reset (new std::istream (in_buf.get ()));
in_buf_ = std::move (in_buf);
in_->exceptions (std::ios::failbit | std::ios::badbit);
@@ -74,16 +73,16 @@ namespace web
{
if (!parameters_)
{
- parameters_.reset (new name_values());
+ parameters_.reset (new name_values ());
try
{
- parse_parameters(rec_->args);
- parse_parameters(form_data ()->c_str ());
+ parse_parameters (rec_->args);
+ parse_parameters (form_data ()->c_str ());
}
- catch(const std::invalid_argument& )
+ catch (const std::invalid_argument& )
{
- throw invalid_request();
+ throw invalid_request ();
}
}
@@ -97,30 +96,29 @@ namespace web
// Get response status code.
//
- status_code status () const noexcept {return status_;}
+ status_code status () const noexcept {return rec_->status;}
// Set response status code.
//
virtual void
status (status_code status)
{
- if (status != status_)
+ if (status != rec_->status)
{
// Setting status code in exception handler is a common usecase
// where no sense to throw but still need to signal apache a
// proper status code.
//
- if (write_flag () && !std::current_exception ())
+ if (get_write_state () && !std::current_exception ())
{
throw sequence_error ("::web::apache::request::status");
}
- status_ = status;
- type_.clear ();
+ rec_->status = status;
buffer_ = true;
out_.reset ();
out_buf_.reset ();
- set_content_type ();
+ ap_set_content_type (rec_, nullptr);
}
}
@@ -158,60 +156,33 @@ namespace web
static std::string
mime_url_decode (const char* b, const char* e, bool trim = false);
- // Save content type to apache internals.
- //
- void
- set_content_type () const noexcept
+ bool
+ get_write_state () const noexcept {return write_state_;}
+
+ virtual void
+ set_write_state ()
{
- if (type_.empty ())
- ap_set_content_type (rec_, nullptr);
- else
+ if (!write_state_)
{
- if(status_ == HTTP_OK)
- {
- ap_set_content_type (rec_,
- apr_pstrdup (rec_->pool, type_.c_str ()));
- }
- else
+ // Preparing to write a response read and discard request
+ // body if any.
+ //
+ int r = ap_discard_request_body (rec_);
+
+ if (r != OK)
{
- // Unfortunatelly there is no way to set a proper content type
- // for error custom response. Depending on presense of
- // "suppress-error-charset" key in request_rec::subprocess_env
- // table content type is set to "text/html" otherwise to
- // "text/html; charset=iso-8859-1" (read http_protocol.c for
- // details). I have chosen the first one as it is better not to
- // specify charset than to set a wrong one. Ensure to put
- // a proper encoding to
- // <meta http-equiv="Content-Type" content="text/html;charset=...">
- // tag so browser can render the page properly.
- // The clean solution would be patching apache but let's leave this
- // troublesome option untill really required.
- //
- apr_table_set (rec_->subprocess_env, "suppress-error-charset", "");
+ throw invalid_request (r);
}
- }
- }
- bool
- write_flag () const noexcept
- {
- if (!buffer_)
- {
- assert (out_buf_);
- auto b = dynamic_cast<ostreambuf*> (out_buf_.get ());
- assert (b);
- return b->write_flag ();
+ write_state_ = true;
}
-
- return false;
}
private:
request_rec* rec_;
- status_code status_ {HTTP_OK};
- std::string type_;
bool buffer_ {true};
+ bool write_state_ {false};
std::unique_ptr<std::streambuf> out_buf_;
std::unique_ptr<std::ostream> out_;
std::unique_ptr<std::streambuf> in_buf_;
diff --git a/web/apache/request.cxx b/web/apache/request.cxx
index 6f043bc..1990fa2 100644
--- a/web/apache/request.cxx
+++ b/web/apache/request.cxx
@@ -4,19 +4,20 @@
#include <web/apache/request>
-#include <stdexcept>
+#include <apr_tables.h>
+
+#include <strings.h> // strcasecmp()
+
#include <ios>
-#include <streambuf>
+#include <ctime>
+#include <chrono>
+#include <memory> // unique_ptr
#include <sstream>
#include <ostream>
-#include <memory> // unique_ptr
+#include <cstring>
+#include <stdexcept>
+#include <streambuf>
#include <algorithm> // move()
-#include <chrono>
-#include <ctime>
-
-#include <strings.h> // strcasecmp()
-
-#include <apr_tables.h>
using namespace std;
@@ -47,17 +48,19 @@ namespace web
if (e && e < v)
v = 0;
- string name (
- v ? mime_url_decode (n, v, true) :
- (e ? mime_url_decode (n, e, true) :
- mime_url_decode (n, n + strlen (n), true)));
+ string name (v
+ ? mime_url_decode (n, v, true)
+ : (e
+ ? mime_url_decode (n, e, true)
+ : mime_url_decode (n, n + strlen (n), true)));
string value;
if (v++)
{
- value = e ? mime_url_decode (v, e, true) :
- mime_url_decode (v, v + strlen (v), true);
+ value = e
+ ? mime_url_decode (v, e, true)
+ : mime_url_decode (v, v + strlen (v), true);
}
if (!name.empty () || !value.empty ())
@@ -75,31 +78,18 @@ namespace web
ostream& request::
content (status_code status, const std::string& type, bool buffer)
{
- if (type.empty ())
+ if (out_ && status == rec_->status && buffer == buffer_ &&
+ !::strcasecmp (rec_->content_type ? rec_->content_type : "",
+ type.c_str ()))
{
- // Getting content stream for writing assumes type to be provided.
- //
- throw std::invalid_argument (
- "::web::apache::request::content invalid type");
+ return *out_;
}
- // Due to apache implementation of error custom response there is no
- // way to make it unbuffered.
- //
- buffer = buffer || status != HTTP_OK;
-
- if ((status != status_ || type != type_ || buffer != buffer_) &
- write_flag ())
+ if (get_write_state ())
{
throw sequence_error ("::web::apache::request::content");
}
- if (status == status_ && type == type_ && buffer == buffer_)
- {
- assert (out_);
- return *out_;
- }
-
if (!buffer)
// Request body will be discarded prior first byte of content is
// written. Save form data now to make it available for furture
@@ -107,9 +97,10 @@ namespace web
//
form_data ();
- std::unique_ptr<std::streambuf> out_buf(
- buffer ? static_cast<std::streambuf*> (new std::stringbuf ()) :
- static_cast<std::streambuf*> (new ostreambuf (rec_)));
+ std::unique_ptr<std::streambuf> out_buf (
+ buffer
+ ? static_cast<std::streambuf*> (new std::stringbuf ())
+ : static_cast<std::streambuf*> (new ostreambuf (rec_, *this)));
out_.reset (new std::ostream (out_buf.get ()));
@@ -118,12 +109,12 @@ namespace web
out_->exceptions (
std::ios::eofbit | std::ios::failbit | std::ios::badbit);
- status_ = status;
- type_ = type;
buffer_ = buffer;
+ rec_->status = status;
- if (!buffer_)
- set_content_type ();
+ ap_set_content_type (
+ rec_,
+ type.empty () ? nullptr : apr_pstrdup (rec_->pool, type.c_str ()));
return *out_;
}
@@ -136,7 +127,7 @@ namespace web
const char* domain,
bool secure)
{
- if (write_flag ())
+ if (get_write_state ())
{
throw sequence_error ("::web::apache::request::cookie");
}
diff --git a/web/apache/request.ixx b/web/apache/request.ixx
index a427fd4..b41de8d 100644
--- a/web/apache/request.ixx
+++ b/web/apache/request.ixx
@@ -2,13 +2,13 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
+#include <strings.h> // strcasecmp()
+
#include <iomanip>
#include <sstream>
#include <cstring>
#include <cstdlib>
-#include <strings.h> // strcasecmp()
-
namespace web
{
namespace apache
@@ -18,10 +18,8 @@ namespace web
{
if (buffer_ && out_buf_)
{
- set_content_type ();
-
auto b = dynamic_cast<std::stringbuf*> (out_buf_.get ());
- assert(b);
+ assert (b);
std::string s (b->str ());
@@ -33,27 +31,21 @@ namespace web
if (r == OK)
{
- if (status_ == HTTP_OK)
- {
- if (ap_rwrite (s.c_str (), s.length (), rec_) < 0)
- {
- status_ = HTTP_REQUEST_TIME_OUT;
- }
- }
- else
- {
- ap_custom_response (rec_, status_, s.c_str ());
- }
+ set_write_state ();
+
+ if (ap_rwrite (s.c_str (), s.length (), rec_) < 0)
+ rec_->status = HTTP_REQUEST_TIME_OUT;
}
+
else
- status_ = r;
+ rec_->status = r;
}
out_.reset ();
out_buf_.reset ();
}
- return status_ == HTTP_OK ? OK : status_;
+ return rec_->status == HTTP_OK || get_write_state () ? OK : rec_->status;
}
inline const request::string_ptr& request::
@@ -66,7 +58,7 @@ namespace web
if (ct && !strncasecmp ("application/x-www-form-urlencoded", ct, 33))
{
- std::istream& istr (data ());
+ std::istream& istr (content ());
std::getline (istr, *form_data_);
// Make request data still be available.
@@ -89,8 +81,8 @@ namespace web
{
for (auto n (args); n != 0; )
{
- const char* v = strchr (n, '=');
- const char* e = strchr (n, '&');
+ const char* v = std::strchr (n, '=');
+ const char* e = ::strchr (n, '&');
if (e && e < v)
v = 0;
@@ -123,6 +115,7 @@ namespace web
char f = o.fill ();
std::ios_base::fmtflags g = o.flags ();
o << std::hex << std::uppercase << std::right << std::setfill ('0');
+
char c;
while ((c = *v++) != '\0')
@@ -140,7 +133,11 @@ namespace web
case '_':
case '-':
case '~': o << c; break;
- default: o << "%" << std::setw (2) << (unsigned short)c;
+ default:
+ {
+ o << "%" << std::setw (2) << static_cast<unsigned short> (c);
+ break;
+ }
}
}
@@ -156,7 +153,7 @@ namespace web
b += std::strspn (b, " ");
if (b >= e)
- return std::string();
+ return std::string ();
while (*--e == ' ');
++e;
@@ -199,7 +196,7 @@ namespace web
"::web::apache::request::mime_url_decode wrong");
}
- value.append (1, (char)vl);
+ value.append (1, static_cast<char> (vl));
b += 2;
break;
}
diff --git a/web/apache/service b/web/apache/service
index 688f8f1..d003767 100644
--- a/web/apache/service
+++ b/web/apache/service
@@ -5,15 +5,18 @@
#ifndef WEB_APACHE_SERVICE
#define WEB_APACHE_SERVICE
-#include <exception>
-#include <string>
-#include <cassert>
+#include <string.h> // memset()
+#include <unistd.h> // getppid()
+#include <signal.h> // kill()
#include <httpd/httpd.h>
#include <httpd/http_config.h>
-#include <web/module>
+#include <string>
+#include <cassert>
+#include <exception>
+#include <web/module>
#include <web/apache/log>
#include <web/apache/request>
@@ -21,13 +24,13 @@ namespace web
{
namespace apache
{
- class service : ::module
+ class service: ::module
{
public:
// Note that the module exemplar is stored by-reference.
//
template <typename M>
- service (const std::string& name, const M& exemplar)
+ service (const std::string& name, M& exemplar)
: ::module
{
STANDARD20_MODULE_STUFF,
@@ -35,25 +38,53 @@ namespace web
nullptr,
nullptr,
nullptr,
- nullptr,
+ directives_,
&register_hooks<M>
},
name_ (name),
- exemplar_ (exemplar)
-
+ 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 ()
+ }
+ }
// Doesn't look like handle_ member is required at all.
// handle_ (&handle_impl<M>)
{
- // instance<M> () invented to delegate processing from apache request
- // handler C function to service non static member function.
- // This appoach resticts number of service objects per module
- // implementation class with just one instance.
+ // 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
+ // specific module implementation class with just one instance.
//
service*& srv = instance<M> ();
assert (srv == nullptr);
srv = this;
}
+ static const char*
+ config_file (cmd_parms *parms, void *mconfig, const char *w)
+ {
+ reinterpret_cast<service*> (parms->cmd->cmd_data)->conf_file_ = w;
+ return 0;
+ }
+
template <typename M>
static service*&
instance () noexcept
@@ -66,10 +97,45 @@ namespace web
static void
register_hooks (apr_pool_t *pool) 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);
+
+ // The registered function is called for each client request.
+ //
ap_hook_handler (&request_handler<M>, NULL, NULL, APR_HOOK_LAST);
}
template <typename M>
+ static void
+ child_initializer (apr_pool_t *pchild, server_rec *s) noexcept
+ {
+ auto srv = instance<M> ();
+
+ try
+ {
+ srv->exemplar_.init (srv->conf_file_.c_str ());
+ }
+ 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 ());
+
+ // Terminate the root apache process.
+ //
+ ::kill (::getppid (), SIGTERM);
+ }
+ }
+
+ template <typename M>
static int
request_handler (request_rec* r) noexcept
{
@@ -78,29 +144,75 @@ namespace web
if (!r->handler || srv->name_ != r->handler)
return DECLINED;
+ static const std::string func_name (
+ "web::apache::service<" + srv->name_ + ">::request_handler");
+
request req (r);
- log l(r);
+ log l (r);
- // As soons as M (), handle () and flush () can throw need to handle
- // exceptions here.
- //
try
{
M m (static_cast<const M&> (srv->exemplar_));
static_cast<module&> (m).handle (req, req, l);
- return req.flush();
+ return req.flush ();
+ }
+ catch (const invalid_request& e)
+ {
+ if (!e.content.empty () && !req.get_write_state ())
+ {
+ try
+ {
+ req.content (e.status, e.type) << e.content;
+ return req.flush ();
+ }
+ catch (const std::exception& e)
+ {
+ l.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+ }
+ }
+
+ return e.status;
}
catch (const std::exception& e)
{
- l.write (nullptr, 0, __PRETTY_FUNCTION__, APLOG_ERR, e.what ());
+ l.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+
+ if (*e.what () && !req.get_write_state ())
+ {
+ try
+ {
+ req.content (HTTP_INTERNAL_SERVER_ERROR,
+ "text/plain;charset=utf-8")
+ << e.what ();
+
+ return req.flush ();
+ }
+ catch (const std::exception& e)
+ {
+ l.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+ }
+ }
}
catch (...)
{
- l.write (nullptr,
- 0,
- __PRETTY_FUNCTION__,
- APLOG_ERR,
- "unknown error");
+ l.write (nullptr, 0, func_name.c_str (), APLOG_ERR, "unknown error");
+
+ if (!req.get_write_state ())
+ {
+ try
+ {
+ req.content (HTTP_INTERNAL_SERVER_ERROR,
+ "text/plain;charset=utf-8")
+ << "unknown error";
+
+ return req.flush ();
+ }
+ catch (const std::exception& e)
+ {
+ l.write (nullptr, 0, func_name.c_str (), APLOG_ERR, e.what ());
+ }
+ }
+
}
return HTTP_INTERNAL_SERVER_ERROR;
@@ -122,7 +234,12 @@ namespace web
}
*/
std::string name_;
- const module& exemplar_;
+ module& exemplar_;
+ std::string conf_;
+ std::string conf_err_;
+ command_rec directives_[2];
+ std::string conf_file_;
+
// void (*handle_) (request&, response&, log&, const module&);
};
}
diff --git a/web/apache/stream b/web/apache/stream
index eb62b85..5f76347 100644
--- a/web/apache/stream
+++ b/web/apache/stream
@@ -5,28 +5,44 @@
#ifndef WEB_APACHE_STREAM
#define WEB_APACHE_STREAM
-#include <streambuf>
-#include <ios> // streamsize
-#include <algorithm> // min(), max()
-#include <cstring> // memmove()
-#include <memory> // unique_ptr
-
#include <httpd/httpd.h>
#include <httpd/http_protocol.h>
+#include <ios> // streamsize
+#include <memory> // unique_ptr
+#include <cstring> // memmove()
+#include <streambuf>
+#include <algorithm> // min(), max()
+
#include <web/module>
namespace web
{
namespace apache
{
- class ostreambuf : public std::streambuf
+ // Object of a class implementing this interface is intended for
+ // keeping the state of writing response to the client.
+ //
+ struct write_state
{
- public:
- ostreambuf (request_rec* rec) : rec_ (rec) {}
+ // Called by ostreambuf methods when some content to be written to the
+ // client.
+ //
+ virtual void
+ set_write_state () = 0;
+
+ // Called to check if any data have already been written to the client.
+ // Such checks required for some operations which are impossible to
+ // execute after response is partially written.
+ //
+ virtual bool
+ get_write_state () const noexcept = 0;
+ };
- bool
- write_flag () const noexcept {return write_;}
+ class ostreambuf: public std::streambuf
+ {
+ public:
+ ostreambuf (request_rec* rec, write_state& ws): rec_ (rec), ws_ (ws) {}
private:
virtual int_type
@@ -34,7 +50,7 @@ namespace web
{
if (c != traits_type::eof ())
{
- flag_write ();
+ ws_.set_write_state ();
char chr = c;
@@ -51,7 +67,7 @@ namespace web
virtual std::streamsize
xsputn (const char* s, std::streamsize num)
{
- flag_write ();
+ ws_.set_write_state ();
if (ap_rwrite (s, num, rec_) < 0)
{
@@ -64,7 +80,7 @@ namespace web
virtual int
sync ()
{
- if(ap_rflush (rec_) < 0)
+ if (ap_rflush (rec_) < 0)
{
throw invalid_request (HTTP_REQUEST_TIME_OUT);
}
@@ -72,40 +88,30 @@ namespace web
return 0;
}
- void
- flag_write () noexcept
- {
- if (!write_)
- {
- // Preparing to write a response read and discard request
- // body if any.
- //
- int r = ap_discard_request_body (rec_);
-
- if (r != OK)
- {
- throw invalid_request (r);
- }
-
- write_ = true;
- }
- }
-
private:
request_rec* rec_;
- bool write_ {false};
+ write_state& ws_;
};
- class istreambuf : public std::streambuf
+ class istreambuf: public std::streambuf
{
public:
- istreambuf (request_rec* rec, size_t bufsize = 1024, size_t putback = 1)
+ istreambuf (request_rec* rec,
+ write_state& ws,
+ size_t bufsize = 1024,
+ size_t putback = 1)
: rec_ (rec),
+ ws_ (ws),
bufsize_ (std::max (bufsize, (size_t)1)),
putback_ (std::min (putback, bufsize_ - 1)),
buf_ (new char[bufsize_])
{
+ if (ws_.get_write_state ())
+ {
+ throw sequence_error ("::web::apache::istreambuf::istreambuf");
+ }
+
char* p = buf_.get () + putback_;
setg (p, p, p);
@@ -121,6 +127,11 @@ namespace web
virtual int_type
underflow ()
{
+ if (ws_.get_write_state ())
+ {
+ throw sequence_error ("::web::apache::istreambuf::underflow");
+ }
+
if (gptr () < egptr ())
return traits_type::to_int_type (*gptr ());
@@ -144,15 +155,13 @@ namespace web
return traits_type::to_int_type (*gptr ());
}
- bool error () const noexcept {return error_;}
-
private:
request_rec* rec_;
+ write_state& ws_;
size_t bufsize_;
size_t putback_;
std::unique_ptr<char[]> buf_;
- bool error_ {false};
};
}
diff --git a/web/module b/web/module
index 3e97ff7..31d0967 100644
--- a/web/module
+++ b/web/module
@@ -26,22 +26,24 @@ namespace web
// By default 400 is returned, which means the request is malformed.
//
// If caught by the web server implementation, it will try to return
- // the specified status and description to the client, if possible.
+ // the specified status and content to the client, if possible.
// It is, however, may not be possible if some unbuffered content has
// already been written. The behavior in this case is implementation-
// specific and may result in no indication of an error being sent to
- // the client. If description is not empty, then it is assumed to be
- // encoded in UTF-8.
+ // the client.
//
struct invalid_request
{
status_code status;
- std::string description;
+ std::string content;
+ std::string type;
//@@ Maybe optional "try again" link?
//
- invalid_request (status_code s = 400, std::string d = "")
- : status (s), description (std::move (d)) {}
+ invalid_request (status_code s = 400,
+ std::string c = "",
+ std::string t = "text/plain;charset=utf-8")
+ : status (s), content (std::move (c)), type (std::move (t)) {}
};
// Exception indicating HTTP request/response sequencing error.
@@ -163,6 +165,15 @@ namespace web
class module
{
public:
+ // The web server calls this method on the module exemplar prior
+ // accepting client requests. Configuration file path is passed
+ // as a parameter. The way configuration file content interpreted is
+ // module implementation specific. Any exception thrown terminates web
+ // server.
+ //
+ virtual void
+ init (const char* path) = 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,