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 @@
///
/// - @ref UserManualActionsIntro
/// - @ref UserManualActionsHelloWorld
+/// - @ref UserManualActionsMatches
+///
+/// - @ref UserManualActionsMatchesExact
+/// - @ref UserManualActionsMatchesPrefix
+/// - @ref UserManualActionsMatchesParameterized
+/// - @ref UserManualActionsMatchesConstraint
+/// - @ref UserManualActionsMatchesOptional
+/// - @ref UserManualActionsMatchesMethod
+/// - @ref UserManualActionsMatching
+///
+///
/// - @ref UserManualActionsHelloJson
-/// - @ref UserManualActionsEcho
-/// - @ref UserManualActionsDYO
-/// - @ref UserManualActionsAdvanced
+/// - @ref UserManualActionsContent
+///
+/// - @ref UserManualActionsContentStatic
+/// - @ref UserManualActionsContentAction
+/// - @ref UserManualActionsContentController
+/// - @ref UserManualActionsContentPrefix
+/// - @ref UserManualActionsContentMethod
+///
+///
+/// - @ref UserManualActionsReqRes
+/// - @ref UserManualActionsAdvanced
+///
+/// - @ref UserManualActionsAdvancedRedirects
+/// - @ref UserManualActionsAdvancedBundles
+/// - @ref UserManualActionsAdvancedMiddleware
+///
+//
///
///
///
@@ -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 = "MovedMoved
This page has moved to