From a20443c285dabdec8d2ee740500c62e31ad90c7b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 23 Apr 2015 12:43:52 +0200 Subject: Implement apache service --- brep/module | 27 +++++------ brep/module.cxx | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++----- brep/search.cxx | 31 +++++++++--- brep/view | 20 ++++++++ brep/view.cxx | 15 ++++++ 5 files changed, 203 insertions(+), 34 deletions(-) create mode 100644 brep/view create mode 100644 brep/view.cxx (limited to 'brep') diff --git a/brep/module b/brep/module index 3f7b409..d976559 100644 --- a/brep/module +++ b/brep/module @@ -5,7 +5,9 @@ #ifndef BREP_MODULE #define BREP_MODULE +#include #include // move() +#include #include @@ -20,28 +22,14 @@ namespace brep // web::module and our module. Or maybe not, need to try. // using web::status_code; + using web::invalid_request; + using web::sequence_error; using web::name_value; using web::name_values; using web::request; using web::response; using web::log; - // This exception is used to signal that the request is invalid - // (4XX codes) rather than that it could not be processed (5XX). - // By default 422 is returned, which means the request was - // semantically invalid. - // - struct invalid_request - { - status_code status {422}; - std::string description; - - //@@ Maybe optional "try again" link? - // - invalid_request (std::string d, status_code s = 422) - : status (s), description (std::move (d)) {} - }; - // And 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 @@ -105,6 +93,7 @@ namespace brep // protected: module (); + module (const module& ); virtual void handle (request&, response&, log&); @@ -114,6 +103,12 @@ namespace brep private: log* log_ {nullptr}; // Diagnostics backend provided by the web server. + // 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); + void log_write (diag_data&&) const; diff --git a/brep/module.cxx b/brep/module.cxx index 15e996f..17790de 100644 --- a/brep/module.cxx +++ b/brep/module.cxx @@ -4,7 +4,16 @@ #include +#include +#include #include // bind() +#include // strncmp() + +#include + +#include + +#include using namespace std; using namespace placeholders; // For std::bind's _1, etc. @@ -15,6 +24,7 @@ namespace brep handle (request& rq, response& rs, log& l) { log_ = &l; + const basic_mark error (severity::error, log_writer_, __PRETTY_FUNCTION__); try { @@ -22,39 +32,151 @@ namespace brep } catch (const invalid_request& e) { - // @@ Both log and format as HTML in proper style, etc. - // - rs.content (e.status, "text/html;charset=utf-8") << e.description; + if (e.description.empty ()) + { + rs.status (e.status); + } + else + { + try + { + rs.content (e.status, "text/html;charset=utf-8") << e.description; + } + catch (const sequence_error& se) + { + error << se.what (); + rs.status (e.status); + } + } } catch (server_error& e) // Non-const because of move() below. { - // @@ Both log and return as 505. - // log_write (move (e.data)); + rs.status (HTTP_INTERNAL_SERVER_ERROR); } catch (const exception& e) { - // @@ Exception: log e.what () & 505. - // - rs.status (505); + error << e.what (); + rs.status (HTTP_INTERNAL_SERVER_ERROR); } catch (...) { - // @@ Unknown exception: log & 505. - // - rs.status (505); + error << "unknown error"; + rs.status (HTTP_INTERNAL_SERVER_ERROR); } } module:: module (): log_writer_ (bind (&module::log_write, this, _1)) {} + // Custom copy constructor is required to initialize log_writer_ properly. + // + module:: + module (const module& m): module () {verb_ = m.verb_;} + +// For function func declared like this: +// using B = std::string (*)(int); +// using A = B (*)(int,int); +// A func(B (*)(char),B (*)(wchar_t)); +// __PRETTY_FUNCTION__ looks like this: +// virtual std::string (* (* brep::search::func(std::string (* (*)(char))(int)\ +// ,std::string (* (*)(wchar_t))(int)) const)(int, int))(int) +// + string module:: + func_name (const string& pretty_name) + { + string::size_type b (0); + string::size_type e (pretty_name.find (' ')); + + // Position b at beginning of supposed function name, + // + if (e != string::npos && !strncmp (pretty_name.c_str (), "virtual ", 8)) + { + // Skip keyword virtual. + // + b = pretty_name.find_first_not_of (' ', e); + e = pretty_name.find (' ', b); + } + + 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) + { + size_t d (1); + + while (--e > b && d) + { + switch (pretty_name[e]) + { + case ')': ++d; break; + case '(': --d; break; + } + } + + if (!d) + { + 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); + } + } + } + + throw invalid_argument (""); + } + void module:: log_write (diag_data&& d) const { if (log_ == nullptr) return; // No backend yet. + auto al = dynamic_cast<::web::apache::log*> (log_); + + if (al) + { + // Considered using lambda for mapping but looks too verbose while can + // be a bit safer in runtime. + // + static int s[] = { APLOG_ERR, APLOG_WARNING, APLOG_INFO, APLOG_TRACE1 }; + + for (const auto& e : d) + { + string name; + + try + { + name = func_name (e.name); + } + catch (const invalid_argument&) + { + // Log "pretty" function description, see in log file & fix. + name = e.name; + } + + al->write (e.loc.file.c_str(), + e.loc.line, + name.c_str(), + s[static_cast (e.sev)], + e.msg.c_str()); + } + } + //@@ Cast log_ to apache::log and write the records. // diff --git a/brep/search.cxx b/brep/search.cxx index 683016c..93690e4 100644 --- a/brep/search.cxx +++ b/brep/search.cxx @@ -5,6 +5,10 @@ #include #include +#include +#include + +#include using namespace std; @@ -15,26 +19,39 @@ namespace brep { MODULE_DIAG; + chrono::seconds ma (60); + rs.cookie ("Oh", " Ah\n\n", &ma, "/"); + rs.cookie ("Hm", ";Yes", &ma); + + info << "handling search request from "; // << rq.client_ip (); + + ostream& o (rs.content (200, "text/html;charset=utf-8", false)); + + o << "Params:"; + const name_values& ps (rq.parameters ()); if (ps.empty ()) - throw invalid_request ("search parameters expected"); + throw invalid_request (422, "search parameters expected"); if (ps.size () > 100) fail << "too many parameters: " << ps.size () << info << "are you crazy to specify so many?"; - info << "handling search request from "; // << rq.client_ip (); - level2 ([&]{trace << "search request with " << ps.size () << " params";}); - ostream& o (rs.content (202, "text/html;charset=utf-8")); + for (const auto& p: ps) + { + o << "
\n" << p.name << "=" << p.value; + } - o << "Search page:" << endl; + o << "
\nCookies:"; - for (const name_value& p: ps) + for (const auto& c : rq.cookies ()) { - o << p.name << "=" << p.value << endl; + o << "
\n" << c.name << "=" << c.value << " "; } + + o << ""; } } diff --git a/brep/view b/brep/view new file mode 100644 index 0000000..819eff3 --- /dev/null +++ b/brep/view @@ -0,0 +1,20 @@ +// file : brep/view -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifndef BREP_VIEW +#define BREP_VIEW + +#include + +namespace brep +{ + class view: public module + { + public: + virtual void + handle (request&, response&); + }; +} + +#endif // BREP_VIEW diff --git a/brep/view.cxx b/brep/view.cxx new file mode 100644 index 0000000..6dafa1b --- /dev/null +++ b/brep/view.cxx @@ -0,0 +1,15 @@ +// file : brep/view.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#include + +using namespace std; + +namespace brep +{ + void view:: + handle (request& rq, response& rs) + { + } +} -- cgit v1.1