diff --git a/CHANGELOG b/CHANGELOG index 93ebfa4d6e..b831d9da36 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ v1.0.1 (2012-XX-XX) ------------------- +* darft for issue #165: front-end application howto + * updated mruby to cf8fdea4a6598aa470e698e8cbc9b9b492319d * fix for issue #190: install doesn't create log directory diff --git a/Doxygen/arango.template b/Doxygen/arango.template index 95f0c6d96b..5d976951b8 100644 --- a/Doxygen/arango.template +++ b/Doxygen/arango.template @@ -1550,7 +1550,7 @@ HIDE_UNDOC_RELATIONS = YES # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will diff --git a/Makefile.in b/Makefile.in index 8df9601086..d88ff6d57c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.12 from Makefile.am. +# Makefile.in generated by automake 1.12.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2012 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -1657,7 +1656,9 @@ SHELL_COMMON = @srcdir@/js/common/tests/shell-document.js \ @srcdir@/js/common/tests/shell-unique-constraint.js \ @srcdir@/js/common/tests/shell-hash-index.js -SHELL_SERVER = $(SHELL_COMMON) +SHELL_SERVER = $(SHELL_COMMON) \ + @srcdir@/js/server/tests/routing.js + UNITTESTS_SERVER = $(addprefix --javascript.unit-tests ,$(SHELL_SERVER)) ################################################################################ diff --git a/Makefile.unittests b/Makefile.unittests index cbffcb0cd7..5233e4ab67 100755 --- a/Makefile.unittests +++ b/Makefile.unittests @@ -149,7 +149,8 @@ SHELL_COMMON = @srcdir@/js/common/tests/shell-document.js \ @srcdir@/js/common/tests/shell-unique-constraint.js \ @srcdir@/js/common/tests/shell-hash-index.js -SHELL_SERVER = $(SHELL_COMMON) +SHELL_SERVER = $(SHELL_COMMON) \ + @srcdir@/js/server/tests/routing.js .PHONY: unittests-shell-server diff --git a/aclocal.m4 b/aclocal.m4 index b8e7ed70b9..bc77b40672 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,8 +1,7 @@ -# generated automatically by aclocal 1.12 -*- Autoconf -*- +# generated automatically by aclocal 1.12.1 -*- Autoconf -*- + +# Copyright (C) 1996-2012 Free Software Foundation, Inc. -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, -# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, -# Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -37,7 +36,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.12' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.12], [], +m4_if([$1], [1.12.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -53,7 +52,7 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.12])dnl +[AM_AUTOMAKE_VERSION([1.12.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) @@ -154,7 +153,7 @@ fi])]) # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 16 +# serial 17 # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, @@ -166,7 +165,7 @@ fi])]) # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. -# NAME is "CC", "CXX", "GCJ", or "OBJC". +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was @@ -182,6 +181,7 @@ AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) @@ -422,7 +422,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 18 +# serial 19 # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. @@ -468,7 +468,10 @@ AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], -[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl +[AC_DIAGNOSE([obsolete], +[$0: two- and three-arguments forms are deprecated. For more info, see: +http://www.gnu.org/software/automake/manual/automake.html#Modernize-AM_INIT_AUTOMAKE-invocation]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl @@ -494,7 +497,8 @@ AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl -AC_REQUIRE([AM_PROG_MKDIR_P])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl # We need awk for the "check" target. The system "awk" is bad on # some platforms. AC_REQUIRE([AC_PROG_AWK])dnl @@ -506,16 +510,23 @@ _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], - [define([AC_PROG_CC], - defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], - [define([AC_PROG_CXX], - defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], - [define([AC_PROG_OBJC], - defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +dnl Support for Objective C++ was only introduced in Autoconf 2.65, +dnl but we still cater to Autoconf 2.62. +m4_ifdef([AC_PROG_OBJCXX], +[AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])])dnl ]) _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl dnl The 'parallel-tests' driver may need to know about EXEEXT, so add the @@ -726,34 +737,6 @@ else fi ]) -# Copyright (C) 2003-2012 Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# serial 2 - -# AM_PROG_MKDIR_P -# --------------- -# Check for 'mkdir -p'. -AC_DEFUN([AM_PROG_MKDIR_P], -[AC_PREREQ([2.60])dnl -AC_REQUIRE([AC_PROG_MKDIR_P])dnl -dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, -dnl while keeping a definition of mkdir_p for backward compatibility. -dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. -dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of -dnl Makefile.ins that do not define MKDIR_P, so we do our own -dnl adjustment using top_builddir (which is defined more often than -dnl MKDIR_P). -AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl -case $mkdir_p in - [[\\/$]]* | ?:[[\\/]]*) ;; - */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; -esac -]) - # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2012 Free Software Foundation, Inc. diff --git a/arangod/Documentation/user-manual.dox b/arangod/Documentation/user-manual.dox index 8879435b05..d7a3413923 100644 --- a/arangod/Documentation/user-manual.dox +++ b/arangod/Documentation/user-manual.dox @@ -350,10 +350,35 @@ /// /// /// @@ -454,8 +479,8 @@ /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/hello/world", -/// ........> callback: { +/// ........> url: { match: "/hello/world" }, +/// ........> content: { /// ........> contentType: "text/html", /// ........> body: "Hello World" }}); /// @endcode @@ -464,13 +489,186 @@ /// or call the internal reload function. /// /// @code -/// arangosh> require("internal").reloadRouting() +/// arangosh> require("org/arangodb/routing").reload() /// @endcode /// /// Now use the browser and access /// /// @LIT{http://localhost:8529/hello/world} /// +/// You should see the @LIT{Hello World} in our browser. +/// +/// @section UserManualActionsMatches Matching an URL +///////////////////////////////////////////////////// +/// +/// There are a lot of options for the @LIT{url} attribute. If you define +/// different routing for the same path, then the following simple rule is +/// applied in order to determine which match wins: If there are two matches, +/// then the more specific wins. I. e, if there is a wildcard match and an exact +/// match, the exact match is prefered. If there is a short and a long match, +/// the longer match wins. +/// +/// @subsection UserManualActionsMatchesExact Exact Match +/// +/// If the definition is +/// +/// @code +/// { url: { match: "/hello/world" } } +/// @endcode +/// +/// then the match must be exact. Only the request for @LIT{/hello/world} will +/// match, everything else, e. g. @LIT{/hello/world/my} or @LIT{/hello/world2}, +/// will not match. +/// +/// The following definition is a short-cut for an exact match. +/// +/// @code +/// { url: "/hello/world" } +/// @endcode +/// +/// @subsection UserManualActionsMatchesPrefix Prefix Match +/// +/// If the definition is +/// +/// @code +/// { url: { match: "/hello/world/*" } } +/// @endcode +/// +/// then the match can be a prefix match. The requests for @LIT{/hello/world}, +/// @LIT{/hello/world/my}, and @LIT{/hello/world/how/are/you} will all +/// match. However @LIT{/hello/world2} does not match. Prefix matches within an +/// URL part, i. e. @LIT{/hello/world*}, are not allowed. The wildcard must +/// occur at the end, i. e. +/// +/// @code +/// /hello/*/world +/// @endcode +/// +/// is also disallowed. +/// +/// If you define two routes +/// +/// @code +/// { url: { match: "/hello/world/*" } } +/// { url: { match: "/hello/world/emil" } } +/// @endcode +/// +/// then the second route will be used for @LIT{/hello/world/emil} because it is +/// more specific. +/// +/// @subsection UserManualActionsMatchesParameterized Parameterized Match +/// +/// A parameterized match is similar to a prefix match, but the parameters are +/// also allowed inside the URL path. +/// +/// If the definition is +/// +/// @code +/// { url: { match: "/hello/:name/world" } } +/// @endcode +/// +/// then the URL must have three parts, the first part being @LIT{hello} and the +/// third part @LIT{world}. For example, @LIT{/hello/emil/world} will match, +/// while @LIT{/hello/emil/meyer/world} will not. +/// +/// @subsection UserManualActionsMatchesConstraint Constraint Match +/// +/// A constraint match is similar to a parameterized match, but the parameters +/// carries a constraint. +/// +/// If the definition is +/// +/// @code +/// { url: { match: "/hello/:name/world", constraint: { name: "/[a-z]+/" } } +/// @endcode +/// +/// then the URL must have three parts, the first part being @LIT{hello} and the +/// third part @LIT{world}. The second part must be all lowercase. +/// +/// It is possible to use more then one constraint for the same URL part. +/// +/// @code +/// { url: { match: "/hello/:name|:id/world", +/// constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } } +/// @endcode +/// +/// @subsection UserManualActionsMatchesOptional Optional Match +/// +/// An optional match is similar to a parameterized match, but the last +/// parameter is optional. +/// +/// If the definition is +/// +/// @code +/// { url: { match: "/hello/:name?", constraint: { name: "/[a-z]+/" } } +/// @endcode +/// +/// then the URL @LIT{/hello} and @LIT{/hello/emil} will match. +/// +/// If the definitions are +/// +/// @code +/// { url: { match: "/hello/world" } } +/// { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } } +/// { url: { match: "/hello/*" } } +/// @endcode +/// +/// then the URL @LIT{/hello/world} will be matched by the first route, because it +/// is the most specific. The URL @LIT{/hello/you} will be matched by the second +/// route, because it is more specific than the prefix match. +/// +/// @subsection UserManualActionsMatchesMethod Method Restriction +/// +/// You can restrict the match to specific methods. +/// +/// If the definition is +/// +/// @code +/// { url: { match: "/hello/world", methods: [ "post", "put" ] } +/// @endcode +/// +/// then only @LIT{POST} and @LIT{PUT} requests will match. +/// +/// @subsection UserManualActionsMatching More on Matching +/// +/// Remember that the more specific match wins. +/// +/// - A match without parameter or wildcard is more specific than a match with +/// parameters or wildcard. +/// - A match with parameter is more specific than a match with a wildcard. +/// - If there is more than one parameter, specificity is applied from left to +/// right. +/// +/// Consider the following definitions +/// +/// @code +/// (1) { url: { match: "/hello/world" } } +/// (2) { url: { match: "/hello/:name", constraint: { name: "/[a-z]+/" } } +/// (3) { url: { match: "/:something/world" } +/// (4) { url: { match: "/hello/*" } } +/// @endcode +/// +/// Then +/// +/// - @LIT{/hello/world} is match by (1) +/// - @LIT{/hello/emil} is match by (2) +/// - @LIT{/your/world} is match by (3) +/// - @LIT{/hello/you} is match by (4) +/// +/// You can write the following document into the @LIT{_routing} collection +/// to test the above examples. +/// +/// @code +/// { +/// routes: [ +/// { url: { match: "/hello/world" }, content: "route 1" }, +/// { url: { match: "/hello/:name|:id", constraint: { name: "/[a-z]+/", id: "/[0-9]+/" } }, content: "route 2" }, +/// { url: { match: "/:something/world" }, content: "route 3" }, +/// { url: { match: "/hello/*" }, content: "route 4" }, +/// ] +/// } +/// @endcode +/// /// @section UserManualActionsHelloJson A Hello World Example for JSON ////////////////////////////////////////////////////////////////////// /// @@ -478,8 +676,8 @@ /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/hello/json", -/// ........> callback: { +/// ........> url: "/hello/json", +/// ........> content: { /// ........> contentType: "application/json", /// ........> body: "{ \"hello\" : \"world\" }" }}); /// arangosh> require("internal").reloadRouting() @@ -500,12 +698,163 @@ /// { "hello" : "world" } /// @endcode /// -/// @section UserManualActionsEcho A Dynamic Example -//////////////////////////////////////////////////// +/// @section UserManualActionsContent Delivering Content +//////////////////////////////////////////////////////// /// -/// The above examples deliver static content, which is fine for an example. -/// But the real power of actions lies in dynamic actions which use JavaScript -/// to construct the result. +/// There are a lot of different ways on how to deliver content. We have already +/// seen the simplest one, where static content is delivered. The fun, however, +/// starts when delivering dynamic content. +/// +/// @subsection UserManualActionsContentStatic Static Content +/// +/// You can specify a body and a content-type. +/// +/// @code +/// { content: { +/// contentType: "text/html", +/// body: "Hallo World" +/// } +/// } +/// @endcode +/// +/// If the content type is @LIT{text/plain} then you can use the short-cut +/// +/// @code +/// { content: "Hallo World" } +/// @endcode +/// +/// @subsection UserManualActionsContentAction A Simple Action +/// +/// The simplest dynamic action is: +/// +/// @code +/// { action: "org/arangodb/actions/echoRequest" } +/// @endcode +/// +/// It is not possible to store functions directly in the routing table, but you +/// can call functions defined in modules. In the above example the function can +/// be accessed from JavaScript as: +/// +/// @LIT{require("org/arangodb/actions").echoRequest} +/// +/// The function @LIT{echoRequest} is pre-defined. It takes the request objects +/// and echos it in the response. +/// +/// The signature of such a function must be +/// +/// @code +/// function (req, res, next, options) +/// @endcode +/// +/// For example +/// +/// @code +/// arangosh> db._routing.save({ +/// ........> url: "/hello/echo", +/// ........> action: "org/arangodb/actions/echoRequest" }); +/// @endcode +/// +/// Reload the routing and check +/// +/// @LIT{http://127.0.0.1:8529/hello/echo} +/// +/// You should see something like +/// +/// @code +/// { +/// "request": { +/// "path": "/hello/echo", +/// "headers": { +/// "accept-encoding": "gzip, deflate", +/// "accept-language": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3", +/// "connection": "keep-alive", +/// "content-length": "0", +/// "host": "localhost:8529", +/// "user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0" +/// }, +/// "requestType": "GET", +/// "parameters": { } +/// }, +/// "options": { } +/// } +/// @endcode +/// +/// The request might contain @LIT{path}, @LIT{prefix}, @LIT{suffix}, and +/// @LIT{urlParameters} attributes. @LIT{path} is the complete path as supplied +/// by the user and always available. If a prefix was matched, then this prefix +/// is stored in the attribute @LIT{prefix} and the remaining URL parts are +/// stored as an array in @LIT{suffix}. If one or more parameters were matched, +/// then the parameter values are stored in @LIT{urlParameters}. +/// +/// For example, if the url description is +/// +/// @code +/// { url: { match: "/hello/:name/:action" } } +/// @endcode +/// +/// and you request the path @LIT{/hello/emil/jump}, then the request object +/// will contain the following attribute +/// +/// @code +/// urlParameters: { name: "emil", action: "jump" } } +/// @endcode +/// +/// @subsection UserManualActionsContentController Action Controller +/// +/// As an alternative to the simple action, you can use controllers. A +/// controller is a module, defines the function @LIT{get}, @LIT{put}, +/// @LIT{post}, @LIT{delete}, @LIT{head}, @LIT{patch}. If a request of +/// the corresponding type is matched, the function will be called. +/// +/// For example +/// +/// @code +/// arangosh> db._routing.save({ +/// ........> url: "/hello/echo", +/// ........> action: { controller: "org/arangodb/actions/echoController" } }); +/// @endcode +/// +/// @subsection UserManualActionsContentPrefix Prefix Action Controller +/// +/// The controller is selected when the definition is read. There is a +/// more flexible, but slower and maybe insecure variant, the prefix +/// controller. +/// +/// Assume that the url is a prefix match +/// +/// @code +/// { url: { match: /hello/*" } } +/// @endcode +/// +/// You can use +/// +/// @code +/// { prefixController: "org/arangodb/actions" } +/// @endcode +/// +/// to define a prefix controller. If the URL @LIT{/hello/echoController} is +/// given, then the module @LIT{org/arangodb/actions/echoController} is used. +/// +/// If you use an prefix controller, you should make certain that no unwanted +/// actions are available under the prefix. +/// +/// @subsection UserManualActionsContentMethod Method Restriction +/// +/// You can restrict the match to specific methods. +/// +/// If the definition is +/// +/// @code +/// { controller: "org/arangodb/actions/echoController", methods: [ "post", "put" ] } +/// @endcode +/// +/// then only @LIT{POST} and @LIT{PUT} requests will match. +/// +/// @section UserManualActionsReqRes Requests and Responses +/////////////////////////////////////////////////////////// +/// +/// The controller must define handler functions which take a request object and +/// fill the response object. /// /// A very simple example is the function @LIT{echoRequest} defined in /// the module @LIT{org/arangodb/actions}. @@ -522,21 +871,12 @@ /// } /// @endcode /// -/// That functions accepts a request and returns this request as JSON object. -/// -/// It is not possible to store functions directly in the routing table, but you -/// can call functions defined in modules. In the above example the function can -/// be accessed from JavaScript as: -/// -/// @LIT{require("org/arangodb/actions").echoRequest} -/// -/// You can use it in the routing collection by specifying the name -/// @LIT{"org/arangodb/actions/echoRequest"}. +/// Install it as /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/hello/echo", -/// ........> callback: "org/arangodb/actions/echoRequest" }); +/// ........> url: "/echo", +/// ........> action: "org/arangodb/actions/echoRequest" }); /// @endcode /// /// Reload the routing and check @@ -569,30 +909,30 @@ /// } /// @endcode /// -/// Please note that +/// Note that /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/hello/echo", -/// ........> callback: "org/arangodb/actions/echoRequest" }); +/// ........> url: "/echo", +/// ........> action: "org/arangodb/actions/echoRequest" }); /// @endcode /// /// is a short-cut for /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/hello/echo-long", -/// ........> callback: { -/// ........> for: "org/arangodb/actions", -/// ........> do: "echoRequest" }}); +/// ........> url: "/echo", +/// ........> action: { do: "org/arangodb/actions/echoRequest" } }); /// @endcode /// -/// The verbose form allows you to pass options to the called function: +/// The latter form allows you to pass options to the called function: /// /// @code -/// arangosh> a = db._routing.firstExample({path: "/hello/echo-long"}); -/// arangosh> a.callback.options = { option: "my option1" }; -/// arangosh> db._replace(a, a); +/// arangosh> db._routing.save({ +/// ........> url: "/echo", +/// ........> action: { +/// ........> do: "org/arangodb/actions/echoRequest", +/// ........> options: { "Hallo": "World" } } }); /// @endcode /// /// You should now see the options in the result. @@ -609,91 +949,61 @@ /// } /// @endcode /// -/// @section UserManualActionsDYO Define Your Own Callback -////////////////////////////////////////////////////////// -/// -/// You can define your own callbacks by adding a new module to ArangoDB. In -/// order to avoid name clashes modules should be named -/// -/// @LIT{tld/domain/modulename} -/// -/// where @LIT{domain.tld} is your domain name. For development you can store -/// your code in files in the filesystem, see @ref MODULES_PATH and -/// @ref MODULES. -/// -/// However, when you are finished with the development, you can rollout the -/// module code by storing it inside the @LIT{_modules} collection. -/// -/// Create a file @LIT{hello-world.js} with the following content: -/// -/// @code -/// var actions = require("org/arangodb/actions"); -/// -/// exports.helloWorld = function (req, res) { -/// res.contentType = "text/html"; -/// res.responseCode = actions.HTTP_OK; -/// res.body = "Hello World!"; -/// }; -/// @endcode -/// -/// Load this file as new module @LIT{de/celler/hello-world} into the database -/// -/// @code -/// arangosh> require("internal").defineModule("de/celler/hello-world", "hello-world.js"); -/// @endcode -/// -/// Define a corresponding routing -/// -/// @code -/// arangosh> db._routing.save({ -/// ........> path: "/my/echo", -/// ........> callback: "de/celler/hello-world/helloWorld" }); -/// arangosh> require("internal").reloadRouting() -/// @endcode -/// -/// and check it -/// -/// @LIT{http://localhost:8529/my/echo} -/// /// @section UserManualActionsAdvanced Advanced Usages ////////////////////////////////////////////////////// /// /// For detailed information see the reference manual. /// -/// @subsection UserManualActionsAdvancedPrefix Using Prefixes -////////////////////////////////////////////////////////////// +/// @subsection UserManualActionsAdvancedRedirects Redirects /// -/// All the above definitions require an exact match. If you set the -/// @LIT{prefix} attribute to @LIT{true}, additional paths are ignored and the -/// URL also results in a match. +/// Use the following for a permanent redirect: /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/hello", -/// ........> prefix: true, -/// ........> callback: "org/arangodb/actions/echoRequest" }); +/// ........> url: "/", +/// ........> action: { +/// ........> do: "org/arangodb/actions/redirectRequest", +/// ........> options: { +/// ........> permanently: true, +/// ........> destination: "http://somewhere.else/" } } }); /// @endcode /// -/// Now try +/// @subsection UserManualActionsAdvancedBundles Routing Bundles /// -/// @LIT{http://localhost:8529/hello/this/is/ignored/but/available/in/path} -/// -/// The complete path is available in the @LIT{path} attribute, while the -/// matched prefix is available in the @LIT{prefix} attribute. +/// Instead of adding all routes for package separately, you can +/// specify a bundle. /// /// @code /// { -/// "request": { -/// "prefix": "/hello", -/// "path": "/hello/this/is/ignored/but/available/in/path", -/// ... -/// }, -/// "options": { } +/// routes: [ +/// { url: "/url1", content: "..." }, +/// { url: "/url2", content: "..." }, +/// { url: "/url3", content: "..." }, +/// ... +/// ] /// } /// @endcode /// +/// The advantage is, that you can put all your routes into one document +/// and use a common prefix. +/// +/// @code +/// { +/// urlPrefix: "/test", +/// +/// routes: [ +/// { url: "/url1", content: "..." }, +/// { url: "/url2", content: "..." }, +/// { url: "/url3", content: "..." }, +/// ... +/// ] +/// } +/// @endcode +/// +/// will define the URL @LIT{/test/url1}, @LIT{/test/url2}, and +/// @LIT{/test/url3}. +/// /// @subsection UserManualActionsAdvancedMiddleware Writing Middleware -////////////////////////////////////////////////////////////////////// /// /// Assume, you want to log every request. In this case you can easily define /// an action for the whole url-space @LIT{/}. This action simply logs @@ -713,23 +1023,10 @@ /// /// @code /// arangosh> db._routing.save({ -/// ........> path: "/", -/// ........> topdown: true, -/// ........> prefix: true, -/// ........> callback: "org/arangodb/actions/logRequest" }); -/// @endcode -/// -/// @subsection UserManualActionsAdvancedeRedirect Redirects -//////////////////////////////////////////////////////////// -/// -/// Use the following for a permanent redirect: -/// -/// @code -/// arangosh> db._routing.save({ -/// ........> path: "/", -/// ........> topdown: true, -/// ........> prefix: true, -/// ........> callback: { redirect: "http://somewhere.else.org/hallo" }); +/// ........> middleware: [ +/// ........> { url: { match: "/*" }, action: "org/arangodb/actions/logRequest" } +/// ........> ] +/// ........> }); /// @endcode //////////////////////////////////////////////////////////////////////////////// @@ -740,5 +1037,5 @@ // Local Variables: // mode: c++ // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}\\)" +// outline-regexp: "\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @section\\|/// @subsection\\|/// @\\}\\)" // End: diff --git a/config/config.guess b/config/config.guess index d622a44e55..de8e6dd2c4 100755 --- a/config/config.guess +++ b/config/config.guess @@ -138,6 +138,16 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +case "${UNAME_MACHINE}" in + i?86) + test -z "$VENDOR" && VENDOR=pc + ;; + *) + test -z "$VENDOR" && VENDOR=unknown + ;; +esac +test -f /etc/SuSE-release -o -f /.buildenv && VENDOR=suse + # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in @@ -202,19 +212,19 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + echo ${UNAME_MACHINE_ARCH}-${VENDOR}-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + echo ${UNAME_MACHINE}-${VENDOR}-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + echo ${UNAME_MACHINE}-${VENDOR}-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} + echo powerpc-${VENDOR}-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + echo ${UNAME_MACHINE}-${VENDOR}-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in @@ -282,13 +292,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 + echo m68k-${VENDOR}-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos + echo ${UNAME_MACHINE}-${VENDOR}-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos + echo ${UNAME_MACHINE}-${VENDOR}-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition @@ -303,7 +313,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) - echo arm-unknown-riscos + echo arm-${VENDOR}-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp @@ -411,7 +421,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} + echo m68k-${VENDOR}-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} @@ -722,9 +732,9 @@ EOF exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + echo ${UNAME_MACHINE}-${VENDOR}-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + echo ${UNAME_MACHINE}-${VENDOR}-osf1 fi exit ;; parisc*:Lites*:*:*) @@ -784,18 +794,18 @@ EOF echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} + echo sparc-${VENDOR}-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + echo ${UNAME_MACHINE}-${VENDOR}-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + echo x86_64-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + echo ${UNAME_PROCESSOR}-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) @@ -820,10 +830,10 @@ EOF echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} + echo x86_64-${VENDOR}-interix${UNAME_RELEASE} exit ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} + echo ia64-${VENDOR}-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) @@ -842,31 +852,31 @@ EOF echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin + echo x86_64-${VENDOR}-cygwin exit ;; p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin + echo powerpcle-${VENDOR}-cygwin exit ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + echo powerpcle-${VENDOR}-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-${VENDOR}-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + echo ${UNAME_MACHINE}-${VENDOR}-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in @@ -880,26 +890,26 @@ EOF esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-gnueabi + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnueabi else - echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnueabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu @@ -908,10 +918,10 @@ EOF echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; i*86:Linux:*:*) LIBC=gnu @@ -925,13 +935,13 @@ EOF echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build @@ -950,54 +960,54 @@ EOF #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + test x"${CPU}" != x && { echo "${CPU}-${VENDOR}-linux-gnu"; exit; } ;; or32:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-gnu + echo sparc-${VENDOR}-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + echo hppa64-${VENDOR}-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; + PA7*) echo hppa1.1-${VENDOR}-linux-gnu ;; + PA8*) echo hppa2.0-${VENDOR}-linux-gnu ;; + *) echo hppa-${VENDOR}-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu + echo powerpc64-${VENDOR}-linux-gnu exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu + echo powerpc-${VENDOR}-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1019,16 +1029,16 @@ EOF echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop + echo ${UNAME_MACHINE}-${VENDOR}-stop exit ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos + echo ${UNAME_MACHINE}-${VENDOR}-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} + echo i386-${VENODR}-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp @@ -1048,7 +1058,7 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + echo ${UNAME_MACHINE}-${VENDOR}-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then @@ -1087,7 +1097,7 @@ EOF if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + echo i860-${VENODR}-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) @@ -1124,19 +1134,19 @@ EOF /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} + echo m68k-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} + echo sparc-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} + echo rs6000-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} + echo powerpc-${VENDOR}-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} @@ -1186,7 +1196,7 @@ EOF if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else - echo mips-unknown-sysv${UNAME_RELEASE} + echo mips-${VENDOR}-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. @@ -1280,13 +1290,13 @@ EOF else UNAME_MACHINE="$cputype" fi - echo ${UNAME_MACHINE}-unknown-plan9 + echo ${UNAME_MACHINE}-${VENDOR}-plan9 exit ;; *:TOPS-10:*:*) - echo pdp10-unknown-tops10 + echo pdp10-${VENDOR}-tops10 exit ;; *:TENEX:*:*) - echo pdp10-unknown-tenex + echo pdp10-${VENDOR}-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 @@ -1295,16 +1305,16 @@ EOF echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) - echo pdp10-unknown-tops20 + echo pdp10-${VENDOR}-tops20 exit ;; *:ITS:*:*) - echo pdp10-unknown-its + echo pdp10-${VENDOR}-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + echo ${UNAME_MACHINE}-${VENDOR}-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` @@ -1326,7 +1336,7 @@ EOF echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx + echo ${UNAME_MACHINE}-${VENDOR}-esx exit ;; esac diff --git a/config/config.sub b/config/config.sub index c894da4550..59bb593f10 100755 --- a/config/config.sub +++ b/config/config.sub @@ -4,7 +4,7 @@ # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. -timestamp='2012-02-10' +timestamp='2012-04-18' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -225,6 +225,12 @@ case $os in -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; -lynx*) os=-lynxos ;; diff --git a/configure b/configure index 553c9332b9..bab9094a7c 100755 --- a/configure +++ b/configure @@ -3168,12 +3168,6 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } -mkdir_p="$MKDIR_P" -case $mkdir_p in - [\\/$]* | ?:[\\/]*) ;; - */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; -esac - for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. @@ -3306,6 +3300,7 @@ AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} +mkdir_p="$MKDIR_P" # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used @@ -9208,7 +9203,7 @@ if test "x$tr_MRUBY" = "xyes"; then MRUBY_LDFLAGS="" MRUBY_LIBS="${srcdir}/3rdParty/mruby/lib/libmruby.a" - TRI_MRUBY_VERSION="2012-07-02 (ac5838ac67d489c5321ac9cb0e8d6e50c6c4fd6f)" + TRI_MRUBY_VERSION="2012-09-20 (15cf8fdea4a6598aa470e698e8cbc9b9b492319d)" cat >>confdefs.h <<_ACEOF diff --git a/js/actions/system/api-system.js b/js/actions/system/api-system.js index c1d7141998..68d964900c 100644 --- a/js/actions/system/api-system.js +++ b/js/actions/system/api-system.js @@ -44,36 +44,31 @@ var actions = require("org/arangodb/actions"); //////////////////////////////////////////////////////////////////////////////// function Routing (req, res) { - var callbacks; - var current; - var i; + var execute; var next; - callbacks = actions.routing(req.requestType, req.suffix); - current = 0; + action = actions.firstRouting(req.requestType, req.suffix); - next = function () { - var callback; - - if (callbacks.length <= current) { - actions.resultNotImplemented(req, res, - "unknown path '" + req.suffix.join("/") + "'"); + execute = function () { + if (action.route === undefined) { + actions.resultNotImplemented(req, res, "unknown path '" + req.suffix.join("/") + "'"); return; } - callback = callbacks[current++]; + req.path = action.route.path; + req.prefix = action.prefix; + req.suffix = action.suffix; + req.urlParameters = action.urlParameters; - if (callback == null) { - actions.resultNotImplemented(req, res, - "not implemented '" + req.suffix.join("/") + "'"); - } - else { - req.prefix = callback.path; - callback.func(req, res, next, callback.options); - } + action.route.callback.controller(req, res, next, action.route.callback.options); } - next(); + next = function () { + action = actions.nextRouting(action); + execute(); + } + + execute(); } actions.defineHttp({ @@ -101,7 +96,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/reloadRouting", + url : "_admin/routing/reload", context : "admin", prefix : false, callback : function (req, res) { @@ -114,6 +109,19 @@ actions.defineHttp({ /// @brief returns system status information for the server //////////////////////////////////////////////////////////////////////////////// +actions.defineHttp({ + url : "_admin/routing/routes", + context : "admin", + prefix : false, + callback : function (req, res) { + actions.resultOk(req, res, actions.HTTP_OK, actions.routingCache()); + } +}); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns system status information for the server +//////////////////////////////////////////////////////////////////////////////// + function AdminRedirect (req, res) { var dest = "/_admin/html/index.html"; diff --git a/js/client/client.js b/js/client/client.js index a27441a1ee..ed33d15211 100644 --- a/js/client/client.js +++ b/js/client/client.js @@ -407,10 +407,24 @@ function help () { internal.reloadRouting = function () { if (typeof arango !== 'undefined') { - arango.POST("/_admin/reloadRouting", ""); + arango.POST("/_admin/routing/reload", ""); } }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief rebuilds the routing cache +//////////////////////////////////////////////////////////////////////////////// + + internal.routingCache = function () { + var result; + + if (typeof arango !== 'undefined') { + result = arango.GET("/_admin/routing/routes", ""); + } + + return result; + }; + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/js/client/js-client.h b/js/client/js-client.h index d3e102fe0e..ef227f09a5 100644 --- a/js/client/js-client.h +++ b/js/client/js-client.h @@ -408,11 +408,25 @@ static string JS_client_client = "\n" " internal.reloadRouting = function () {\n" " if (typeof arango !== 'undefined') {\n" - " arango.POST(\"/_admin/reloadRouting\", \"\");\n" + " arango.POST(\"/_admin/routing/reload\", \"\");\n" " }\n" " };\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief rebuilds the routing cache\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + " internal.routingCache = function () {\n" + " var result;\n" + "\n" + " if (typeof arango !== 'undefined') {\n" + " result = arango.GET(\"/_admin/routing/routes\", \"\");\n" + " }\n" + "\n" + " return result;\n" + " };\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" "/// @}\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" diff --git a/js/server/modules/org/arangodb/actions.js b/js/server/modules/org/arangodb/actions.js index 45aaa0774b..c97dfa0039 100644 --- a/js/server/modules/org/arangodb/actions.js +++ b/js/server/modules/org/arangodb/actions.js @@ -1,3 +1,12 @@ +/*jslint indent: 2, + nomen: true, + maxlen: 100, + sloppy: true, + vars: true, + white: true, + plusplus: true */ +/*global require, exports */ + //////////////////////////////////////////////////////////////////////////////// /// @brief JavaScript actions modules /// @@ -41,7 +50,7 @@ var console = require("console"); /// @brief routing cache //////////////////////////////////////////////////////////////////////////////// -var RoutingCache = {}; +var RoutingCache; //////////////////////////////////////////////////////////////////////////////// /// @} @@ -57,143 +66,515 @@ var RoutingCache = {}; //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a callback for a string +/// @brief splits an URL into parts //////////////////////////////////////////////////////////////////////////////// -function LookupCallbackString (callback) { - var components; - var module; - var fn; +function splitUrl (url) { + var cleaned; + var i; + var parts; + var re1; + var re2; + var cut; - if (callback == "") { - return undefined; + re1 = /^(:[a-z]+)(\|:[a-z]+)*$/; + re2 = /^(:[a-z]+)\?$/; + + parts = url.split("/"); + cleaned = []; + + cut = function (x) { + return x.substr(1); + }; + + for (i = 0; i < parts.length; ++i) { + var part = parts[i]; + + if (part !== "" && part !== ".") { + if (part === "..") { + cleaned.pop(); + } + else if (part === "*") { + cleaned.push({ prefix: true }); + } + else if (re1.test(part)) { + var ors = part.split("|").map(cut); + + cleaned.push({ parameters: ors }); + } + else if (re2.test(part)) { + var ors = [ part.substr(1, part.length - 2) ]; + + cleaned.push({ parameters: ors, optional: true }); + } + else { + cleaned.push(part); + } + } } - components = callback.split("/"); - fn = components[components.length - 1]; - - components.pop(); - module = components.join("/"); - - try { - module = require(module); - } - catch (err) { - console.error("cannot load callback '%s' from module '%s': %s", - fn, module, "" + err); - return undefined; - } - - if (fn in module) { - return module[fn]; - } - - console.error("callback '%s' is not defined in module '%s'", fn, module); - return undefined; + return cleaned; } //////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a callback for a function +/// @brief looks up an URL //////////////////////////////////////////////////////////////////////////////// -function LookupCallbackFunction (callback) { - var module; - var fn; - - try { - module = require(callback.module); - } - catch (err) { - console.error("cannot load callback '%s' from module '%s': %s", - fn, callback.module, "" + err); - return undefined; +function lookupUrl (prefix, url) { + if (url === undefined) { + return null; } - fn = callback.function; - - if (fn in module) { - return module[fn]; + if (typeof url === 'string') { + return { + urlParts: splitUrl(prefix + "/" + url), + methods: [ exports.GET, exports.HEAD ], + constraint: {} + }; } - console.error("callback '%s' is not defined in module '%s'", fn, module); - return undefined; + if (url.hasOwnProperty('match')) { + return { + urlParts: splitUrl(prefix + "/" + url.match), + methods: url.methods || exports.ALL_METHODS, + constraint: url.constraint || {} + }; + } + + return null; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a callback for static data //////////////////////////////////////////////////////////////////////////////// -function LookupCallbackStatic (callback) { +function lookupCallbackStatic (content) { var type; var body; + var methods; + var options; - type = callback.contentType || "text/plain"; - body = callback.body || ""; + if (typeof content === 'string') { + type = "text/plain"; + body = content; + methods = [ exports.GET, exports.HEAD ]; + options = {}; + } + else { + type = content.contentType || "text/plain"; + body = content.body || ""; + methods = content.methods || [ exports.GET, exports.HEAD ]; + } - return function (req, res) { - res.responseCode = exports.HTTP_OK; - res.contentType = type; - res.body = body; + return { + controller: function (req, res, next, options) { + res.responseCode = exports.HTTP_OK; + res.contentType = type; + res.body = body; + }, + + options: options, + methods: methods }; } //////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a callback for a redirect +/// @brief looks up a callback for an action //////////////////////////////////////////////////////////////////////////////// -function LookupCallbackRedirect (callback) { - var redirect; +function lookupCallbackAction (action) { + if (typeof action === 'string') { + var path = action.split("/"); + var name = path.pop(); + var func = null; - redirect = callback.redirect; + try { + var module = require(path.join("/")); - if (redirect == "") { - console.error("empty redirect not allowed"); - return undefined; + if (module.hasOwnProperty(name)) { + func = module[name]; + } + else { + console.error("cannot find action named '%s' in module '%s'", name, path.join("/")); + } + } + catch (err) { + console.error("cannot find action named '%s' in module '%s': %s", name, path.join("/"), String(err)); + return null; + } + + if (func === null || typeof func !== 'function') { + return null; + } + + return { + controller: func, + options: {}, + methods: exports.ALL_METHODS + }; } - return function (req, res, next, options) { - var perm = true; + if (action.hasOwnProperty('do')) { + var path = action.do.split("/"); + var name = path.pop(); + var func = null; - if ('permanent' in options) { - perm = options.perm; + try { + var module = require(path.join("/")); + + if (module.hasOwnProperty(name)) { + func = module[name]; + } + else { + console.error("cannot find action named '%s' in module '%s'", name, path.join("/")); + } + } + catch (err) { + console.error("cannot find action named '%s' in module '%s': %s", name, path.join("/"), String(err)); + return null; } - if (perm) { - actions.resultPermanentRedirect(req, res, redirect); + if (func === null || typeof func !== 'function') { + return null; } - else { - actions.resultTemporaryRedirect(req, res, redirect); + + return { + controller: func, + options: action.options || {}, + methods: action.methods || exports.ALL_METHODS + }; + } + + if (action.hasOwnProperty('controller')) { + try { + var module = require(action.controller); + + return { + controller: function (req, res, next, options) { + if (req.requestType === exports.GET && module.hasOwnProperty('get')) { + return module['get'](req, res, next, options); + } + + if (req.requestType === exports.HEAD && module.hasOwnProperty('head')) { + return module['head'](req, res, next, options); + } + + if (req.requestType === exports.PUT && module.hasOwnProperty('put')) { + return module['put'](req, res, next, options); + } + + if (req.requestType === exports.POST && module.hasOwnProperty('post')) { + return module['post'](req, res, next, options); + } + + if (req.requestType === exports.DELETE && module.hasOwnProperty('delete')) { + return module['delete'](req, res, next, options); + } + + if (req.requestType === exports.PATCH && module.hasOwnProperty('patch')) { + return module['patch'](req, res, next, options); + } + + if (module.hasOwnProperty('do')) { + return module['do'](req, res, next, options); + } + + next(); + }, + options: action.options || {}, + methods: action.methods || exports.ALL_METHODS + }; } - }; + catch (err) { + console.error("cannot load action controller module '%s': %s", action.controller, String(err)); + return null; + } + } + + if (action.hasOwnProperty('prefixController')) { + var prefixController = action.prefixController; + + return { + controller: function (req, res, next, options) { + var module; + + try { + + if (req.hasOwnProperty('suffix')) { + module = require(prefixController + "/" + req.suffix.join("/")); + } + else { + module = require(prefixController); + } + } + catch (err) { + console.error("cannot load prefix controller: %s", String(err)); + next(); + return; + } + + if (req.requestType === exports.GET && module.hasOwnProperty('get')) { + return module['get'](req, res, next, options); + } + + if (req.requestType === exports.HEAD && module.hasOwnProperty('head')) { + return module['head'](req, res, next, options); + } + + if (req.requestType === exports.PUT && module.hasOwnProperty('put')) { + return module['put'](req, res, next, options); + } + + if (req.requestType === exports.POST && module.hasOwnProperty('post')) { + return module['post'](req, res, next, options); + } + + if (req.requestType === exports.DELETE && module.hasOwnProperty('delete')) { + return module['delete'](req, res, next, options); + } + + if (req.requestType === exports.PATCH && module.hasOwnProperty('patch')) { + return module['patch'](req, res, next, options); + } + + if (module.hasOwnProperty('do')) { + return module['do'](req, res, next, options); + } + + next(); + }, + options: action.options || {}, + methods: action.methods || exports.ALL_METHODS + }; + } + + return null; } //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a callback //////////////////////////////////////////////////////////////////////////////// -function LookupCallback (callback) { - var type; +function lookupCallback (route) { + var result = null; - if (typeof callback === 'object') { - if ('body' in callback) { - return LookupCallbackStatic(callback); + if (route.hasOwnProperty('content')) { + result = lookupCallbackStatic(route.content); + } + else if (route.hasOwnProperty('action')) { + result = lookupCallbackAction(route.action); + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief intersect methods +//////////////////////////////////////////////////////////////////////////////// + +function intersectMethods (a, b) { + var d = {}; + var i; + var j; + var results = []; + + a = a || exports.ALL_METHODS; + b = b || exports.ALL_METHODS; + + for (i = 0; i < b.length; i++) { + d[b[i].toUpperCase()] = true; + } + + for (var j = 0; j < a.length; j++) { + var name = a[j].toUpperCase(); + + if (d[name]) { + results.push(name); } - else if ('for' in callback && 'do' in callback) { - return LookupCallbackFunction(callback); + } + + return results; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief defines a new route part +//////////////////////////////////////////////////////////////////////////////// + +function defineRoutePart (route, subwhere, parts, pos, constraint, callback) { + var i; + var p; + var part; + var subsub; + var ok; + + part = parts[pos]; + + if (typeof part === "string") { + if (! subwhere.hasOwnProperty('exact')) { + subwhere.exact = {}; } - else if ('redirect' in callback) { - return LookupCallbackRedirect(callback); + + if (! subwhere.exact.hasOwnProperty(part)) { + subwhere.exact[part] = {}; + } + + if (pos + 1< parts.length) { + defineRoutePart(route, subwhere.exact[part], parts, pos + 1, constraint, callback); } else { - console.error("unknown callback type '%s'", JSON.stringify(callback)); - return undefined; + subwhere.exact[part].route = route; + subwhere.exact[part].callback = callback; } } - else if (typeof callback === "string") { - return LookupCallbackString(callback); + else if (part.hasOwnProperty('parameters')) { + if (! subwhere.hasOwnProperty('parameters')) { + subwhere.parameters = []; + } + + ok = true; + + if (part.optional) { + if (pos + 1 < parts.length) { + console.error("cannot define optional parameter within url, ignoring route"); + ok = false; + } + else if (part.parameters.length !== 1) { + console.error("cannot define multiple optional parameters, ignoring route"); + ok = false; + } + } + + if (ok) { + for (i = 0; i < part.parameters.length; ++i) { + p = part.parameters[i]; + subsub = { parameter: p, match: {}}; + subwhere.parameters.push(subsub); + + if (constraint.hasOwnProperty(p)) { + subsub.constraint = constraint[p]; + } + + subsub.optional = part.optional || false; + + if (pos + 1 < parts.length) { + defineRoutePart(route, subsub.match, parts, pos + 1, constraint, callback); + } + else { + subsub.match.route = route; + subsub.match.callback = callback; + } + } + } + } + else if (part.hasOwnProperty('prefix')) { + if (! subwhere.hasOwnProperty('prefix')) { + subwhere.prefix = {}; + } + + if (pos + 1 < parts.length) { + console.error("cannot define prefix match within url, ignoring route"); + } + else { + subwhere.prefix.route = route; + subwhere.prefix.callback = callback; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief defines a new route +//////////////////////////////////////////////////////////////////////////////// + +function defineRoute (route, where, url, callback) { + var methods; + var branch; + var i; + var j; + + methods = intersectMethods(url.methods, callback.methods); + + for (j = 0; j < methods.length; ++j) { + var method = methods[j]; + + defineRoutePart(route, where[method], url.urlParts, 0, url.constraint, callback); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the routing for a path and a a method +//////////////////////////////////////////////////////////////////////////////// + +function flattenRouting (routes, path, urlParameters, depth, prefix) { + var cur; + var i; + var k; + var result = []; + + if (routes.hasOwnProperty('exact')) { + for (k in routes.exact) { + if (routes.exact.hasOwnProperty(k)) { + cur = path + "/" + k.replace(/([\.\+\*\?\^\$\(\)\[\]])/g, "\\$1"); + result = result.concat(flattenRouting(routes.exact[k], cur, urlParameters.shallowCopy, depth + 1, false)); + } + } } - return undefined; + if (routes.hasOwnProperty('parameters')) { + for (i = 0; i < routes.parameters.length; ++i) { + var parameter = routes.parameters[i]; + var newUrlParameters = urlParameters.shallowCopy; + var match; + + if (parameter.hasOwnProperty('constraint')) { + var constraint = parameter.constraint; + + if (/\/.*\//.test(constraint)) { + match = "/" + constraint.substr(1, constraint.length - 2); + } + else { + match = "/" + constraint; + } + } + else { + match = "/[^/]+"; + } + + if (parameter.optional) { + cur = path + "(" + match + ")?"; + } + else { + cur = path + match; + } + + newUrlParameters[parameter.parameter] = depth; + + result = result.concat(flattenRouting(parameter.match, cur, newUrlParameters, depth + 1, false)); + } + } + + if (routes.hasOwnProperty('callback')) { + result = result.concat([{ + path: path, + regexp: new RegExp("^" + path + "$"), + prefix: prefix, + depth: depth, + urlParameters: urlParameters, + callback: routes.callback, + route: routes.route + }]); + } + + if (routes.hasOwnProperty('prefix')) { + if (! routes.prefix.hasOwnProperty('callback')) { + console.error("prefix match must end in '/*'"); + } + else { + cur = path + "(/[^/]+)*"; + result = result.concat(flattenRouting(routes.prefix, cur, urlParameters.shallowCopy, depth + 1, true)); + } + } + + return result; } //////////////////////////////////////////////////////////////////////////////// @@ -276,13 +657,14 @@ function LookupCallback (callback) { /// - @c "string" //////////////////////////////////////////////////////////////////////////////// -function DefineHttp (options) { +function defineHttp (options) { var url = options.url; var contexts = options.context; var callback = options.callback; var parameters = options.parameters; var prefix = true; var userContext = false; + var i; if (! contexts) { contexts = "user"; @@ -296,7 +678,9 @@ function DefineHttp (options) { contexts = [ contexts ]; } else { - for (var i = 0; i < contexts.length && ! userContext; ++i) { + for (i = 0; i < contexts.length && ! userContext; ++i) { + var context = contexts[i]; + if (context === "user") { userContext = true; } @@ -338,8 +722,10 @@ function DefineHttp (options) { /// Returns the error message for an error code. //////////////////////////////////////////////////////////////////////////////// -function GetErrorMessage (code) { - for (var key in internal.errors) { +function getErrorMessage (code) { + var key; + + for (key in internal.errors) { if (internal.errors.hasOwnProperty(key)) { if (internal.errors[key].code === code) { return internal.errors[key].message; @@ -354,14 +740,14 @@ function GetErrorMessage (code) { /// @brief extracts the body as json //////////////////////////////////////////////////////////////////////////////// -function GetJsonBody (req, res, code) { +function getJsonBody (req, res, code) { var body; try { body = JSON.parse(req.requestBody || "{}") || {}; } catch (err) { - ResultBad(req, res, exports.ERROR_HTTP_CORRUPTED_JSON, err); + resultBad(req, res, exports.ERROR_HTTP_CORRUPTED_JSON, err); return undefined; } @@ -370,7 +756,7 @@ function GetJsonBody (req, res, code) { code = exports.ERROR_HTTP_CORRUPTED_JSON; } - ResultBad(req, res, code, err); + resultBad(req, res, code, err); return undefined; } @@ -390,7 +776,7 @@ function GetJsonBody (req, res, code) { /// into the result. //////////////////////////////////////////////////////////////////////////////// -function ResultError (req, res, httpReturnCode, errorNum, errorMessage, headers, keyvals) { +function resultError (req, res, httpReturnCode, errorNum, errorMessage, headers, keyvals) { res.responseCode = httpReturnCode; res.contentType = "application/json; charset=utf-8"; @@ -427,251 +813,203 @@ function ResultError (req, res, httpReturnCode, errorNum, errorMessage, headers, /// @brief flushes cache and reload routing information //////////////////////////////////////////////////////////////////////////////// -function ReloadRouting () { - var all = [ exports.DELETE, exports.GET, exports.HEAD, exports.POST, exports.PUT, exports.PATCH ]; - var callbacks; +function reloadRouting () { var i; - var method; var routes; - var routing = internal.db._collection("_routing"); - var routings; + var routing; + var handleRoute; + var handleMiddleware; + + // ............................................................................. + // clear the routing cache + // ............................................................................. RoutingCache = {}; - for (i = 0; i < all.length; ++i) { - method = all[i]; - RoutingCache[method] = {}; + RoutingCache.routes = {}; + RoutingCache.middleware = {}; + + for (i = 0; i < exports.ALL_METHODS.length; ++i) { + method = exports.ALL_METHODS[i]; + + RoutingCache.routes[method] = {}; + RoutingCache.middleware[method] = {}; } + // ............................................................................. + // lookup all routes + // ............................................................................. + + routing = internal.db._collection("_routing"); + if (routing === null) { - return; + routes = { hasNext: function() { return false; } }; + } + else { + routes = routing.all(); } - routes = routing.all(); + // ............................................................................. + // defines a new route + // ............................................................................. + + handleRoute = function (storage, urlPrefix, modulePrefix, route) { + var url; + + url = lookupUrl(urlPrefix, route.url); + + if (url === null) { + console.error("route '%s' has an unkown url, ignoring", JSON.stringify(route)); + return; + } + + callback = lookupCallback(route); + + if (callback === null) { + console.error("route '%s' has an unknown callback, ignoring", JSON.stringify(route)); + return; + } + + defineRoute(route, storage, url, callback); + } + + // ............................................................................. + // loop over the routes or routes bundle + // ............................................................................. while (routes.hasNext()) { var route = routes.next(); - var callback; - var methods = [ exports.GET, exports.HEAD ]; - var topdown = false; - var path; - var prefix = false; - var prio = 1.0; - var tmp; + if (route.hasOwnProperty('routes') || route.hasOwnProperty('middleware')) { + var urlPrefix = route.urlPrefix || ""; + var modulePrefix = route.modulePrefix || ""; - // extract path - if (! ('path' in route)) { - console.error("route %s is missing the 'path' attribute", route._id); - continue; - } + if (route.hasOwnProperty('routes')) { + var r = route.routes; - path = route.path; - - // extract callback - if (! ('callback' in route)) { - console.error("route %s is missing the 'callback' attribute", route._id); - continue; - } - - callback = route.callback; - - if ( !(callback instanceof Array)) { - callback = [ callback ]; - } - - tmp = []; - - for (i = 0; i < callback.length; ++i) { - var cb = LookupCallback(callback[i]); - - if (cb === undefined) { - console.error("route '%s' contains invalid callback '%s'", - route._id, JSON.stringify(callback[i])); - } - else if (typeof cb !== "function") { - console.error("route '%s' contains non-function callback '%s'", - route._id, JSON.stringify(callback[i])); - } - else { - var result = { - func: cb, - options: callback[i].options || {} - }; - - tmp.push(result); - } - } - - callback = tmp; - - // extract method - if ('method' in route) { - methods = route.method; - - if (typeof methods === 'string') { - if (methods === "all" || methods === "*") { - methods = all; + for (i = 0; i < r.length; ++i) { + handleRoute(RoutingCache.routes, urlPrefix, modulePrefix, r[i]); } - else { - methods = [ methods ]; + } + + if (route.hasOwnProperty('middleware')) { + var r = route.middleware; + + for (i = 0; i < r.length; ++i) { + handleRoute(RoutingCache.middleware, urlPrefix, modulePrefix, r[i]); } } } - - // extract priority - if ('priority' in route) { - prio = route.priority; + else { + handleRoute(RoutingCache.routes, "", "", route); } + } - // extract topdown flag - if ('topdown' in route) { - topdown = route.topdown; - } + // ............................................................................. + // compute the flat routes + // ............................................................................. - // extract prefix flag - if ('prefix' in route) { - prefix = route.prefix; - } + RoutingCache.flat = {}; - for (i = 0; i < methods.length; ++i) { - method = methods[i].toUpperCase(); - routings = RoutingCache[method]; + for (i = 0; i < exports.ALL_METHODS.length; ++i) { + var a; + var b; - if (routings === undefined) { - console.error("unknown method '%s' in route %s", method, route._id); - continue; - } + method = exports.ALL_METHODS[i]; - if (path in routings) { - callbacks = routings[path]; - } - else { - callbacks = routings[path] = [] - } + a = flattenRouting(RoutingCache.routes[method], "", {}, 0, false); + b = flattenRouting(RoutingCache.middleware[method], "", {}, 0, false).reverse(); - callbacks.push({ - route : route._id, - path : path, - topdown : topdown, - priority : prio, - prefix : prefix, - callback : callback - }); - } + RoutingCache.flat[method] = b.concat(a); } } //////////////////////////////////////////////////////////////////////////////// -/// @brief extratcs the routing for a path and a a method +/// @brief finds the first routing //////////////////////////////////////////////////////////////////////////////// -function Routing (method, path) { - var bottomup; - var components; - var current; - var i; - var j; - var l; - var result; - var routings; - var topdown; +function firstRouting (type, parts) { + var url = parts; - routings = RoutingCache[method]; + if (typeof url === 'string') { + parts = url.split("/"); - if (routings === undefined) { - return []; + if (parts[0] === '') { + parts.shift(); + } + } + else { + url = "/" + parts.join("/"); } - topdown = []; - bottomup = []; + if (! RoutingCache.flat.hasOwnProperty(type)) { + return { + routing: undefined, + parts: parts, + position: -1, + url: url, + route: undefined, + prefix: undefined, + suffix: undefined, + urlParameters: {} + }; + } - current = "/"; - components = [""].concat(path); + return nextRouting({ + routing: RoutingCache.flat[type], + parts: parts, + position: -1, + url: url, + route: undefined, + prefix: undefined, + suffix: undefined, + urlParameters: {} + }); +} - l = components.length; +//////////////////////////////////////////////////////////////////////////////// +/// @brief finds the next routing +//////////////////////////////////////////////////////////////////////////////// - for (i = 0; i < l; ++i) { - var full = (i + 1) == l; +function nextRouting (state) { + var i; + var k; - if (1 < i) { - current += "/"; - } + for (i = state.position + 1; i < state.routing.length; ++i) { + var route = state.routing[i]; - current += components[i]; + if (route.regexp.test(state.url)) { + state.position = i; - if (current in routings) { - var callbacks = routings[current]; + state.route = route; - for (j = 0; j < callbacks.length; ++j) { - var callback = callbacks[j]; + if (route.prefix) { + state.prefix = "/" + state.parts.slice(0, route.depth - 1).join("/"); + state.suffix = state.parts.slice(route.depth - 1, state.parts.length); + } + else { + state.prefix = undefined; + state.suffix = undefined; + } - if (callback.prefix || full) { - if (callback.topdown) { - topdown.push(callback); - } - else { - bottomup.push(callback); - } + state.urlParameters = {}; + + if (route.urlParameters) { + for (k in route.urlParameters) { + state.urlParameters[k] = state.parts[route.urlParameters[k]]; } } + + return state; } } - var sortTD = function (a, b) { - var la = a.path.length; - var lb = b.path.length; + state.route = undefined; + state.prefix = undefined; + state.suffix = undefined; + state.urlParameters = {}; - if (la == lb) { - return a.priority - b.priority; - } - - return la - lb; - } - - var sortBU = function (a, b) { - var la = a.path.length; - var lb = b.path.length; - - if (la == lb) { - return b.priority - a.priority; - } - - return lb - la; - } - - topdown.sort(sortTD); - bottomup.sort(sortBU); - - result = []; - - for (i = 0; i < topdown.length; ++i) { - var td = topdown[i]; - var callback = td.callback; - - for (j = 0; j < callback.length; ++j) { - result.push({ - func: callback[j].func, - options: callback[j].options, - path: td.path - }); - } - } - - for (i = 0; i < bottomup.length; ++i) { - var bu = bottomup[i]; - var callback = bu.callback; - - for (j = 0; j < callback.length; ++j) { - result.push({ - func: callback[j].func, - options: callback[j].options, - path: bu.path - }); - } - } - - return result; + return state; } //////////////////////////////////////////////////////////////////////////////// @@ -699,7 +1037,7 @@ function Routing (method, path) { /// and @LIT{code} with value @FA{code} to the @FA{result}. //////////////////////////////////////////////////////////////////////////////// -function ResultOk (req, res, httpReturnCode, result, headers) { +function resultOk (req, res, httpReturnCode, result, headers) { res.responseCode = httpReturnCode; res.contentType = "application/json; charset=utf-8"; @@ -726,15 +1064,15 @@ function ResultOk (req, res, httpReturnCode, result, headers) { /// The functions generates an error response. //////////////////////////////////////////////////////////////////////////////// -function ResultBad (req, res, code, msg, headers) { +function resultBad (req, res, code, msg, headers) { if (msg === undefined || msg === null) { - msg = GetErrorMessage(code); + msg = getErrorMessage(code); } else { msg = "" + msg; } - ResultError(req, res, exports.HTTP_BAD, code, msg, headers); + resultError(req, res, exports.HTTP_BAD, code, msg, headers); } //////////////////////////////////////////////////////////////////////////////// @@ -745,8 +1083,8 @@ function ResultBad (req, res, code, msg, headers) { /// The functions generates an error response. //////////////////////////////////////////////////////////////////////////////// -function ResultNotFound (req, res, msg, headers) { - ResultError(req, res, exports.HTTP_NOT_FOUND, exports.ERROR_HTTP_NOT_FOUND, "" + msg, headers); +function resultNotFound (req, res, msg, headers) { + resultError(req, res, exports.HTTP_NOT_FOUND, exports.ERROR_HTTP_NOT_FOUND, "" + msg, headers); } //////////////////////////////////////////////////////////////////////////////// @@ -757,8 +1095,8 @@ function ResultNotFound (req, res, msg, headers) { /// The functions generates an error response. //////////////////////////////////////////////////////////////////////////////// -function ResultNotImplemented (req, res, msg, headers) { - ResultError(req, res, exports.HTTP_NOT_IMPLEMENTED, exports.ERROR_NOT_IMPLEMENTED, "" + msg, headers); +function resultNotImplemented (req, res, msg, headers) { + resultError(req, res, exports.HTTP_NOT_IMPLEMENTED, exports.ERROR_NOT_IMPLEMENTED, "" + msg, headers); } //////////////////////////////////////////////////////////////////////////////// @@ -769,8 +1107,8 @@ function ResultNotImplemented (req, res, msg, headers) { /// The functions generates an error response. //////////////////////////////////////////////////////////////////////////////// -function ResultUnsupported (req, res, headers) { - ResultError(req, res, +function resultUnsupported (req, res, headers) { + resultError(req, res, exports.HTTP_METHOD_NOT_ALLOWED, exports.ERROR_HTTP_METHOD_NOT_ALLOWED, "Unsupported method", headers); @@ -784,8 +1122,8 @@ function ResultUnsupported (req, res, headers) { /// The functions generates an error response. //////////////////////////////////////////////////////////////////////////////// -function ResultPermanentRedirect (req, res, destination, headers) { - res.responseCode = actions.HTTP_MOVED_PERMANENTLY; +function resultPermanentRedirect (req, res, destination, headers) { + res.responseCode = exports.HTTP_MOVED_PERMANENTLY; res.contentType = "text/html"; res.body = "Moved

Moved

This page has moved to