2014-05-23 05:36:47 -04:00
|
|
|
#include "config.h"
|
2014-05-14 22:08:42 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
2014-05-23 03:48:26 -04:00
|
|
|
#include <ctype.h>
|
2014-05-14 22:08:42 -04:00
|
|
|
|
2014-05-15 01:39:50 -04:00
|
|
|
// PCRE
|
|
|
|
#include <pcre.h>
|
|
|
|
|
2014-05-16 08:22:25 -04:00
|
|
|
#include "r3.h"
|
2015-11-17 08:17:18 -05:00
|
|
|
#include "r3_slug.h"
|
2014-06-01 18:27:35 -04:00
|
|
|
#include "slug.h"
|
2015-11-17 08:17:18 -05:00
|
|
|
#include "str.h"
|
2015-11-17 08:24:36 -05:00
|
|
|
#include "r3_debug.h"
|
2014-05-20 13:50:15 -04:00
|
|
|
#include "zmalloc.h"
|
2014-05-15 01:39:50 -04:00
|
|
|
|
2015-11-10 07:41:52 -05:00
|
|
|
#ifdef __GNUC__
|
|
|
|
# define likely(x) __builtin_expect(!!(x), 1)
|
|
|
|
# define unlikely(x) __builtin_expect(!!(x), 0)
|
|
|
|
#else
|
|
|
|
# define likely(x) !!(x)
|
|
|
|
# define unlikely(x) !!(x)
|
|
|
|
#endif
|
2014-06-01 09:58:29 -04:00
|
|
|
|
|
|
|
#define CHECK_PTR(ptr) if (ptr == NULL) return NULL;
|
|
|
|
|
2014-05-14 22:08:42 -04:00
|
|
|
// String value as the index http://judy.sourceforge.net/doc/JudySL_3x.htm
|
2014-05-15 01:39:50 -04:00
|
|
|
|
2014-05-20 11:25:12 -04:00
|
|
|
|
|
|
|
static int strndiff(char * d1, char * d2, unsigned int n) {
|
|
|
|
char * o = d1;
|
2014-05-20 13:50:15 -04:00
|
|
|
while ( *d1 == *d2 && n-- > 0 ) {
|
2014-05-20 11:25:12 -04:00
|
|
|
d1++;
|
|
|
|
d2++;
|
|
|
|
}
|
|
|
|
return d1 - o;
|
|
|
|
}
|
|
|
|
|
2014-06-03 10:15:59 -04:00
|
|
|
/*
|
2014-05-20 11:25:12 -04:00
|
|
|
static int strdiff(char * d1, char * d2) {
|
|
|
|
char * o = d1;
|
2014-05-20 13:50:15 -04:00
|
|
|
while( *d1 == *d2 ) {
|
2014-05-20 11:25:12 -04:00
|
|
|
d1++;
|
|
|
|
d2++;
|
|
|
|
}
|
|
|
|
return d1 - o;
|
|
|
|
}
|
2014-06-03 10:15:59 -04:00
|
|
|
*/
|
2014-05-20 11:25:12 -04:00
|
|
|
|
|
|
|
|
2014-05-15 06:26:41 -04:00
|
|
|
/**
|
2014-05-16 03:29:25 -04:00
|
|
|
* Create a node object
|
2014-05-15 06:26:41 -04:00
|
|
|
*/
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * r3_tree_create(int cap) {
|
|
|
|
R3Node * n = (R3Node*) zmalloc( sizeof(R3Node) );
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(n);
|
2014-05-15 01:39:50 -04:00
|
|
|
|
2016-03-08 01:19:54 -05:00
|
|
|
n->edges = zmalloc(sizeof(R3Edge) * cap);
|
2014-06-04 04:22:28 -04:00
|
|
|
CHECK_PTR(n->edges);
|
2014-05-16 08:58:30 -04:00
|
|
|
n->edge_len = 0;
|
|
|
|
n->edge_cap = cap;
|
2014-05-20 04:16:36 -04:00
|
|
|
|
|
|
|
n->routes = NULL;
|
|
|
|
n->route_len = 0;
|
|
|
|
n->route_cap = 0;
|
|
|
|
|
2014-05-15 06:26:41 -04:00
|
|
|
n->endpoint = 0;
|
2016-03-08 01:19:54 -05:00
|
|
|
n->compare_type = NODE_COMPARE_STR;
|
2014-05-15 09:17:30 -04:00
|
|
|
n->combined_pattern = NULL;
|
2014-05-16 08:51:30 -04:00
|
|
|
n->pcre_pattern = NULL;
|
|
|
|
n->pcre_extra = NULL;
|
2014-06-13 12:55:24 -04:00
|
|
|
n->data = NULL;
|
2014-05-15 01:39:50 -04:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
void r3_tree_free(R3Node * tree) {
|
2015-11-10 07:29:07 -05:00
|
|
|
if (tree->edges) {
|
2016-03-08 01:19:54 -05:00
|
|
|
for (int j=0;j<tree->edge_len;j++) {
|
|
|
|
r3_edge_free(tree->edges[j]);
|
|
|
|
}
|
2015-11-10 07:29:07 -05:00
|
|
|
zfree(tree->edges);
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
2016-03-08 01:19:54 -05:00
|
|
|
if (tree->routes) {
|
|
|
|
for (int k=0;k<tree->route_len;k++) {
|
|
|
|
r3_route_free(tree->routes[k]);
|
|
|
|
}
|
|
|
|
zfree(tree->routes);
|
|
|
|
}
|
2014-05-21 06:16:48 -04:00
|
|
|
if (tree->pcre_pattern) {
|
|
|
|
pcre_free(tree->pcre_pattern);
|
|
|
|
}
|
2014-05-21 06:28:21 -04:00
|
|
|
#ifdef PCRE_STUDY_JIT_COMPILE
|
2014-05-21 06:21:15 -04:00
|
|
|
if (tree->pcre_extra) {
|
|
|
|
pcre_free_study(tree->pcre_extra);
|
2014-05-21 06:16:48 -04:00
|
|
|
}
|
2014-05-21 06:28:21 -04:00
|
|
|
#endif
|
2014-05-22 23:15:57 -04:00
|
|
|
zfree(tree->combined_pattern);
|
2014-05-21 06:12:14 -04:00
|
|
|
zfree(tree);
|
|
|
|
tree = NULL;
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
|
|
|
|
2014-06-01 20:19:44 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connect two node objects, and create an edge object between them.
|
|
|
|
*/
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * r3_node_connectl(R3Node * n, const char * pat, int len, int dupl, R3Node *child) {
|
2014-05-15 01:39:50 -04:00
|
|
|
// find the same sub-pattern, if it does not exist, create one
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * e;
|
2014-05-15 06:02:10 -04:00
|
|
|
|
2014-06-01 20:04:57 -04:00
|
|
|
e = r3_node_find_edge(n, pat, len);
|
2014-05-15 06:02:10 -04:00
|
|
|
if (e) {
|
2014-05-15 10:57:13 -04:00
|
|
|
return e;
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
|
|
|
|
2014-05-23 01:49:18 -04:00
|
|
|
if (dupl) {
|
|
|
|
pat = zstrndup(pat, len);
|
|
|
|
}
|
2014-06-01 21:48:00 -04:00
|
|
|
e = r3_edge_createl(pat, len, child);
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(e);
|
2016-03-08 01:19:54 -05:00
|
|
|
R3Edge * e2 = r3_node_append_edge(n, e);
|
2015-11-10 07:29:07 -05:00
|
|
|
return e2;
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * r3_node_append_edge(R3Node *n, R3Edge *e)
|
2015-11-10 07:29:07 -05:00
|
|
|
{
|
2014-05-20 03:14:12 -04:00
|
|
|
if (n->edges == NULL) {
|
2014-05-16 08:58:30 -04:00
|
|
|
n->edge_cap = 3;
|
2015-11-17 23:52:06 -05:00
|
|
|
n->edges = zmalloc(sizeof(R3Edge) * n->edge_cap);
|
2014-05-16 03:29:25 -04:00
|
|
|
}
|
2014-05-16 08:58:30 -04:00
|
|
|
if (n->edge_len >= n->edge_cap) {
|
|
|
|
n->edge_cap *= 2;
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * p = zrealloc(n->edges, sizeof(R3Edge) * n->edge_cap);
|
2014-05-20 03:32:22 -04:00
|
|
|
if(p) {
|
|
|
|
n->edges = p;
|
|
|
|
}
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
2015-11-10 07:29:07 -05:00
|
|
|
|
2016-03-08 01:19:54 -05:00
|
|
|
// Insert edge into edge array
|
|
|
|
n->edges[n->edge_len] = e;
|
|
|
|
return n->edges[n->edge_len++];
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
|
|
|
|
2014-06-01 20:04:57 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the existing edge with specified pattern (include slug)
|
2014-06-01 20:19:44 -04:00
|
|
|
*
|
|
|
|
* if "pat" is a slug, we should compare with the specified pattern.
|
2014-06-01 20:04:57 -04:00
|
|
|
*/
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * r3_node_find_edge(const R3Node * n, const char * pat, int pat_len) {
|
|
|
|
R3Edge * e;
|
2014-06-01 20:19:44 -04:00
|
|
|
int i;
|
|
|
|
for (i = 0 ; i < n->edge_len ; i++ ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i];
|
2014-06-01 20:04:57 -04:00
|
|
|
// there is a case: "{foo}" vs "{foo:xxx}",
|
|
|
|
// we should return the match result: full-match or partial-match
|
2015-11-10 07:31:36 -05:00
|
|
|
if (strcmp(e->pattern, pat) == 0) {
|
|
|
|
return e;
|
2014-05-15 01:39:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
int r3_tree_compile(R3Node *n, char **errstr)
|
2014-05-16 00:33:59 -04:00
|
|
|
{
|
2015-11-10 07:29:07 -05:00
|
|
|
int i;
|
2014-05-31 13:51:42 -04:00
|
|
|
int ret = 0;
|
2014-05-16 06:57:36 -04:00
|
|
|
bool use_slug = r3_node_has_slug_edges(n);
|
2014-05-16 02:05:51 -04:00
|
|
|
if ( use_slug ) {
|
2014-05-31 13:51:42 -04:00
|
|
|
if ( (ret = r3_tree_compile_patterns(n, errstr)) ) {
|
|
|
|
return ret;
|
|
|
|
}
|
2014-05-16 00:33:59 -04:00
|
|
|
} else {
|
|
|
|
// use normal text matching...
|
|
|
|
n->combined_pattern = NULL;
|
|
|
|
}
|
2014-05-16 02:05:51 -04:00
|
|
|
|
2015-11-10 07:29:07 -05:00
|
|
|
for (i = 0 ; i < n->edge_len ; i++ ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
if ((ret = r3_tree_compile(n->edges[i]->child, errstr))) {
|
2014-05-31 13:51:42 -04:00
|
|
|
return ret; // stop here if error occurs
|
|
|
|
}
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
2014-05-31 13:51:42 -04:00
|
|
|
return 0;
|
2014-05-16 00:33:59 -04:00
|
|
|
}
|
2014-05-15 09:17:30 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function combines ['/foo', '/bar', '/{slug}'] into (/foo)|(/bar)|/([^/]+)}
|
|
|
|
*
|
2014-06-02 04:24:32 -04:00
|
|
|
* Return -1 if error occurs
|
|
|
|
* Return 0 if success
|
2014-05-15 09:17:30 -04:00
|
|
|
*/
|
2015-11-17 23:52:06 -05:00
|
|
|
int r3_tree_compile_patterns(R3Node * n, char **errstr) {
|
|
|
|
R3Edge *e = NULL;
|
2014-05-15 09:17:30 -04:00
|
|
|
char * p;
|
2015-11-10 06:56:07 -05:00
|
|
|
char * cpat = zcalloc(sizeof(char) * 64 * 3); // XXX
|
2014-05-31 13:51:42 -04:00
|
|
|
if (!cpat) {
|
|
|
|
asprintf(errstr, "Can not allocate memory");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-15 09:17:30 -04:00
|
|
|
|
|
|
|
p = cpat;
|
2015-11-10 06:56:07 -05:00
|
|
|
int opcode_cnt = 0;
|
|
|
|
int i = 0;
|
|
|
|
for (; i < n->edge_len ; i++) {
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i];
|
2015-11-10 06:56:07 -05:00
|
|
|
if (e->opcode) {
|
2014-05-23 03:48:26 -04:00
|
|
|
opcode_cnt++;
|
2015-11-10 06:56:07 -05:00
|
|
|
}
|
2014-05-23 03:48:26 -04:00
|
|
|
|
2015-11-10 06:56:07 -05:00
|
|
|
if (e->has_slug) {
|
2014-05-23 00:08:06 -04:00
|
|
|
// compile "foo/{slug}" to "foo/[^/]+"
|
2014-06-03 08:50:19 -04:00
|
|
|
char * slug_pat = r3_slug_compile(e->pattern, e->pattern_len);
|
2014-05-16 00:33:59 -04:00
|
|
|
strcat(p, slug_pat);
|
2016-03-08 01:19:54 -05:00
|
|
|
zfree(slug_pat);
|
2014-05-16 00:33:59 -04:00
|
|
|
} else {
|
2014-06-01 13:05:57 -04:00
|
|
|
strncat(p,"^(", 2);
|
|
|
|
p += 2;
|
2014-05-15 09:17:30 -04:00
|
|
|
|
2014-05-16 00:33:59 -04:00
|
|
|
strncat(p, e->pattern, e->pattern_len);
|
|
|
|
p += e->pattern_len;
|
2014-05-15 09:17:30 -04:00
|
|
|
|
2014-05-16 00:33:59 -04:00
|
|
|
strncat(p++,")", 1);
|
|
|
|
}
|
2014-05-15 09:17:30 -04:00
|
|
|
|
2014-05-18 08:08:43 -04:00
|
|
|
if ( i + 1 < n->edge_len && n->edge_len > 1 ) {
|
2014-05-15 09:17:30 -04:00
|
|
|
strncat(p++,"|",1);
|
|
|
|
}
|
2014-05-15 08:38:07 -04:00
|
|
|
}
|
2014-05-16 08:51:30 -04:00
|
|
|
|
2014-05-18 08:08:43 -04:00
|
|
|
info("pattern: %s\n",cpat);
|
|
|
|
|
2014-05-23 00:08:06 -04:00
|
|
|
// if all edges use opcode, we should skip the combined_pattern.
|
|
|
|
if ( opcode_cnt == n->edge_len ) {
|
2014-05-23 03:48:26 -04:00
|
|
|
// zfree(cpat);
|
|
|
|
n->compare_type = NODE_COMPARE_OPCODE;
|
|
|
|
} else {
|
|
|
|
n->compare_type = NODE_COMPARE_PCRE;
|
2014-05-23 00:08:06 -04:00
|
|
|
}
|
2014-05-16 08:58:30 -04:00
|
|
|
|
2014-05-15 09:17:30 -04:00
|
|
|
n->combined_pattern = cpat;
|
2014-05-16 02:05:51 -04:00
|
|
|
|
2014-05-31 13:51:42 -04:00
|
|
|
const char *pcre_error;
|
|
|
|
int pcre_erroffset;
|
2014-05-17 21:05:55 -04:00
|
|
|
unsigned int option_bits = 0;
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2014-05-24 06:45:43 -04:00
|
|
|
n->ov_cnt = (1 + n->edge_len) * 3;
|
|
|
|
|
2014-05-21 06:20:48 -04:00
|
|
|
if (n->pcre_pattern) {
|
|
|
|
pcre_free(n->pcre_pattern);
|
2014-05-21 06:16:48 -04:00
|
|
|
}
|
2014-05-16 02:05:51 -04:00
|
|
|
n->pcre_pattern = pcre_compile(
|
|
|
|
n->combined_pattern, /* the pattern */
|
2014-05-17 21:05:55 -04:00
|
|
|
option_bits, /* default options */
|
2014-05-31 13:51:42 -04:00
|
|
|
&pcre_error, /* for error message */
|
|
|
|
&pcre_erroffset, /* for error offset */
|
2014-05-16 02:05:51 -04:00
|
|
|
NULL); /* use default character tables */
|
2014-05-16 03:29:25 -04:00
|
|
|
if (n->pcre_pattern == NULL) {
|
2014-05-31 13:51:42 -04:00
|
|
|
if (errstr) {
|
2014-06-01 08:15:25 -04:00
|
|
|
asprintf(errstr, "PCRE compilation failed at offset %d: %s, pattern: %s", pcre_erroffset, pcre_error, n->combined_pattern);
|
2014-05-31 13:51:42 -04:00
|
|
|
}
|
|
|
|
return -1;
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
2014-05-21 06:28:21 -04:00
|
|
|
#ifdef PCRE_STUDY_JIT_COMPILE
|
|
|
|
if (n->pcre_extra) {
|
|
|
|
pcre_free_study(n->pcre_extra);
|
|
|
|
}
|
2014-05-31 13:51:42 -04:00
|
|
|
n->pcre_extra = pcre_study(n->pcre_pattern, 0, &pcre_error);
|
2014-05-16 03:29:25 -04:00
|
|
|
if (n->pcre_extra == NULL) {
|
2014-05-31 13:51:42 -04:00
|
|
|
if (errstr) {
|
2014-06-01 08:15:25 -04:00
|
|
|
asprintf(errstr, "PCRE study failed at offset %s, pattern: %s", pcre_error, n->combined_pattern);
|
2014-05-31 13:51:42 -04:00
|
|
|
}
|
|
|
|
return -1;
|
2014-05-16 03:29:25 -04:00
|
|
|
}
|
2014-05-21 06:28:21 -04:00
|
|
|
#endif
|
2014-05-31 13:51:42 -04:00
|
|
|
return 0;
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-05-16 02:24:00 -04:00
|
|
|
|
2014-05-18 00:32:20 -04:00
|
|
|
|
2014-05-16 08:51:30 -04:00
|
|
|
/**
|
|
|
|
* This function matches the URL path and return the left node
|
|
|
|
*
|
2014-05-18 22:34:48 -04:00
|
|
|
* r3_tree_matchl returns NULL when the path does not match. returns *node when the path matches.
|
2014-05-16 08:51:30 -04:00
|
|
|
*
|
|
|
|
* @param node n the root of the tree
|
|
|
|
* @param char* path the URL path to dispatch
|
|
|
|
* @param int path_len the length of the URL path.
|
|
|
|
* @param match_entry* entry match_entry is used for saving the captured dynamic strings from pcre result.
|
|
|
|
*/
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * r3_tree_matchl(const R3Node * n, const char * path, int path_len, match_entry * entry) {
|
2014-05-18 08:08:43 -04:00
|
|
|
info("try matching: %s\n", path);
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge *e;
|
2015-11-10 06:29:18 -05:00
|
|
|
unsigned int i;
|
|
|
|
unsigned int restlen;
|
2014-05-24 05:13:55 -04:00
|
|
|
|
2014-06-04 11:03:58 -04:00
|
|
|
const char *pp;
|
|
|
|
const char *pp_end;
|
|
|
|
|
2014-05-23 04:17:35 -04:00
|
|
|
if (n->compare_type == NODE_COMPARE_OPCODE) {
|
2014-06-04 11:03:58 -04:00
|
|
|
pp_end = path + path_len;
|
2014-06-04 07:54:41 -04:00
|
|
|
|
2014-06-04 19:45:34 -04:00
|
|
|
for (i = n->edge_len; i--; ) {
|
2014-06-04 07:54:41 -04:00
|
|
|
pp = path;
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i];
|
2014-05-23 03:48:26 -04:00
|
|
|
switch(e->opcode) {
|
|
|
|
case OP_EXPECT_NOSLASH:
|
2014-05-23 10:58:20 -04:00
|
|
|
while (*pp != '/' && pp < pp_end) pp++;
|
|
|
|
break;
|
|
|
|
case OP_EXPECT_MORE_ALPHA:
|
|
|
|
while ( isalpha(*pp) && pp < pp_end) pp++;
|
2014-05-23 03:48:26 -04:00
|
|
|
break;
|
2014-05-23 10:58:20 -04:00
|
|
|
case OP_EXPECT_MORE_DIGITS:
|
|
|
|
while ( isdigit(*pp) && pp < pp_end) pp++;
|
2014-05-23 03:48:26 -04:00
|
|
|
break;
|
2014-05-23 10:58:20 -04:00
|
|
|
case OP_EXPECT_MORE_WORDS:
|
|
|
|
while ( (isdigit(*pp) || isalpha(*pp)) && pp < pp_end) pp++;
|
2014-05-23 03:48:26 -04:00
|
|
|
break;
|
|
|
|
case OP_EXPECT_NODASH:
|
2014-05-23 10:58:20 -04:00
|
|
|
while (*pp != '-' && pp < pp_end) pp++;
|
2014-05-23 03:48:26 -04:00
|
|
|
break;
|
|
|
|
}
|
2014-05-23 04:17:35 -04:00
|
|
|
// check match
|
2015-11-10 07:42:41 -05:00
|
|
|
if ((pp - path) > 0) {
|
2014-05-23 03:48:26 -04:00
|
|
|
if (entry) {
|
|
|
|
str_array_append(entry->vars , zstrndup(path, pp - path));
|
|
|
|
}
|
2015-11-10 06:48:22 -05:00
|
|
|
restlen = pp_end - pp;
|
2014-05-23 03:48:26 -04:00
|
|
|
if (restlen == 0) {
|
|
|
|
return e->child && e->child->endpoint > 0 ? e->child : NULL;
|
|
|
|
}
|
2015-11-10 06:48:22 -05:00
|
|
|
return r3_tree_matchl(e->child, pp, restlen, entry);
|
2014-05-23 03:48:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-16 08:51:30 -04:00
|
|
|
|
|
|
|
// if the pcre_pattern is found, and the pointer is not NULL, then it's
|
|
|
|
// pcre pattern node, we use pcre_exec to match the nodes
|
|
|
|
if (n->pcre_pattern) {
|
2014-06-04 11:03:58 -04:00
|
|
|
const char *substring_start = NULL;
|
2014-05-24 06:45:43 -04:00
|
|
|
int substring_length = 0;
|
|
|
|
int ov[ n->ov_cnt ];
|
2014-08-03 13:07:37 -04:00
|
|
|
int rc;
|
2014-05-24 06:45:43 -04:00
|
|
|
|
2014-05-18 08:08:43 -04:00
|
|
|
info("pcre matching %s on %s\n", n->combined_pattern, path);
|
2014-05-16 06:57:36 -04:00
|
|
|
|
2014-05-16 02:05:51 -04:00
|
|
|
rc = pcre_exec(
|
2014-05-22 23:47:10 -04:00
|
|
|
n->pcre_pattern, /* the compiled pattern */
|
2014-05-22 23:31:19 -04:00
|
|
|
n->pcre_extra,
|
2014-05-22 23:47:10 -04:00
|
|
|
path, /* the subject string */
|
|
|
|
path_len, /* the length of the subject */
|
|
|
|
0, /* start at offset 0 in the subject */
|
|
|
|
0, /* default options */
|
2014-05-22 23:42:19 -04:00
|
|
|
ov, /* output vector for substring information */
|
2014-05-24 06:45:43 -04:00
|
|
|
n->ov_cnt); /* number of elements in the output vector */
|
2014-05-16 02:05:51 -04:00
|
|
|
|
2014-05-23 11:00:49 -04:00
|
|
|
// does not match all edges, return NULL;
|
2014-05-16 02:05:51 -04:00
|
|
|
if (rc < 0) {
|
2014-05-23 11:00:49 -04:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf("pcre rc: %d\n", rc );
|
2014-05-16 02:05:51 -04:00
|
|
|
switch(rc)
|
|
|
|
{
|
2014-05-22 23:47:10 -04:00
|
|
|
case PCRE_ERROR_NOMATCH:
|
|
|
|
printf("pcre: no match '%s' on pattern '%s'\n", path, n->combined_pattern);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Handle other special cases if you like
|
|
|
|
default:
|
|
|
|
printf("pcre matching error '%d' '%s' on pattern '%s'\n", rc, path, n->combined_pattern);
|
|
|
|
break;
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
2014-05-23 11:00:49 -04:00
|
|
|
#endif
|
2014-05-16 02:05:51 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-18 08:08:43 -04:00
|
|
|
|
2014-06-04 19:43:51 -04:00
|
|
|
|
|
|
|
restlen = path_len - ov[1]; // if it's fully matched to the end (rest string length)
|
|
|
|
|
|
|
|
if (restlen == 0 ) {
|
|
|
|
// Check the substring to decide we should go deeper on which edge
|
|
|
|
for (i = 1; i < rc; i++)
|
|
|
|
{
|
|
|
|
substring_length = ov[2*i+1] - ov[2*i];
|
|
|
|
|
|
|
|
// if it's not matched for this edge, just skip them quickly
|
|
|
|
if (substring_length == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
substring_start = path + ov[2*i];
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i - 1];
|
2014-06-04 19:43:51 -04:00
|
|
|
|
|
|
|
if (entry && e->has_slug) {
|
|
|
|
// append captured token to entry
|
|
|
|
str_array_append(entry->vars , zstrndup(substring_start, substring_length));
|
|
|
|
}
|
|
|
|
|
|
|
|
// since restlen == 0 return the edge quickly.
|
|
|
|
return e->child && e->child->endpoint > 0 ? e->child : NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check the substring to decide we should go deeper on which edge
|
2014-05-16 02:05:51 -04:00
|
|
|
for (i = 1; i < rc; i++)
|
|
|
|
{
|
2014-05-23 04:17:35 -04:00
|
|
|
substring_length = ov[2*i+1] - ov[2*i];
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2014-06-04 19:43:51 -04:00
|
|
|
// if it's not matched for this edge, just skip them quickly
|
2014-06-04 11:03:58 -04:00
|
|
|
if ( substring_length == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2014-06-04 19:43:51 -04:00
|
|
|
substring_start = path + ov[2*i];
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i - 1];
|
2014-06-04 11:03:58 -04:00
|
|
|
|
|
|
|
if (entry && e->has_slug) {
|
|
|
|
// append captured token to entry
|
|
|
|
str_array_append(entry->vars , zstrndup(substring_start, substring_length));
|
|
|
|
}
|
2014-06-04 19:43:51 -04:00
|
|
|
|
2014-06-04 11:03:58 -04:00
|
|
|
// get the length of orginal string: $0
|
|
|
|
return r3_tree_matchl( e->child, path + (ov[1] - ov[0]), restlen, entry);
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
2014-05-16 02:24:00 -04:00
|
|
|
// does not match
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-05-16 02:05:51 -04:00
|
|
|
|
2015-11-10 07:42:41 -05:00
|
|
|
if ((e = r3_node_find_edge_str(n, path, path_len)) != NULL) {
|
2014-05-23 03:48:26 -04:00
|
|
|
restlen = path_len - e->pattern_len;
|
2014-05-20 12:37:27 -04:00
|
|
|
if (restlen == 0) {
|
2014-05-20 13:15:54 -04:00
|
|
|
return e->child && e->child->endpoint > 0 ? e->child : NULL;
|
2014-05-16 02:24:00 -04:00
|
|
|
}
|
2014-05-20 12:37:27 -04:00
|
|
|
return r3_tree_matchl(e->child, path + e->pattern_len, restlen, entry);
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-05-24 05:13:55 -04:00
|
|
|
|
|
|
|
|
2015-11-18 01:16:26 -05:00
|
|
|
R3Route * r3_tree_match_route(const R3Node *tree, match_entry * entry) {
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node *n;
|
2014-06-04 08:01:46 -04:00
|
|
|
int i;
|
2014-05-18 22:34:48 -04:00
|
|
|
n = r3_tree_match_entry(tree, entry);
|
2014-05-31 13:16:53 -04:00
|
|
|
if (n && n->routes && n->route_len > 0) {
|
2014-06-04 19:45:34 -04:00
|
|
|
for (i = n->route_len; i--; ) {
|
2014-05-18 22:39:03 -04:00
|
|
|
if ( r3_route_cmp(n->routes[i], entry) == 0 ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
// Add slugs from found route to match_entry
|
2016-03-08 04:51:42 -05:00
|
|
|
entry->vars->slugs = n->routes[i]->slugs;
|
2014-05-18 01:06:36 -04:00
|
|
|
return n->routes[i];
|
2014-05-18 00:56:53 -04:00
|
|
|
}
|
2014-05-18 00:49:58 -04:00
|
|
|
}
|
|
|
|
}
|
2014-05-18 00:56:53 -04:00
|
|
|
return NULL;
|
2014-05-18 00:49:58 -04:00
|
|
|
}
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
inline R3Edge * r3_node_find_edge_str(const R3Node * n, const char * str, int str_len) {
|
|
|
|
R3Edge * e;
|
2014-06-04 19:43:51 -04:00
|
|
|
unsigned int i;
|
2015-11-10 07:42:41 -05:00
|
|
|
char firstbyte = *str;
|
2014-06-04 19:43:51 -04:00
|
|
|
for (i = n->edge_len; i--; ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i];
|
2015-11-10 06:48:22 -05:00
|
|
|
if (firstbyte == e->pattern[0]) {
|
2015-11-10 07:42:41 -05:00
|
|
|
if (strncmp(e->pattern, str, e->pattern_len) == 0) {
|
2016-03-08 01:19:54 -05:00
|
|
|
return n->edges[i];
|
2014-05-23 01:49:18 -04:00
|
|
|
}
|
|
|
|
return NULL;
|
2014-05-16 02:05:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
2014-05-15 08:38:07 -04:00
|
|
|
}
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * r3_node_create() {
|
|
|
|
R3Node * n = (R3Node*) zmalloc( sizeof(R3Node) );
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(n);
|
2014-05-16 03:29:25 -04:00
|
|
|
n->edges = NULL;
|
2014-05-16 08:58:30 -04:00
|
|
|
n->edge_len = 0;
|
|
|
|
n->edge_cap = 0;
|
2014-05-18 00:24:07 -04:00
|
|
|
|
2014-05-18 01:06:36 -04:00
|
|
|
n->routes = NULL;
|
|
|
|
n->route_len = 0;
|
|
|
|
n->route_cap = 0;
|
2014-05-18 00:24:07 -04:00
|
|
|
|
2014-05-16 03:29:25 -04:00
|
|
|
n->endpoint = 0;
|
|
|
|
n->combined_pattern = NULL;
|
|
|
|
n->pcre_pattern = NULL;
|
2014-06-04 03:59:13 -04:00
|
|
|
n->pcre_extra = NULL;
|
2014-06-13 12:55:24 -04:00
|
|
|
n->data = NULL;
|
2014-05-16 03:29:25 -04:00
|
|
|
return n;
|
|
|
|
}
|
2014-05-15 06:26:41 -04:00
|
|
|
|
2015-11-18 01:16:26 -05:00
|
|
|
void r3_route_free(R3Route * route) {
|
2016-03-08 01:19:54 -05:00
|
|
|
assert(route);
|
|
|
|
for ( int i = 0; i < route->slugs_len ; i++ ) {
|
|
|
|
if (route->slugs[ i ]) {
|
|
|
|
zfree(route->slugs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
zfree(route->slugs);
|
2014-05-22 10:26:14 -04:00
|
|
|
zfree(route);
|
2014-05-18 00:24:07 -04:00
|
|
|
}
|
|
|
|
|
2016-03-08 01:19:54 -05:00
|
|
|
static bool router_slugs_full(const R3Route * route) {
|
|
|
|
return route->slugs_len >= route->slugs_cap;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool router_slugs_resize(R3Route * route, int new_cap) {
|
|
|
|
route->slugs = zrealloc(route->slugs, sizeof(char**) * new_cap);
|
|
|
|
route->slugs_cap = new_cap;
|
|
|
|
return route->slugs != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool router_append_slug(R3Route * route, char * slug) {
|
|
|
|
if ( router_slugs_full(route) ) {
|
|
|
|
bool ret = router_slugs_resize(route, route->slugs_cap + 20);
|
|
|
|
if (ret == false ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
route->slugs[ route->slugs_len++ ] = slug;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void get_slugs(const char * path, int path_len, R3Route * route) {
|
|
|
|
char *plh = path;
|
|
|
|
int l = 0;
|
|
|
|
int namel;
|
|
|
|
char *name;
|
|
|
|
while(1) {
|
|
|
|
plh = r3_slug_find_placeholder(plh+l,&l);
|
|
|
|
if (plh == NULL) break;
|
|
|
|
namel = 0;
|
|
|
|
name = r3_slug_find_name(plh,&namel);
|
|
|
|
router_append_slug(route,zstrndup(name,namel));
|
|
|
|
if ((plh + l) >= (path + path_len)) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-18 01:16:26 -05:00
|
|
|
R3Route * r3_route_createl(const char * path, int path_len) {
|
|
|
|
R3Route * info = zmalloc(sizeof(R3Route));
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(info);
|
2016-03-08 01:19:54 -05:00
|
|
|
info->slugs_cap = 3;
|
|
|
|
info->slugs_len = 0;
|
|
|
|
info->slugs = (char**) zmalloc( sizeof(char*) * info->slugs_cap);
|
|
|
|
get_slugs(path, path_len, info);
|
2014-05-26 13:07:33 -04:00
|
|
|
info->path = (char*) path;
|
2014-05-18 00:24:07 -04:00
|
|
|
info->path_len = path_len;
|
|
|
|
info->request_method = 0; // can be (GET || POST)
|
|
|
|
|
2014-05-18 00:40:06 -04:00
|
|
|
info->data = NULL;
|
|
|
|
|
2014-05-18 00:24:07 -04:00
|
|
|
info->host = NULL; // required host name
|
|
|
|
info->host_len = 0;
|
|
|
|
|
|
|
|
info->remote_addr_pattern = NULL;
|
|
|
|
info->remote_addr_pattern_len = 0;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2014-05-31 07:56:46 -04:00
|
|
|
|
2014-06-03 11:50:02 -04:00
|
|
|
/**
|
|
|
|
* Helper function for creating routes from request URI path and request method
|
|
|
|
*
|
|
|
|
* method (int): METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE ...
|
|
|
|
*/
|
2015-11-18 01:16:26 -05:00
|
|
|
R3Route * r3_tree_insert_routel_ex(R3Node *tree, int method, const char *path, int path_len, void *data, char **errstr) {
|
|
|
|
R3Route *r = r3_route_createl(path, path_len);
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(r);
|
2014-05-31 07:56:46 -04:00
|
|
|
r->request_method = method; // ALLOW GET OR POST METHOD
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * ret = r3_tree_insert_pathl_ex(tree, path, path_len, r, data, errstr);
|
2014-06-03 11:50:02 -04:00
|
|
|
if (!ret) {
|
|
|
|
// failed insert
|
|
|
|
r3_route_free(r);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-05-31 07:56:46 -04:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-06-01 21:09:41 -04:00
|
|
|
/**
|
|
|
|
* Find common prefix from the edges of the node.
|
|
|
|
*
|
|
|
|
* Some cases of the common prefix:
|
|
|
|
*
|
|
|
|
* 1. "/foo/{slug}" vs "/foo/bar" => common prefix = "/foo/"
|
|
|
|
* 2. "{slug}/hate" vs "{slug}/bar" => common prefix = "{slug}/"
|
|
|
|
* 2. "/z/{slug}/hate" vs "/z/{slog}/bar" => common prefix = "/z/"
|
|
|
|
* 3. "{slug:xxx}/hate" vs "{slug:yyy}/bar" => common prefix = ""
|
|
|
|
* 4. "aaa{slug:xxx}/hate" vs "aab{slug:yyy}/bar" => common prefix = "aa"
|
|
|
|
* 5. "/foo/{slug}/hate" vs "/fo{slug}/bar" => common prefix = "/fo"
|
|
|
|
*/
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * r3_node_find_common_prefix(R3Node *n, const char *path, int path_len, int *prefix_len, char **errstr) {
|
2014-06-01 21:09:41 -04:00
|
|
|
int i = 0;
|
|
|
|
int prefix = 0;
|
2014-06-02 04:08:46 -04:00
|
|
|
*prefix_len = 0;
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge *e = NULL;
|
2014-06-01 21:09:41 -04:00
|
|
|
for(i = 0 ; i < n->edge_len ; i++ ) {
|
|
|
|
// ignore all edges with slug
|
2016-03-08 01:19:54 -05:00
|
|
|
prefix = strndiff( (char*) path, n->edges[i]->pattern, n->edges[i]->pattern_len);
|
2014-06-01 21:09:41 -04:00
|
|
|
|
|
|
|
// no common, consider insert a new edge
|
|
|
|
if ( prefix > 0 ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i];
|
2014-06-01 21:09:41 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// found common prefix edge
|
|
|
|
if (prefix > 0) {
|
|
|
|
r3_slug_t *slug;
|
|
|
|
int ret = 0;
|
2014-06-03 10:21:40 -04:00
|
|
|
const char *offset = path;
|
|
|
|
const char *p = path + prefix;
|
2014-06-01 21:09:41 -04:00
|
|
|
|
|
|
|
slug = r3_slug_new(path, path_len);
|
|
|
|
|
|
|
|
do {
|
2014-06-02 03:52:40 -04:00
|
|
|
ret = r3_slug_parse(slug, path, path_len, offset, errstr);
|
2014-06-01 21:09:41 -04:00
|
|
|
// found slug
|
|
|
|
if (ret == 1) {
|
|
|
|
// inside slug, backtrace to the begin of the slug
|
|
|
|
if ( p >= slug->begin && p <= slug->end ) {
|
|
|
|
prefix = slug->begin - path - 1;
|
|
|
|
break;
|
|
|
|
} else if ( p < slug->begin ) {
|
|
|
|
break;
|
2014-06-01 21:48:00 -04:00
|
|
|
} else if ( p >= slug->end && p < (path + path_len) ) {
|
2014-06-03 10:20:54 -04:00
|
|
|
offset = slug->end + 1;
|
2014-06-01 21:48:00 -04:00
|
|
|
prefix = p - path;
|
2014-06-01 21:09:41 -04:00
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
2014-06-02 03:52:40 -04:00
|
|
|
} else if (ret == -1) {
|
2014-08-12 06:02:48 -04:00
|
|
|
r3_slug_free(slug);
|
2014-06-02 14:09:11 -04:00
|
|
|
return NULL;
|
2014-06-01 21:48:00 -04:00
|
|
|
} else {
|
|
|
|
break;
|
2014-06-01 21:09:41 -04:00
|
|
|
}
|
|
|
|
} while(ret == 1);
|
2014-08-12 06:02:48 -04:00
|
|
|
|
|
|
|
// free the slug
|
|
|
|
r3_slug_free(slug);
|
2014-06-01 21:09:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
*prefix_len = prefix;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-05-18 00:24:07 -04:00
|
|
|
/**
|
|
|
|
* Return the last inserted node.
|
|
|
|
*/
|
2015-11-18 01:16:26 -05:00
|
|
|
R3Node * r3_tree_insert_pathl_ex(R3Node *tree, const char *path, int path_len, R3Route * route, void * data, char **errstr)
|
2014-05-15 06:26:41 -04:00
|
|
|
{
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * n = tree;
|
2014-06-01 21:09:41 -04:00
|
|
|
|
|
|
|
// common edge
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * e = NULL;
|
2014-05-15 10:57:13 -04:00
|
|
|
|
2015-11-17 03:10:26 -05:00
|
|
|
// If there is no path to insert at the node, we just increase the mount
|
|
|
|
// point on the node and append the route.
|
|
|
|
if (path_len == 0) {
|
|
|
|
tree->endpoint++;
|
|
|
|
if (route) {
|
|
|
|
route->data = data;
|
|
|
|
r3_node_append_route(tree, route);
|
|
|
|
}
|
|
|
|
return tree;
|
|
|
|
}
|
2014-06-01 21:48:00 -04:00
|
|
|
|
2014-05-15 10:57:13 -04:00
|
|
|
/* length of common prefix */
|
2014-05-18 06:58:31 -04:00
|
|
|
int prefix_len = 0;
|
2014-06-04 01:45:28 -04:00
|
|
|
char *err = NULL;
|
2014-06-02 14:09:11 -04:00
|
|
|
e = r3_node_find_common_prefix(tree, path, path_len, &prefix_len, &err);
|
|
|
|
if (err) {
|
|
|
|
// copy the error message pointer
|
|
|
|
if (errstr) *errstr = err;
|
2014-06-02 03:52:40 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-05-15 10:57:13 -04:00
|
|
|
|
2014-06-01 02:33:56 -04:00
|
|
|
const char * subpath = path + prefix_len;
|
|
|
|
const int subpath_len = path_len - prefix_len;
|
|
|
|
|
2014-05-18 06:58:31 -04:00
|
|
|
// common prefix not found, insert a new edge for this pattern
|
|
|
|
if ( prefix_len == 0 ) {
|
|
|
|
// there are two more slugs, we should break them into several parts
|
2015-08-26 10:00:23 -04:00
|
|
|
int slug_cnt = r3_slug_count(path, path_len, errstr);
|
2014-06-02 03:47:02 -04:00
|
|
|
if (slug_cnt == -1) {
|
2014-06-01 18:38:50 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-01 08:51:47 -04:00
|
|
|
|
2014-05-23 01:49:18 -04:00
|
|
|
if ( slug_cnt > 1 ) {
|
2014-05-20 09:38:37 -04:00
|
|
|
int slug_len;
|
2014-06-03 08:50:19 -04:00
|
|
|
char *p = r3_slug_find_placeholder(path, &slug_len);
|
2014-05-18 00:28:12 -04:00
|
|
|
|
2014-05-18 06:58:31 -04:00
|
|
|
#ifdef DEBUG
|
|
|
|
assert(p);
|
|
|
|
#endif
|
|
|
|
|
2014-05-23 01:49:18 -04:00
|
|
|
// find the next one '{', then break there
|
2014-05-20 09:38:37 -04:00
|
|
|
if(p) {
|
2014-06-03 08:50:19 -04:00
|
|
|
p = r3_slug_find_placeholder(p + slug_len + 1, NULL);
|
2014-05-20 09:38:37 -04:00
|
|
|
}
|
2014-05-18 06:58:31 -04:00
|
|
|
#ifdef DEBUG
|
|
|
|
assert(p);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// insert the first one edge, and break at "p"
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * child = r3_tree_create(3);
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(child);
|
|
|
|
|
2016-03-08 01:19:54 -05:00
|
|
|
r3_node_connectl(n, path, p - path, 1, child); // duplicate
|
2014-05-18 06:58:31 -04:00
|
|
|
|
|
|
|
// and insert the rest part to the child
|
2014-06-02 02:47:46 -04:00
|
|
|
return r3_tree_insert_pathl_ex(child, p, path_len - (int)(p - path), route, data, errstr);
|
2014-05-23 01:49:18 -04:00
|
|
|
|
2014-05-18 06:58:31 -04:00
|
|
|
} else {
|
2014-05-23 01:49:18 -04:00
|
|
|
if (slug_cnt == 1) {
|
|
|
|
// there is one slug, let's see if it's optimiz-able by opcode
|
|
|
|
int slug_len = 0;
|
2014-06-03 08:50:19 -04:00
|
|
|
char *slug_p = r3_slug_find_placeholder(path, &slug_len);
|
2014-05-23 01:49:18 -04:00
|
|
|
int slug_pattern_len = 0;
|
2014-06-03 08:50:19 -04:00
|
|
|
char *slug_pattern = r3_slug_find_pattern(slug_p, &slug_pattern_len);
|
2014-06-01 20:04:57 -04:00
|
|
|
|
2014-05-23 01:49:18 -04:00
|
|
|
int opcode = 0;
|
|
|
|
// if there is a pattern defined.
|
2014-06-01 20:04:57 -04:00
|
|
|
if (slug_pattern_len) {
|
2014-06-03 08:50:19 -04:00
|
|
|
char *cpattern = r3_slug_compile(slug_pattern, slug_pattern_len);
|
2014-05-23 01:49:18 -04:00
|
|
|
opcode = r3_pattern_to_opcode(cpattern, strlen(cpattern));
|
|
|
|
zfree(cpattern);
|
|
|
|
} else {
|
|
|
|
opcode = OP_EXPECT_NOSLASH;
|
|
|
|
}
|
2014-06-01 20:04:57 -04:00
|
|
|
|
|
|
|
|
|
|
|
// if the slug starts after one+ charactor, for example foo{slug}
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node *c1;
|
2014-06-01 20:04:57 -04:00
|
|
|
if (slug_p > path) {
|
|
|
|
c1 = r3_tree_create(3);
|
|
|
|
CHECK_PTR(c1);
|
|
|
|
r3_node_connectl(n, path, slug_p - path, 1, c1); // duplicate
|
|
|
|
} else {
|
|
|
|
c1 = n;
|
|
|
|
}
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * c2 = r3_tree_create(3);
|
2014-06-01 20:04:57 -04:00
|
|
|
CHECK_PTR(c2);
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge * op_edge = r3_node_connectl(c1, slug_p, slug_len , 1, c2);
|
2014-06-01 20:04:57 -04:00
|
|
|
if(opcode) {
|
2014-05-23 01:49:18 -04:00
|
|
|
op_edge->opcode = opcode;
|
2014-06-01 20:04:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int restlen = path_len - ((slug_p - path) + slug_len);
|
2014-05-23 01:49:18 -04:00
|
|
|
|
2014-06-01 20:04:57 -04:00
|
|
|
if (restlen) {
|
2014-06-02 02:47:46 -04:00
|
|
|
return r3_tree_insert_pathl_ex(c2, slug_p + slug_len, restlen, route, data, errstr);
|
2014-05-23 01:49:18 -04:00
|
|
|
}
|
2014-06-01 20:04:57 -04:00
|
|
|
|
|
|
|
c2->data = data;
|
|
|
|
c2->endpoint++;
|
|
|
|
if (route) {
|
|
|
|
route->data = data;
|
|
|
|
r3_node_append_route(c2, route);
|
|
|
|
}
|
|
|
|
return c2;
|
2014-05-23 01:49:18 -04:00
|
|
|
}
|
|
|
|
// only one slug
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Node * child = r3_tree_create(3);
|
2014-06-01 09:58:29 -04:00
|
|
|
CHECK_PTR(child);
|
2014-05-18 06:58:31 -04:00
|
|
|
child->endpoint++;
|
2014-06-01 20:04:57 -04:00
|
|
|
if (data)
|
|
|
|
child->data = data;
|
|
|
|
|
|
|
|
r3_node_connectl(n, path, path_len, 1, child);
|
2014-05-18 06:58:31 -04:00
|
|
|
if (route) {
|
|
|
|
route->data = data;
|
|
|
|
r3_node_append_route(child, route);
|
|
|
|
}
|
|
|
|
return child;
|
2014-05-18 00:28:12 -04:00
|
|
|
}
|
2014-05-18 06:58:31 -04:00
|
|
|
} else if ( prefix_len == e->pattern_len ) { // fully-equal to the pattern of the edge
|
2014-05-15 11:03:23 -04:00
|
|
|
|
2014-05-15 10:57:13 -04:00
|
|
|
// there are something more we can insert
|
2014-05-18 00:24:07 -04:00
|
|
|
if ( subpath_len > 0 ) {
|
2014-06-02 02:47:46 -04:00
|
|
|
return r3_tree_insert_pathl_ex(e->child, subpath, subpath_len, route, data, errstr);
|
2014-05-15 10:57:13 -04:00
|
|
|
} else {
|
2014-05-18 22:49:47 -04:00
|
|
|
// there are no more path to insert
|
|
|
|
|
2014-06-12 05:50:47 -04:00
|
|
|
// see if there is an endpoint already, we should n't overwrite the data on child.
|
|
|
|
// but we still need to append the route.
|
|
|
|
|
2014-05-18 01:06:36 -04:00
|
|
|
if (route) {
|
|
|
|
route->data = data;
|
|
|
|
r3_node_append_route(e->child, route);
|
2014-06-12 05:50:47 -04:00
|
|
|
e->child->endpoint++; // make it as an endpoint
|
|
|
|
return e->child;
|
2014-05-18 00:28:12 -04:00
|
|
|
}
|
2014-06-12 05:50:47 -04:00
|
|
|
|
|
|
|
// insertion without route
|
|
|
|
if (e->child->endpoint > 0) {
|
|
|
|
// TODO: return an error code instead of NULL
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
e->child->endpoint++; // make it as an endpoint
|
|
|
|
e->child->data = data; // set data
|
2014-05-15 10:57:13 -04:00
|
|
|
return e->child;
|
|
|
|
}
|
|
|
|
|
2014-05-18 06:58:31 -04:00
|
|
|
} else if ( prefix_len < e->pattern_len ) {
|
2014-05-17 03:20:59 -04:00
|
|
|
/* it's partially matched with the pattern,
|
2014-05-15 10:57:13 -04:00
|
|
|
* we should split the end point and make a branch here...
|
|
|
|
*/
|
2014-05-18 06:58:31 -04:00
|
|
|
r3_edge_branch(e, prefix_len);
|
2014-06-02 02:47:46 -04:00
|
|
|
return r3_tree_insert_pathl_ex(e->child, subpath, subpath_len, route , data, errstr);
|
2014-05-15 10:57:13 -04:00
|
|
|
} else {
|
2014-06-01 19:03:32 -04:00
|
|
|
fprintf(stderr, "unexpected route.");
|
2014-05-15 10:57:13 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return n;
|
2014-05-15 06:26:41 -04:00
|
|
|
}
|
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
bool r3_node_has_slug_edges(const R3Node *n) {
|
2015-11-17 08:40:21 -05:00
|
|
|
bool found = false;
|
2015-11-17 23:52:06 -05:00
|
|
|
R3Edge *e;
|
2014-05-16 08:58:30 -04:00
|
|
|
for ( int i = 0 ; i < n->edge_len ; i++ ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
e = n->edges[i];
|
2014-06-01 18:27:35 -04:00
|
|
|
e->has_slug = r3_path_contains_slug_char(e->pattern);
|
2014-05-20 13:50:15 -04:00
|
|
|
if (e->has_slug)
|
2015-11-17 08:40:21 -05:00
|
|
|
found = true;
|
2014-05-16 00:33:59 -04:00
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2014-05-16 03:29:25 -04:00
|
|
|
|
2014-05-15 06:02:10 -04:00
|
|
|
|
2015-11-17 23:52:06 -05:00
|
|
|
void r3_tree_dump(const R3Node * n, int level) {
|
2014-05-18 08:08:43 -04:00
|
|
|
print_indent(level);
|
2014-05-20 13:15:54 -04:00
|
|
|
|
|
|
|
printf("(o)");
|
|
|
|
|
2014-05-18 03:06:11 -04:00
|
|
|
if ( n->combined_pattern ) {
|
|
|
|
printf(" regexp:%s", n->combined_pattern);
|
|
|
|
}
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2014-05-18 03:06:11 -04:00
|
|
|
printf(" endpoint:%d", n->endpoint);
|
2014-05-18 03:00:11 -04:00
|
|
|
|
2014-05-18 03:06:11 -04:00
|
|
|
if (n->data) {
|
|
|
|
printf(" data:%p", n->data);
|
|
|
|
}
|
|
|
|
printf("\n");
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2014-05-18 03:06:11 -04:00
|
|
|
for ( int i = 0 ; i < n->edge_len ; i++ ) {
|
2016-03-08 01:19:54 -05:00
|
|
|
R3Edge * e = n->edges[i];
|
2014-05-18 08:08:43 -04:00
|
|
|
print_indent(level + 1);
|
|
|
|
printf("|-\"%s\"", e->pattern);
|
2014-05-16 02:05:51 -04:00
|
|
|
|
2014-05-23 01:49:18 -04:00
|
|
|
if (e->opcode ) {
|
|
|
|
printf(" opcode:%d", e->opcode);
|
|
|
|
}
|
|
|
|
|
2014-05-18 03:06:11 -04:00
|
|
|
if ( e->child ) {
|
2014-05-18 08:08:43 -04:00
|
|
|
printf("\n");
|
2014-05-18 03:06:11 -04:00
|
|
|
r3_tree_dump( e->child, level + 1);
|
2014-05-16 00:33:59 -04:00
|
|
|
}
|
2014-05-18 03:06:11 -04:00
|
|
|
printf("\n");
|
2014-05-16 00:33:59 -04:00
|
|
|
}
|
|
|
|
}
|
2014-05-16 06:03:52 -04:00
|
|
|
|
2014-05-17 23:59:30 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* return 0 == equal
|
|
|
|
*
|
2014-05-18 01:06:36 -04:00
|
|
|
* -1 == different route
|
2014-05-17 23:59:30 -04:00
|
|
|
*/
|
2015-11-18 01:16:26 -05:00
|
|
|
inline int r3_route_cmp(const R3Route *r1, const match_entry *r2) {
|
2014-05-18 00:49:58 -04:00
|
|
|
if (r1->request_method != 0) {
|
|
|
|
if (0 == (r1->request_method & r2->request_method) ) {
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-17 23:59:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( r1->host && r2->host ) {
|
2014-05-18 00:56:53 -04:00
|
|
|
if (strcmp(r1->host, r2->host) != 0 ) {
|
2014-05-17 23:59:30 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-18 00:49:58 -04:00
|
|
|
if (r1->remote_addr_pattern) {
|
2014-05-18 01:38:30 -04:00
|
|
|
/*
|
|
|
|
* XXX: consider "netinet/in.h"
|
|
|
|
if (r2->remote_addr) {
|
|
|
|
inet_addr(r2->remote_addr);
|
|
|
|
}
|
|
|
|
*/
|
2014-05-18 00:56:53 -04:00
|
|
|
if ( strcmp(r1->remote_addr_pattern, r2->remote_addr) != 0 ) {
|
2014-05-17 23:59:30 -04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-05-18 00:24:07 -04:00
|
|
|
|
|
|
|
/**
|
2014-05-20 13:50:15 -04:00
|
|
|
*
|
2014-05-18 00:24:07 -04:00
|
|
|
*/
|
2015-11-18 01:16:26 -05:00
|
|
|
void r3_node_append_route(R3Node * n, R3Route * r) {
|
2014-05-20 03:49:31 -04:00
|
|
|
if (n->routes == NULL) {
|
2014-05-18 01:06:36 -04:00
|
|
|
n->route_cap = 3;
|
2015-11-18 01:16:26 -05:00
|
|
|
n->routes = zmalloc(sizeof(R3Route) * n->route_cap);
|
2014-05-18 00:24:07 -04:00
|
|
|
}
|
2014-05-18 01:06:36 -04:00
|
|
|
if (n->route_len >= n->route_cap) {
|
|
|
|
n->route_cap *= 2;
|
2015-11-18 01:16:26 -05:00
|
|
|
n->routes = zrealloc(n->routes, sizeof(R3Route) * n->route_cap);
|
2014-05-18 00:24:07 -04:00
|
|
|
}
|
2014-05-20 03:32:22 -04:00
|
|
|
n->routes[ n->route_len++ ] = r;
|
2014-05-18 00:24:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|