diff options
-rw-r--r-- | brep/buildfile | 28 | ||||
-rw-r--r-- | brep/module | 2 | ||||
-rw-r--r-- | brep/module.cxx | 25 | ||||
-rw-r--r-- | brep/options.cli | 34 | ||||
-rw-r--r-- | brep/package-search (renamed from brep/search) | 14 | ||||
-rw-r--r-- | brep/package-search.cxx | 154 | ||||
-rw-r--r-- | brep/package-version-search (renamed from brep/view) | 14 | ||||
-rw-r--r-- | brep/package-version-search.cxx | 73 | ||||
-rw-r--r-- | brep/search.cxx | 169 | ||||
-rw-r--r-- | brep/services.cxx | 26 | ||||
-rw-r--r-- | brep/shared-database | 4 | ||||
-rw-r--r-- | brep/shared-database.cxx | 8 | ||||
-rw-r--r-- | brep/view.cxx | 131 | ||||
-rwxr-xr-x | build.sh | 11 | ||||
-rw-r--r-- | etc/httpd.conf | 32 | ||||
-rw-r--r-- | etc/package-search.conf | 11 | ||||
-rw-r--r-- | etc/package-version-search.conf | 11 | ||||
-rw-r--r-- | etc/search.conf | 8 | ||||
-rw-r--r-- | etc/view.conf | 5 | ||||
-rw-r--r-- | tests/web/xhtml/driver.cxx | 28 | ||||
-rw-r--r-- | web/apache/request | 12 | ||||
-rw-r--r-- | web/apache/request.cxx | 125 | ||||
-rw-r--r-- | web/mime-url-encoding | 26 | ||||
-rw-r--r-- | web/mime-url-encoding.cxx | 135 | ||||
-rw-r--r-- | web/module | 12 | ||||
-rw-r--r-- | web/xhtml | 53 | ||||
-rw-r--r-- | www/htdocs/index.html | 31 |
27 files changed, 637 insertions, 545 deletions
diff --git a/brep/buildfile b/brep/buildfile index 5340d22..9b64ed4 100644 --- a/brep/buildfile +++ b/brep/buildfile @@ -6,20 +6,36 @@ using cli .: libso{brep brep-apache} -import libs += libbpkg%lib{bpkg} -import libs += libodb-pgsql%lib{odb-pgsql} +# brep library build rules. +# import libs += libodb%lib{odb} +import libs += libodb-pgsql%lib{odb-pgsql} +import libs += libbpkg%lib{bpkg} brep = cxx{package package-odb} + libso{brep}: $brep $libs libso{brep}: cxx.export.poptions = -I$out_root -I$src_root -brep = cxx{diagnostics module services search view shared-database} \ - cli.cxx{options} -web = ../web/apache/cxx{request service} +# brep-apache library build rules. +# +import libs += libstudxml%lib{studxml} + +brep = cxx{diagnostics module services package-search package-version-search \ + shared-database} cli.cxx{options} +web = ../web/apache/cxx{request service} ../web/cxx{mime-url-encoding} + libso{brep-apache}: $brep $web libso{brep} $libs +# Set option prefix to the empty value to handle all unknown request parameters +# uniformly with a single catch block. +# +# @@ Adding --option-prefix "" leads to appearing the following warnings +# related to options _parse methed: +# brep/options.cxx:663:33: warning: unused parameter ‘opt_mode’ [-Wunused-parameter] +# ::cli::unknown_mode opt_mode, +# cli.options += -I $src_root --include-with-brackets --include-prefix brep \ ---guard-prefix BREP --generate-file-scanner --suppress-usage +--guard-prefix BREP --generate-file-scanner --suppress-usage --option-prefix "" cli.cxx{options}: cli{options} diff --git a/brep/module b/brep/module index 08fbfea..0743598 100644 --- a/brep/module +++ b/brep/module @@ -92,6 +92,8 @@ namespace brep module (); module (const module& ); + // Can be used by module implementation to parse HTTP request parameters. + // class param_scanner: public cli::scanner { public: diff --git a/brep/module.cxx b/brep/module.cxx index c52d13a..62c70c9 100644 --- a/brep/module.cxx +++ b/brep/module.cxx @@ -22,6 +22,7 @@ using namespace std; using namespace placeholders; // For std::bind's _1, etc. +using namespace cli; namespace brep { @@ -98,22 +99,22 @@ namespace brep { // Read module implementation configuration. // - cli::argv_file_scanner s (0, - argc, - const_cast<char**> (argv.data ()), - "conf"); + argv_file_scanner s (0, + argc, + const_cast<char**> (argv.data ()), + "conf"); init (s); } // Read brep::module configuration. // - cli::argv_file_scanner s (0, - argc, - const_cast<char**> (argv.data ()), - "conf"); + argv_file_scanner s (0, + argc, + const_cast<char**> (argv.data ()), + "conf"); - options::module o (s, cli::unknown_mode::skip, cli::unknown_mode::skip); + options::module o (s, unknown_mode::skip, unknown_mode::skip); verb_ = o.verb (); } catch (const server_error& e) @@ -253,7 +254,7 @@ namespace brep if (i_ != name_values_.end ()) return name_ ? i_->name.c_str () : i_->value.c_str (); else - throw cli::eos_reached (); + throw eos_reached (); } const char* module::param_scanner:: @@ -270,7 +271,7 @@ namespace brep return r; } else - throw cli::eos_reached (); + throw eos_reached (); } void module::param_scanner:: @@ -284,6 +285,6 @@ namespace brep name_ = !name_; } else - throw cli::eos_reached (); + throw eos_reached (); } } diff --git a/brep/options.cli b/brep/options.cli index 2894979..8062f59 100644 --- a/brep/options.cli +++ b/brep/options.cli @@ -3,7 +3,7 @@ // license : MIT; see accompanying LICENSE file include <string>; -include <cstdint>; +include <cstdint>; // uint16_t namespace brep { @@ -22,23 +22,43 @@ namespace brep std::uint16_t db-port = 5432; }; - class search: module, db + class package_search: module, db { - size_t results-on-page = 10; + std::uint16_t results-on-page = 10; }; - class view: module, db + class package_version_search: module, db { + std::uint16_t results-on-page = 10; }; } - // Web module request parameters. + // Web module HTTP request parameters. // namespace params { - class search + // Use parameters long names in the C++ code, short aliases in HTTP URL. + // + class package_search { - size_t page = 0; + // Display package search result list starting from this page. + // + std::uint16_t page | p = 0; + + // Package search criteria. + // + std::string query | q = ""; + }; + + class package_version_search + { + // Display package version search result list starting from this page. + // + std::uint16_t page | p = 0; + + // Package version search criteria. + // + std::string query | q = ""; }; } } diff --git a/brep/search b/brep/package-search index 0283859..156ed68 100644 --- a/brep/search +++ b/brep/package-search @@ -1,20 +1,20 @@ -// file : brep/search -*- C++ -*- +// file : brep/package-search -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#ifndef BREP_SEARCH -#define BREP_SEARCH +#ifndef BREP_PACKAGE_SEARCH +#define BREP_PACKAGE_SEARCH #include <memory> // shared_ptr -#include <odb/database.hxx> +#include <odb/forward.hxx> // database #include <brep/module> #include <brep/options> namespace brep { - class search: public module + class package_search: public module { private: virtual void @@ -24,9 +24,9 @@ namespace brep init (cli::scanner&); private: - std::shared_ptr<options::search> options_; + std::shared_ptr<options::package_search> options_; std::shared_ptr<odb::core::database> db_; }; } -#endif // BREP_SEARCH +#endif // BREP_PACKAGE_SEARCH diff --git a/brep/package-search.cxx b/brep/package-search.cxx new file mode 100644 index 0000000..3317d43 --- /dev/null +++ b/brep/package-search.cxx @@ -0,0 +1,154 @@ +// file : brep/package-search.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <brep/package-search> + +#include <string> +#include <memory> // make_shared() + +#include <xml/serializer> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <web/xhtml> +#include <web/module> +#include <web/mime-url-encoding> + +#include <brep/package> +#include <brep/package-odb> +#include <brep/shared-database> + +using namespace std; +using namespace cli; +using namespace odb::core; + +namespace brep +{ + void package_search:: + init (scanner& s) + { + MODULE_DIAG; + + options_ = make_shared<options::package_search> ( + s, unknown_mode::fail, unknown_mode::fail); + + db_ = shared_database (options_->db_host (), options_->db_port ()); + } + + void package_search:: + handle (request& rq, response& rs) + { + using namespace xml; + using namespace web; + using namespace web::xhtml; + + MODULE_DIAG; + + params::package_search pr; + + try + { + param_scanner s (rq.parameters ()); + pr = params::package_search (s, unknown_mode::fail, unknown_mode::fail); + } + catch (const unknown_argument& e) + { + throw invalid_request (400, e.what ()); + } + + // @@ Would be nice to have a manipulator identing string properly + // according to the most nested element identation. + // + const char* ident ("\n "); + const char* title ("Package Search"); + serializer s (rs.content (), title); + + s << HTML + << HEAD + << TITLE << title << ~TITLE + << STYLE(TYPE="text/css") << ident + << ".package {margin: 0 0 0.5em;}" << ident + << ".name a {text-decoration: none;}" << ident + << ".summary {font-size: small;}" + << ~STYLE + << ~HEAD + << BODY; + + string q ( + pr.query ().empty () ? "" : "q=" + mime_url_encode (pr.query ())); + + transaction t (db_->begin ()); + + // @@ Use appropriate view when clarify which package info to be displayed + // and search index structure get implemented. + // + using query = query<package>; + auto r ( + db_->query<package> ( + "ORDER BY" + query::name + + "OFFSET" + to_string (pr.page () * options_->results_on_page ()) + + "LIMIT" + to_string (options_->results_on_page ()))); + + for (const auto& p: r) + { + s << DIV(CLASS="package") + << DIV(CLASS="name") + << A + << HREF + << "/go/" << mime_url_encode (p.name); + + // Propagate search criteria to the package version search url. + // + if (!q.empty ()) + s << "?" << q; + + s << ~HREF + << p.name + << ~A + << ~DIV + << DIV(CLASS="summary") + << p.summary + << ~DIV + << ~DIV; + } + + t.commit (); + + if (pr.page () || r.size () == options_->results_on_page ()) + { + s << DIV; + + if (pr.page ()) + { + s << A + << HREF << "/?p=" << pr.page () - 1 + << (q.empty () ? "" : "&" + q) + << ~HREF + << "Previous" + << ~A + << " "; + } + + // @@ Not ideal as can produce link to an empty page, but easy to fix + // and most likelly will be replaced with something more meaningful + // based on knowing the total number of matched packages. + // + if (r.size () == options_->results_on_page ()) + { + s << A + << HREF << "/?p=" << pr.page () + 1 + << (q.empty () ? "" : "&" + q) + << ~HREF + << "Next" + << ~A; + } + + s << ~DIV; + } + + s << ~BODY + << ~HTML; + } +} diff --git a/brep/view b/brep/package-version-search index 280b9ab..0292ae0 100644 --- a/brep/view +++ b/brep/package-version-search @@ -1,20 +1,20 @@ -// file : brep/view -*- C++ -*- +// file : brep/package-version-search -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#ifndef BREP_VIEW -#define BREP_VIEW +#ifndef BREP_PACKAGE_VERSION_SEARCH +#define BREP_PACKAGE_VERSION_SEARCH #include <memory> // shared_ptr -#include <odb/database.hxx> +#include <odb/forward.hxx> // database #include <brep/module> #include <brep/options> namespace brep { - class view: public module + class package_version_search: public module { private: virtual void @@ -24,9 +24,9 @@ namespace brep init (cli::scanner&); private: - std::shared_ptr<options::view> options_; + std::shared_ptr<options::package_version_search> options_; std::shared_ptr<odb::core::database> db_; }; } -#endif // BREP_VIEW +#endif // BREP_PACKAGE_VERSION_SEARCH diff --git a/brep/package-version-search.cxx b/brep/package-version-search.cxx new file mode 100644 index 0000000..a989434 --- /dev/null +++ b/brep/package-version-search.cxx @@ -0,0 +1,73 @@ +// file : brep/package-version-search.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <brep/package-version-search> + +#include <string> +#include <memory> // make_shared() + +#include <xml/serializer> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <web/xhtml> +#include <web/module> +#include <web/mime-url-encoding> + +#include <brep/package> +#include <brep/package-odb> +#include <brep/shared-database> + +using namespace std; +using namespace cli; +using namespace odb::core; + +namespace brep +{ + void package_version_search:: + init (scanner& s) + { + MODULE_DIAG; + + options_ = make_shared<options::package_version_search> ( + s, unknown_mode::fail, unknown_mode::fail); + + db_ = shared_database (options_->db_host (), options_->db_port ()); + } + + void package_version_search:: + handle (request& rq, response& rs) + { + using namespace xml; + using namespace web; + using namespace web::xhtml; + + MODULE_DIAG; + + const string& name (*rq.path ().rbegin ()); + params::package_version_search pr; + + try + { + param_scanner s (rq.parameters ()); + pr = params::package_version_search ( + s, unknown_mode::fail, unknown_mode::fail); + } + catch (const unknown_argument& e) + { + throw invalid_request (400, e.what ()); + } + + const char* title ("Package"); + serializer s (rs.content (), title); + + s << HTML + << HEAD + << TITLE << title << ~TITLE + << ~HEAD + << BODY << name << ~BODY + << ~HTML; + } +} diff --git a/brep/search.cxx b/brep/search.cxx deleted file mode 100644 index 9185aa5..0000000 --- a/brep/search.cxx +++ /dev/null @@ -1,169 +0,0 @@ -// file : brep/search.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <brep/search> - -#include <memory> // shared_ptr, make_shared() -#include <chrono> -#include <ostream> - -#include <odb/session.hxx> -#include <odb/database.hxx> -#include <odb/transaction.hxx> - -#include <butl/path> - -#include <web/module> - -#include <brep/package> -#include <brep/package-odb> -#include <brep/shared-database> - -using namespace std; -using namespace odb::core; - -namespace brep -{ - void search:: - init (cli::scanner& s) - { - MODULE_DIAG; - - options_ = make_shared<options::search> (s, - cli::unknown_mode::fail, - cli::unknown_mode::fail); - - db_ = shared_database (options_->db_host (), options_->db_port ()); - - 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; - - shared_ptr<package> cli ( - make_shared<package> ("cli", - "CLI is ...", - strings ({"compiler", "c++"}), - string ("This is CLI"), - url (), - url (), - email (), - email ())); - - shared_ptr<repository> stable ( - make_shared<repository> ( - repository_location ("http://pkg.cpp.org/1/stable"), - "Stable", - dir_path ("/var/pkg/1/stable"))); - - licenses l; - l.comment = "License\"A'"; - l.push_back ("XXX"); - l.push_back ("AAA"); - l.push_back ("BBB"); - l.push_back ("CCC"); - - dependency_alternatives da; - da.push_back ( - {"icl", version_comparison{version ("1.3.3"), comparison::gt}}); - - da.push_back ( - {"ocl", version_comparison{version ("1.5.5"), comparison::lt}}); - - requirement_alternatives ra1; - ra1.push_back ("TAO"); - ra1.push_back ("ORBacus"); - - requirement_alternatives ra2; - ra2.push_back ("Xerces"); - - shared_ptr<package_version> v ( - make_shared<package_version> (stable, - cli, - version ("1.1"), - priority (), - license_alternatives ({l}), - "some changes 1\nsome changes 2", - dependencies ({da}), - requirements ({ra1, ra2}))); - - transaction t (db_->begin ()); -// t.tracer (odb::stderr_full_tracer); - - { - db_->persist (cli); - db_->persist (stable); - db_->persist (v); - } - - t.commit (); - - 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 ()); - - 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 << "<p>\n<b>Params:</b>"; - - const name_values& ps (rq.parameters ()); - - if (ps.empty ()) - 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?"; - - level2 ([&]{trace << "search request with " << ps.size () << " params";}); - - for (const auto& p: ps) - { - o << "<br>\n" << p.name << "=" << p.value; - } - - param_scanner s (ps); - unique_ptr<params::search> prm; - - try - { - prm.reset (new params::search (s, - cli::unknown_mode::fail, - cli::unknown_mode::fail)); - } - catch (const cli::unknown_argument& e) - { - throw invalid_request (400, e.what ()); - } - - o << "<br>\nPage num: " << prm->page (); - o << "<p>\n<b>Cookies:</b>"; - - for (const auto& c: rq.cookies ()) - { - o << "<br>\n" << c.name << "=" << c.value; - } - - o << "<p><a href='view'>View</a>" - << "</body></html>"; - } -} diff --git a/brep/services.cxx b/brep/services.cxx index 86b27b7..510a231 100644 --- a/brep/services.cxx +++ b/brep/services.cxx @@ -1,21 +1,25 @@ -// file : services.cxx -*- C++ -*- +// file : brep/services.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <brep/view> -#include <brep/search> +#include <ap_config.h> // AP_MODULE_DECLARE_DATA #include <web/apache/service> +#include <brep/package-search> +#include <brep/package-version-search> + using namespace brep; using web::apache::service; -static search search_mod; -service AP_MODULE_DECLARE_DATA search_srv ("search", - search_mod, - {"db-host", "db-port", "conf"}); +static package_search package_search_mod; +service AP_MODULE_DECLARE_DATA package_search_srv ( + "package-search", + package_search_mod, + {"db-host", "db-port", "conf"}); -static view view_mod; -service AP_MODULE_DECLARE_DATA view_srv ("view", - view_mod, - {"db-host", "db-port", "conf"}); +static package_version_search package_version_search_mod; +service AP_MODULE_DECLARE_DATA package_version_search_srv ( + "package-version-search", + package_version_search_mod, + {"db-host", "db-port", "conf"}); diff --git a/brep/shared-database b/brep/shared-database index cad526f..36fa0a2 100644 --- a/brep/shared-database +++ b/brep/shared-database @@ -14,8 +14,8 @@ namespace brep { // Returns pointer to the shared database instance, creating one on the // first call. On subsequent calls ensures passed host and port equals - // to ones of the shared database instance throwing runtime_error otherwise. - // Is not thread-safe. + // to ones of the existing database instance throwing runtime_error + // otherwise. Is not thread-safe. // std::shared_ptr<odb::core::database> shared_database (const std::string& host, unsigned int port); diff --git a/brep/shared-database.cxx b/brep/shared-database.cxx index fb952d8..fc17659 100644 --- a/brep/shared-database.cxx +++ b/brep/shared-database.cxx @@ -10,14 +10,14 @@ #include <odb/pgsql/database.hxx> using namespace std; -using namespace odb; namespace brep { - shared_ptr<database> - shared_database (const string& host, unsigned int port) + shared_ptr<odb::database> + shared_database (const string& h, unsigned int p) { - static weak_ptr<pgsql::database> db; + using odb::pgsql::database; + static weak_ptr<database> db; // In C++11, function-static variable initialization is // guaranteed to be thread-safe, thought this doesn't diff --git a/brep/view.cxx b/brep/view.cxx deleted file mode 100644 index 3098e93..0000000 --- a/brep/view.cxx +++ /dev/null @@ -1,131 +0,0 @@ -// file : brep/view.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <brep/view> - -#include <memory> // shared_ptr, make_shared() -#include <ostream> - -#include <odb/session.hxx> -#include <odb/database.hxx> -#include <odb/transaction.hxx> - -#include <web/module> - -#include <brep/package> -#include <brep/package-odb> -#include <brep/shared-database> - -using namespace std; -using namespace odb::core; - -#pragma db namespace session -namespace brep -{ - void view:: - init (cli::scanner& s) - { - options_ = make_shared<options::view> (s, - cli::unknown_mode::fail, - cli::unknown_mode::fail); - - db_ = shared_database (options_->db_host (), options_->db_port ()); - } - - void view:: - handle (request& rq, response& rs) - { - session s; - transaction t (db_->begin ()); - - shared_ptr<package> p (db_->load<package> ("cli")); - - for (auto& vp: p->versions) - { - shared_ptr<package_version> v (vp.load ()); - v->repository.load (); - v->package.load (); - } - - t.commit (); - - 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>\n" << p->name << ": " << p->versions.size (); - - for (const auto& vp: p->versions) - { - // Just finds package_version object in session cache. - // - shared_ptr<package_version> v (vp.load ()); - - assert (v != nullptr); - assert (v->repository.get_eager () != nullptr); - assert (v->package.get_eager () != nullptr); - - o << "<br>version:" << v->version.string () - << "<br>package:" << v->package->name - << "<br>repo:" << v->repository->display_name - << "<br>changes:" << v->changes - << "<br>licenses:" << v->license_alternatives.size (); - - for (const auto& la: v->license_alternatives) - { - o << "<br>"; - - for (const auto& l: la) - { - o << " |" << l << "|"; - } - } - - o << "<br>deps:" << v->dependencies.size (); - - for (const auto& da: v->dependencies) - { - o << "<br>"; - - for (const auto& d: da) - { - o << " |" << d.package; - - if (d.version) - { - o << "," << d.version->value.string () << "," - << static_cast<int> (d.version->operation) << "|"; - } - } - } - - o << "<br>requirements:" << v->requirements.size (); - - for (const auto& ra: v->requirements) - { - o << "<br>"; - - for (const auto& r: ra) - { - o << " |" << r << "|"; - } - } - } - - o << "<p><a href='search?a=1&b&c=2&d=&&x=a+b'>Search</a>" - << "</body></html>"; - } -} @@ -25,16 +25,19 @@ g++ -shared $DEBUG -std=c++11 -I.. -I../../libbpkg \ echo "cli brep-apache options" cli --include-with-brackets --include-prefix brep --hxx-suffix "" \ - --guard-prefix BREP --generate-file-scanner --suppress-usage ./options.cli + --guard-prefix BREP --generate-file-scanner --suppress-usage \ + --option-prefix "" ./options.cli echo "g++ libbrep-apache.so" -s="search.cxx view.cxx module.cxx diagnostics.cxx services.cxx options.cxx \ -shared-database.cxx ../web/apache/request.cxx ../web/apache/service.cxx" +s="package-search.cxx package-version-search.cxx module.cxx diagnostics.cxx \ +services.cxx options.cxx shared-database.cxx \ +../web/apache/request.cxx ../web/apache/service.cxx \ +../web/mime-url-encoding.cxx" g++ -shared $DEBUG -std=c++11 -I. -I/usr/include/apr-1 -I/usr/include/httpd \ -I.. -I../../libbpkg -I../../libbutl -L. -L../../libbpkg/bpkg \ - -fPIC -o libbrep-apache.so $s -lbrep -lbpkg -lodb-pgsql -lodb + -fPIC -o libbrep-apache.so $s -lbrep -lbpkg -lodb-pgsql -lodb -lstudxml cd ../loader diff --git a/etc/httpd.conf b/etc/httpd.conf index 71ac903..ef8cb50 100644 --- a/etc/httpd.conf +++ b/etc/httpd.conf @@ -40,30 +40,33 @@ 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 -LoadModule search_srv ${AP_MODULE_DIR}/libbrep-apache.so +LoadModule package_search_srv ${AP_MODULE_DIR}/libbrep-apache.so -<IfModule search_srv> - search-db-host ${AP_DB_HOST} - search-db-port ${AP_DB_PORT} - search-conf "${AP_CONFIG_DIR}/search.conf" +<IfModule package_search_srv> + package-search-db-host ${AP_DB_HOST} + package-search-db-port ${AP_DB_PORT} + package-search-conf "${AP_CONFIG_DIR}/package-search.conf" </IfModule> -LoadModule view_srv ${AP_MODULE_DIR}/libbrep-apache.so +LoadModule package_version_search_srv ${AP_MODULE_DIR}/libbrep-apache.so -<IfModule view_srv> - view-db-host ${AP_DB_HOST} - view-db-port ${AP_DB_PORT} - view-conf "${AP_CONFIG_DIR}/view.conf" +<IfModule package_version_search_srv> + package-version-search-db-host ${AP_DB_HOST} + package-version-search-db-port ${AP_DB_PORT} + package-version-search-conf "${AP_CONFIG_DIR}/package-version-search.conf" </IfModule> -<LocationMatch ^/search$> - SetHandler search +<LocationMatch ^/$> + SetHandler package-search </LocationMatch> -<LocationMatch ^/view$> - SetHandler view +<LocationMatch ^/go/[^/]+$> + SetHandler package-version-search </LocationMatch> +# Location for package version details url +#<LocationMatch ^/go/[^/]+/[^/]+$> + ExtendedStatus On <Location /server-status> @@ -81,3 +84,4 @@ ExtendedStatus On DirectoryIndex index.html TypesConfig /etc/mime.types +AddOutputFilterByType DEFLATE application/xhtml+xml diff --git a/etc/package-search.conf b/etc/package-search.conf new file mode 100644 index 0000000..f7eb07c --- /dev/null +++ b/etc/package-search.conf @@ -0,0 +1,11 @@ +# file : etc/package-search.conf +# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file +# +# brep::module options +# +verb 1 + +# brep::package_search options +# +results-on-page 2 diff --git a/etc/package-version-search.conf b/etc/package-version-search.conf new file mode 100644 index 0000000..aa69170 --- /dev/null +++ b/etc/package-version-search.conf @@ -0,0 +1,11 @@ +# file : etc/package-version-search.conf +# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file +# +# brep::module options +# +verb 1 + +# brep::package_version_search options +# +results-on-page 10 diff --git a/etc/search.conf b/etc/search.conf deleted file mode 100644 index be86098..0000000 --- a/etc/search.conf +++ /dev/null @@ -1,8 +0,0 @@ -# brep::module options - -verb 1 - -# brep::search options - -#ah 7 -results-on-page 20 diff --git a/etc/view.conf b/etc/view.conf deleted file mode 100644 index 7c9b163..0000000 --- a/etc/view.conf +++ /dev/null @@ -1,5 +0,0 @@ -# brep::module options - -verb 2 - -#oh 8 diff --git a/tests/web/xhtml/driver.cxx b/tests/web/xhtml/driver.cxx index 23531f7..9f393fd 100644 --- a/tests/web/xhtml/driver.cxx +++ b/tests/web/xhtml/driver.cxx @@ -3,6 +3,7 @@ // license : MIT; see accompanying LICENSE file #include <iostream> +#include <functional> #include <xml/serializer> @@ -11,11 +12,38 @@ using namespace std; using namespace xml; +static bool +bad_sequence (const function<void(serializer& s)>& f) +{ + ostringstream o; + serializer s (o, "osstream"); + + try + { + f (s); + return false; + } + catch (const serialization&) + { + return true; + } +} + int main () { using namespace web::xhtml; + assert (bad_sequence ([](serializer& s) {s << HTML << ~HEAD;})); + assert (bad_sequence ([](serializer& s) {s << HTML << DIV << ~P << ~HTML;})); + assert (bad_sequence ([](serializer& s) {s << HTML << DIV << ~A << ~HTML;})); + assert (bad_sequence ([](serializer& s) {s << P << A << "a" << ~P << ~P;})); + assert (bad_sequence ([](serializer& s) {s << P << A << "a" << ~I << ~P;})); + + assert ( + bad_sequence ( + [](serializer& s) {s << P << A << ID << "A" << ~HREF << ~A << ~P;})); + serializer s (cout, "output"); s << HTML diff --git a/web/apache/request b/web/apache/request index 59d4600..88b38a9 100644 --- a/web/apache/request +++ b/web/apache/request @@ -43,6 +43,11 @@ namespace web int flush (); + // Get request path. + // + virtual const path_type& + path (); + // Get request body data stream. // virtual std::istream& @@ -93,12 +98,6 @@ namespace web void parse_parameters (const char* args); - static void - mime_url_encode (const char* v, std::ostream& o); - - static std::string - mime_url_decode (const char* b, const char* e, bool trim = false); - bool get_write_state () const noexcept {return write_state_;} @@ -129,6 +128,7 @@ namespace web std::unique_ptr<std::ostream> out_; std::unique_ptr<std::streambuf> in_buf_; std::unique_ptr<std::istream> in_; + path_type path_; std::unique_ptr<name_values> parameters_; std::unique_ptr<name_values> cookies_; std::unique_ptr<std::string> form_data_; diff --git a/web/apache/request.cxx b/web/apache/request.cxx index 7727b35..497d2d6 100644 --- a/web/apache/request.cxx +++ b/web/apache/request.cxx @@ -14,18 +14,20 @@ #include <memory> // unique_ptr #include <sstream> #include <ostream> +#include <istream> #include <cstring> #include <utility> // move() #include <stdexcept> #include <streambuf> +#include <web/mime-url-encoding> + using namespace std; namespace web { namespace apache { - istream& request:: content () { @@ -46,10 +48,25 @@ namespace web return *in_; } + const path& request:: + path () + { + if (path_.empty ()) + { + path_ = path_type (rec_->uri); + + // Module request handler can not be called if URI is empty. + // + assert (!path_.empty ()); + } + + return path_; + } + const name_values& request:: parameters () { - if (!parameters_) + if (parameters_ == nullptr) { parameters_.reset (new name_values ()); @@ -70,7 +87,7 @@ namespace web const name_values& request:: cookies () { - if (!cookies_) + if (cookies_ == nullptr) { cookies_.reset (new name_values ()); @@ -264,107 +281,5 @@ namespace web n = e ? e + 1 : 0; } } - - void request:: - mime_url_encode (const char* v, ostream& o) - { - char f (o.fill ()); - ios_base::fmtflags g (o.flags ()); - o << hex << uppercase << right << setfill ('0'); - - char c; - - while ((c = *v++) != '\0') - { - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9')) - { - o << c; - } - else - switch (c) - { - case ' ': o << '+'; break; - case '.': - case '_': - case '-': - case '~': o << c; break; - default: - { - o << "%" << setw (2) << static_cast<unsigned short> (c); - break; - } - } - } - - o.flags (g); - o.fill (f); - } - - string request:: - mime_url_decode (const char* b, const char* e, bool trim) - { - if (trim) - { - b += strspn (b, " "); - - if (b >= e) - return string (); - - while (*--e == ' '); - ++e; - } - - string value; - value.reserve (e - b); - - char bf[3]; - bf[2] = '\0'; - - while (b != e) - { - char c (*b++); - - switch (c) - { - case '+': - { - value.append (" "); - break; - } - case '%': - { - if (*b == '\0' || b[1] == '\0') - { - throw invalid_argument ( - "::web::apache::request::mime_url_decode short"); - } - - *bf = *b; - bf[1] = b[1]; - - char* ebf (nullptr); - size_t vl (strtoul (bf, &ebf, 16)); - - if (*ebf != '\0') - { - throw invalid_argument ( - "::web::apache::request::mime_url_decode wrong"); - } - - value.append (1, static_cast<char> (vl)); - b += 2; - break; - } - default: - { - value.append (1, c); - break; - } - } - } - - return value; - } } } diff --git a/web/mime-url-encoding b/web/mime-url-encoding new file mode 100644 index 0000000..9ae5d6d --- /dev/null +++ b/web/mime-url-encoding @@ -0,0 +1,26 @@ +// file : web/mime-url-encoding -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_MIME_URL_ENCODING +#define WEB_MIME_URL_ENCODING + +#include <string> +#include <iosfwd> + +namespace web +{ + void + mime_url_encode (const char* v, std::ostream& o); + + std::string + mime_url_encode (const char* v); + + std::string + mime_url_encode (const std::string& v); + + std::string + mime_url_decode (const char* b, const char* e, bool trim = false); +} + +#endif // WEB_MIME_URL_ENCODING diff --git a/web/mime-url-encoding.cxx b/web/mime-url-encoding.cxx new file mode 100644 index 0000000..1d68bf3 --- /dev/null +++ b/web/mime-url-encoding.cxx @@ -0,0 +1,135 @@ +// file : web/mime-url-encoding.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include <web/mime-url-encoding> + +#include <ios> // hex, uppercase, right +#include <string> +#include <iomanip> // setw(), setfill () +#include <ostream> +#include <sstream> +#include <cstring> // size_t, strspn() +#include <stdexcept> // invalid_argument + +using namespace std; + +namespace web +{ + // Encode characters different from unreserved ones specified in + // "2.3. Unreserved Characters" of http://tools.ietf.org/html/rfc3986. + // + void + mime_url_encode (const char* v, ostream& o) + { + char f (o.fill ()); + ostream::fmtflags g (o.flags ()); + o << hex << uppercase << right << setfill ('0'); + + char c; + while ((c = *v++) != '\0') + { + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9')) + { + o << c; + } + else + { + switch (c) + { + case ' ': o << '+'; break; + case '.': + case '_': + case '-': + case '~': o << c; break; + default: + { + o << "%" << setw (2) << static_cast<unsigned short> (c); + break; + } + } + } + } + + o.flags (g); + o.fill (f); + } + + string + mime_url_encode (const char* v) + { + stringstream o; + mime_url_encode (v, o); + return o.str (); + } + + string + mime_url_encode (const string& v) + { + return mime_url_encode (v.c_str ()); + } + + string + mime_url_decode (const char* b, const char* e, bool trim) + { + if (trim) + { + b += strspn (b, " "); + + if (b >= e) + return string (); + + while (*--e == ' '); + ++e; + } + + string value; + value.reserve (e - b); + + char bf[3]; + bf[2] = '\0'; + + while (b != e) + { + char c (*b++); + switch (c) + { + case '+': + { + value.append (" "); + break; + } + case '%': + { + if (*b == '\0' || b[1] == '\0') + { + throw invalid_argument ("::web::mime_url_decode short"); + } + + *bf = *b; + bf[1] = b[1]; + + char* ebf (nullptr); + size_t vl (strtoul (bf, &ebf, 16)); + + if (*ebf != '\0') + { + throw invalid_argument ("::web::mime_url_decode wrong"); + } + + value.append (1, static_cast<char> (vl)); + b += 2; + break; + } + default: + { + value.append (1, c); + break; + } + } + } + + return value; + } +} @@ -13,6 +13,8 @@ #include <utility> // move() #include <stdexcept> // runtime_error +#include <butl/path> + namespace web { // HTTP status code. @@ -68,10 +70,20 @@ namespace web }; using name_values = std::vector<name_value>; + using path = butl::path; class request { public: + using path_type = web::path; + + // Corresponds to abs_path portion of HTTP URL as described in + // "3.2.2 HTTP URL" of http://tools.ietf.org/html/rfc2616. + // Returns '/' if no abs_path is present in URL. + // + virtual const path_type& + path () = 0; + //@@ Why not pass parameters directly? Lazy parsing? //@@ Why not have something like operator[] for lookup? Probably // in name_values. @@ -5,6 +5,8 @@ #ifndef WEB_XHTML #define WEB_XHTML +#include <functional> // function + #include <xml/serializer> namespace web @@ -36,9 +38,9 @@ namespace web // namespace xhtml { - const char* xmlns = "http://www.w3.org/1999/xhtml"; + const char* const xmlns = "http://www.w3.org/1999/xhtml"; - using serializer_func = void (*) (xml::serializer&); + using serializer_func = std::function<void(xml::serializer&)>; struct attr_value_base { @@ -124,7 +126,10 @@ namespace web operator() (xml::serializer& s) const {s.start_element (xmlns, name);} virtual serializer_func - operator~ () const {return [](xml::serializer& s) {s.end_element ();};} + operator~ () const + { + return [this](xml::serializer& s) {s.end_element (xmlns, name);}; + } // s << elem(attr1 = 123, attr2 = "abc"); // @@ -143,8 +148,11 @@ namespace web return attr_element (*this, a1); } - protected: - element () = default; +// @@ Now always need to provide element name, so operator~ () could create +// the lambda capable to pass a valid name to end_element call. +// +// protected: +// element () = default; }; struct inline_element: element @@ -162,9 +170,9 @@ namespace web virtual serializer_func operator~ () const { - return [](xml::serializer& s) + return [this](xml::serializer& s) { - s.end_element (); s.resume_indentation (); + s.end_element (xmlns, name); s.resume_indentation (); }; } }; @@ -194,7 +202,10 @@ namespace web operator() (xml::serializer& s) const {s.start_attribute (name);} virtual serializer_func - operator~ () const {return [](xml::serializer& s) {s.end_attribute ();};} + operator~ () const + { + return [this](xml::serializer& s) {s.end_attribute (name);}; + } }; // Elements. @@ -206,13 +217,13 @@ namespace web // struct html_element: element { - html_element () {} // Uninitialized const static. + html_element (): element ("html") {} virtual void operator() (xml::serializer& s) const { s.doctype_decl ("html"); - s.start_element (xmlns, "html"); + s.start_element (xmlns, name); s.namespace_decl (xmlns, ""); } }; @@ -220,12 +231,12 @@ namespace web struct head_element: element { - head_element () {} // Uninitialized const static. + head_element (): element ("head") {} virtual void operator() (xml::serializer& s) const { - s.start_element (xmlns, "head"); + s.start_element (xmlns, name); s.start_element (xmlns, "meta"); s.attribute ("charset", "UTF-8"); s.end_element (); @@ -233,10 +244,13 @@ namespace web }; static const head_element HEAD; + static const element BODY ("body"); + static const element DIV ("div"); + static const element P ("p"); + static const element STYLE ("style"); static const element TITLE ("title"); - static const element BODY ("body"); - static const element P ("p"); + static const inline_element A ("a"); static const inline_element B ("b"); static const inline_element I ("i"); static const inline_element U ("u"); @@ -246,9 +260,16 @@ namespace web // Attributes. // - static const attribute ID ("id"); static const attribute CLASS ("class"); - static const attribute STYLE ("style"); + static const attribute HREF ("href"); + static const attribute ID ("id"); + static const attribute TYPE ("type"); + +// @@ Attribute variable names clash with element variable names. +// Should we prefix/suffix attribute names like _STYLE, STYLE_ or there +// are some better ideas ? +// +// static const attribute STYLE ("style"); } } diff --git a/www/htdocs/index.html b/www/htdocs/index.html deleted file mode 100644 index d8c1185..0000000 --- a/www/htdocs/index.html +++ /dev/null @@ -1,31 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta name="viewport" content="width=device-width, initial-scale=1"/> - <style type="text/css"> -body { - font-size : 1.1em; - line-height : 1.4em; - - text-align: justify; - - - /* 320px ~ 20em @ 1 font-size. This is factored in when doing font - boosting. Specifying 320px directly seems to mess it up. */ - min-width: 17em; - max-width: 40em; -} - </style> - </head> - <body> - <p>Home Page</p> - build2 is a build system aimed at improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build system aimed build2 is a build system aimed at improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build system aimed at improving cross-platform C++ at improving cross-platform C++ at improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build system aimed at ddw improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build system aimed at improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build system aimed at improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build sdhjsd system aimed at improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - build2 is a build system aimed at rkdf improving cross-platform C++ software building and library use. It will be accompanied by a C++ package manager and a central repository of open source C++ libraries. - </body> -</html> |