aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-08-14 13:03:08 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-08-23 13:53:50 +0200
commitbcd246076540a8353fa55fc0a5e19343c1a2dbc9 (patch)
tree9fe2c24ca33b3d670267a5cbc8c8c756589f359b
parent24903813d11813f8ff9ac906d23b21e6c33b981d (diff)
Implement package search service mockup
-rw-r--r--brep/buildfile28
-rw-r--r--brep/module2
-rw-r--r--brep/module.cxx25
-rw-r--r--brep/options.cli34
-rw-r--r--brep/package-search (renamed from brep/search)14
-rw-r--r--brep/package-search.cxx154
-rw-r--r--brep/package-version-search (renamed from brep/view)14
-rw-r--r--brep/package-version-search.cxx73
-rw-r--r--brep/search.cxx169
-rw-r--r--brep/services.cxx26
-rw-r--r--brep/shared-database4
-rw-r--r--brep/shared-database.cxx8
-rw-r--r--brep/view.cxx131
-rwxr-xr-xbuild.sh11
-rw-r--r--etc/httpd.conf32
-rw-r--r--etc/package-search.conf11
-rw-r--r--etc/package-version-search.conf11
-rw-r--r--etc/search.conf8
-rw-r--r--etc/view.conf5
-rw-r--r--tests/web/xhtml/driver.cxx28
-rw-r--r--web/apache/request12
-rw-r--r--web/apache/request.cxx125
-rw-r--r--web/mime-url-encoding26
-rw-r--r--web/mime-url-encoding.cxx135
-rw-r--r--web/module12
-rw-r--r--web/xhtml53
-rw-r--r--www/htdocs/index.html31
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>";
- }
-}
diff --git a/build.sh b/build.sh
index 3d06a80..ae71e72 100755
--- a/build.sh
+++ b/build.sh
@@ -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;
+ }
+}
diff --git a/web/module b/web/module
index e1cccf0..7398d45 100644
--- a/web/module
+++ b/web/module
@@ -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.
diff --git a/web/xhtml b/web/xhtml
index b8d6ce0..4fc6119 100644
--- a/web/xhtml
+++ b/web/xhtml
@@ -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>