From 3b952e34fa247d919a7946533ec0456d9f3ea5fb Mon Sep 17 00:00:00 2001 From: Sean McMurray Date: Mon, 6 Apr 2015 20:36:18 -0700 Subject: [PATCH 1/3] Allow regex in routes --- source/vibe/http/router.d | 67 ++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/source/vibe/http/router.d b/source/vibe/http/router.d index 9d94ebbce7..523b5f7202 100644 --- a/source/vibe/http/router.d +++ b/source/vibe/http/router.d @@ -366,41 +366,42 @@ private struct Route { string pattern; HTTPServerRequestDelegate cb; + this(HTTPMethod method, string url_match, HTTPServerRequestDelegate cb){ + /*** + * Convert the url_match into a regex before storing to minimize compiles. + * The regex should preserve variable names that start with ":". + * The regex will be terminated by any "*" within the string, + * so if the "*" was intended as a regex "*", + * use the regex version of this method instead. + ***/ + this.method = method; + this.cb = cb; + replaceAllInto!(p => "(P<"~p[1]~">(?!/).*)")(this.pattern, "^" ~ replaceFirst(url_match, regex("\*.*"), ""), regex(":((?!/).+)")); + this.pattern = regex(this.pattern); + } + + this(HTTPMethod method, Regex url_match, HTTPServerRequestDelegate cb){ + this.method = method; + this.cb = cb; + this.pattern = url_match; + } + bool matches(string url, ref string[string] params) const { - size_t i, j; - - // store parameters until a full match is confirmed - import std.typecons; - Tuple!(string, string)[maxRouteParameters] tmpparams; - size_t tmppparams_length = 0; - - for (i = 0, j = 0; i < url.length && j < pattern.length;) { - if (pattern[j] == '*') { - foreach (t; tmpparams[0 .. tmppparams_length]) - params[t[0]] = t[1]; - return true; - } - if (url[i] == pattern[j]) { - i++; - j++; - } else if(pattern[j] == ':') { - j++; - string name = skipPathNode(pattern, j); - string match = skipPathNode(url, i); - assert(tmppparams_length < maxRouteParameters, "Maximum number of route parameters exceeded."); - tmpparams[tmppparams_length++] = tuple(name, match); - } else return false; - } - - if ((j < pattern.length && pattern[j] == '*') || (i == url.length && j == pattern.length)) { - foreach (t; tmpparams[0 .. tmppparams_length]) - params[t[0]] = t[1]; - return true; - } - - return false; - } + /*** + * Runs the regex stored in this.pattern against the url. + * If it matches, populate params with any captures returned by the regex, + * including named captures. + ***/ + Captures cap = url.matchFirst(this.pattern); + if (!cap.length) return false; + assert(cap.length < maxRouteParameters, "Maximum number of route parameters exceeded."); + foreach(n; this.pattern.namedCaptures) + params[n] = cap[n]; + for(i=1; i Date: Mon, 6 Apr 2015 20:48:05 -0700 Subject: [PATCH 2/3] Accomodate \ in regex. --- source/vibe/http/router.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/vibe/http/router.d b/source/vibe/http/router.d index 523b5f7202..8ffe06d896 100644 --- a/source/vibe/http/router.d +++ b/source/vibe/http/router.d @@ -376,7 +376,7 @@ private struct Route { ***/ this.method = method; this.cb = cb; - replaceAllInto!(p => "(P<"~p[1]~">(?!/).*)")(this.pattern, "^" ~ replaceFirst(url_match, regex("\*.*"), ""), regex(":((?!/).+)")); + replaceAllInto!(p => "(P<"~p[1]~">(?!/).*)")(this.pattern, "^" ~ replaceFirst(url_match, regex(r"\*.*"), ""), regex(":((?!/).+)")); this.pattern = regex(this.pattern); } From 80d6cf90c204ecbfac724c1cb15207c0f80d8145 Mon Sep 17 00:00:00 2001 From: Sean McMurray Date: Mon, 6 Apr 2015 21:07:21 -0700 Subject: [PATCH 3/3] Learning std.regex --- source/vibe/http/router.d | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/source/vibe/http/router.d b/source/vibe/http/router.d index 8ffe06d896..169f1d1f18 100644 --- a/source/vibe/http/router.d +++ b/source/vibe/http/router.d @@ -362,8 +362,9 @@ unittest { private enum maxRouteParameters = 64; private struct Route { + import std.regex; HTTPMethod method; - string pattern; + Regex!char pattern; HTTPServerRequestDelegate cb; this(HTTPMethod method, string url_match, HTTPServerRequestDelegate cb){ @@ -376,11 +377,10 @@ private struct Route { ***/ this.method = method; this.cb = cb; - replaceAllInto!(p => "(P<"~p[1]~">(?!/).*)")(this.pattern, "^" ~ replaceFirst(url_match, regex(r"\*.*"), ""), regex(":((?!/).+)")); - this.pattern = regex(this.pattern); + this.pattern = regex(replaceAll!(p => "(P<"~p[1]~">(?!/).*)")("^" ~ replaceFirst(url_match, regex(r"\*.*"), ""), regex(":((?!/).+)"))); } - this(HTTPMethod method, Regex url_match, HTTPServerRequestDelegate cb){ + this(HTTPMethod method, Regex!char url_match, HTTPServerRequestDelegate cb){ this.method = method; this.cb = cb; this.pattern = url_match; @@ -393,13 +393,12 @@ private struct Route { * If it matches, populate params with any captures returned by the regex, * including named captures. ***/ - Captures cap = url.matchFirst(this.pattern); + Captures!(string) cap = url.matchFirst(this.pattern); if (!cap.length) return false; assert(cap.length < maxRouteParameters, "Maximum number of route parameters exceeded."); foreach(n; this.pattern.namedCaptures) params[n] = cap[n]; - for(i=1; i