r3/src/node.c

713 lines
19 KiB
C
Raw Normal View History

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-14 22:08:42 -04:00
// Judy array
2014-05-16 10:05:23 -04:00
// #include <Judy.h>
2014-05-14 22:08:42 -04:00
2014-05-16 08:22:25 -04:00
#include "r3.h"
2014-05-20 11:33:51 -04:00
#include "r3_str.h"
2014-05-16 08:22:25 -04:00
#include "str_array.h"
#include "zmalloc.h"
2014-05-15 01:39:50 -04:00
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
static int strndiff(char * d1, char * d2, unsigned int n) {
char * o = d1;
while ( *d1 == *d2 && n-- > 0 ) {
d1++;
d2++;
}
return d1 - o;
}
static int strdiff(char * d1, char * d2) {
char * o = d1;
while( *d1 == *d2 ) {
d1++;
d2++;
}
return d1 - o;
}
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
*/
2014-05-16 06:57:36 -04:00
node * r3_tree_create(int cap) {
node * n = (node*) zmalloc( sizeof(node) );
2014-05-15 01:39:50 -04:00
n->edges = (edge**) zmalloc( sizeof(edge*) * cap );
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;
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-05-15 01:39:50 -04:00
return n;
}
2014-05-16 06:57:36 -04:00
void r3_tree_free(node * tree) {
2014-05-21 06:12:14 -04:00
for (int i = 0 ; i < tree->edge_len ; i++ ) {
if (tree->edges[i]) {
r3_edge_free(tree->edges[ i ]);
2014-05-15 10:57:13 -04:00
}
2014-05-15 01:39:50 -04:00
}
zfree(tree->edges);
zfree(tree->routes);
if (tree->pcre_pattern) {
pcre_free(tree->pcre_pattern);
}
#ifdef PCRE_STUDY_JIT_COMPILE
2014-05-21 06:21:15 -04:00
if (tree->pcre_extra) {
pcre_free_study(tree->pcre_extra);
}
#endif
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-05-23 01:49:18 -04:00
edge * r3_node_connectl(node * n, char * pat, int len, int dupl, node *child) {
2014-05-15 01:39:50 -04:00
// find the same sub-pattern, if it does not exist, create one
2014-05-16 03:29:25 -04:00
edge * e;
2014-05-15 06:02:10 -04:00
2014-05-16 06:57:36 -04:00
e = r3_node_find_edge(n, pat);
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);
}
e = r3_edge_create(pat, len, child);
2014-05-18 00:24:07 -04:00
r3_node_append_edge(n, e);
2014-05-15 10:57:13 -04:00
return e;
2014-05-15 01:39:50 -04:00
}
2014-05-18 00:24:07 -04:00
void r3_node_append_edge(node *n, edge *e) {
2014-05-20 03:14:12 -04:00
if (n->edges == NULL) {
n->edge_cap = 3;
n->edges = zmalloc(sizeof(edge) * n->edge_cap);
2014-05-16 03:29:25 -04:00
}
if (n->edge_len >= n->edge_cap) {
n->edge_cap *= 2;
edge ** p = zrealloc(n->edges, sizeof(edge) * n->edge_cap);
if(p) {
n->edges = p;
}
2014-05-15 01:39:50 -04:00
}
n->edges[ n->edge_len++ ] = e;
2014-05-15 01:39:50 -04:00
}
2014-05-16 06:57:36 -04:00
edge * r3_node_find_edge(node * n, char * pat) {
2014-05-16 03:29:25 -04:00
edge * e;
for (int i = 0 ; i < n->edge_len ; i++ ) {
2014-05-15 10:57:13 -04:00
e = n->edges[i];
2014-05-15 06:02:10 -04:00
if ( strcmp(e->pattern, pat) == 0 ) {
return e;
2014-05-15 01:39:50 -04:00
}
}
return NULL;
}
2014-05-16 06:57:36 -04:00
void r3_tree_compile(node *n)
2014-05-16 00:33:59 -04:00
{
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-16 06:57:36 -04:00
r3_tree_compile_patterns(n);
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
for (int i = 0 ; i < n->edge_len ; i++ ) {
2014-05-16 06:57:36 -04:00
r3_tree_compile(n->edges[i]->child);
2014-05-16 02:05:51 -04:00
}
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-05-16 06:57:36 -04:00
void r3_tree_compile_patterns(node * n) {
2014-05-15 09:17:30 -04:00
char * cpat;
char * p;
2014-05-21 03:50:37 -04:00
cpat = zcalloc(sizeof(char) * 128);
2014-05-15 09:17:30 -04:00
if (cpat==NULL)
return;
p = cpat;
2014-05-16 06:57:36 -04:00
strncat(p, "^", 1);
p++;
2014-05-16 06:03:52 -04:00
2014-05-16 03:29:25 -04:00
edge *e = NULL;
2014-05-23 03:48:26 -04:00
int opcode_cnt = 0;
for ( int i = 0 ; i < n->edge_len ; i++ ) {
2014-05-15 10:57:13 -04:00
e = n->edges[i];
2014-05-23 03:48:26 -04:00
if ( e->opcode )
opcode_cnt++;
2014-05-16 00:33:59 -04:00
if ( e->has_slug ) {
2014-05-23 00:08:06 -04:00
// compile "foo/{slug}" to "foo/[^/]+"
2014-05-20 11:25:55 -04:00
char * slug_pat = slug_compile(e->pattern, e->pattern_len);
2014-05-16 00:33:59 -04:00
strcat(p, slug_pat);
} else {
strncat(p++,"(", 1);
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
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
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-15 09:17:30 -04:00
n->combined_pattern = cpat;
2014-05-16 02:05:51 -04:00
const char *error;
int erroffset;
2014-05-17 21:05:55 -04:00
unsigned int option_bits = 0;
2014-05-16 06:03:52 -04:00
2014-05-21 06:20:48 -04:00
if (n->pcre_pattern) {
pcre_free(n->pcre_pattern);
}
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-16 02:05:51 -04:00
&error, /* for error message */
&erroffset, /* for error offset */
NULL); /* use default character tables */
2014-05-16 03:29:25 -04:00
if (n->pcre_pattern == NULL) {
printf("PCRE compilation failed at offset %d: %s, pattern: %s\n", erroffset, error, n->combined_pattern);
2014-05-16 02:05:51 -04:00
return;
}
#ifdef PCRE_STUDY_JIT_COMPILE
if (n->pcre_extra) {
pcre_free_study(n->pcre_extra);
}
2014-05-16 03:29:25 -04:00
n->pcre_extra = pcre_study(n->pcre_pattern, 0, &error);
if (n->pcre_extra == NULL) {
printf("PCRE study failed at offset %s\n", error);
return;
}
#endif
2014-05-16 02:05:51 -04:00
}
2014-05-17 11:54:18 -04:00
match_entry * match_entry_createl(char * path, int path_len) {
match_entry * entry = zmalloc(sizeof(match_entry));
2014-05-16 07:12:01 -04:00
if(!entry)
return NULL;
entry->vars = str_array_create(3);
entry->path = path;
entry->path_len = path_len;
2014-05-17 23:06:24 -04:00
entry->data = NULL;
2014-05-16 07:12:01 -04:00
return entry;
}
void match_entry_free(match_entry * entry) {
2014-05-22 10:26:14 -04:00
str_array_free(entry->vars);
zfree(entry);
2014-05-16 07:12:01 -04:00
}
2014-05-16 02:05:51 -04:00
2014-05-16 02:24:00 -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.
*/
2014-05-20 13:22:49 -04:00
node * r3_tree_matchl(const node * n, char * path, int path_len, match_entry * entry) {
info("try matching: %s\n", path);
2014-05-16 06:03:52 -04:00
2014-05-16 06:57:36 -04:00
edge *e;
2014-05-16 09:02:02 -04:00
int rc;
int i;
2014-05-22 23:42:19 -04:00
int ov_cnt;
2014-05-23 03:48:26 -04:00
int restlen;
2014-05-23 04:17:35 -04:00
char *pp;
char *pp_end = path + path_len;
2014-05-23 03:48:26 -04:00
2014-05-23 04:17:35 -04:00
if (n->compare_type == NODE_COMPARE_OPCODE) {
2014-05-23 03:48:26 -04:00
for (i = 0; i < n->edge_len ; i++ ) {
2014-05-23 04:17:35 -04:00
pp = path;
2014-05-23 03:48:26 -04:00
e = n->edges[i];
switch(e->opcode) {
case OP_EXPECT_NOSLASH:
while (*pp != '/' && pp < pp_end) {
pp++;
}
break;
case OP_EXPECT_DIGITS:
while ( isdigit(*pp) && pp < pp_end) {
pp++;
}
break;
case OP_EXPECT_WORDS:
while ( (isdigit(*pp) || isalpha(*pp)) && pp < pp_end) {
pp++;
}
break;
case OP_EXPECT_NODASH:
while (*pp != '-' && pp < pp_end) {
pp++;
}
break;
}
2014-05-23 04:17:35 -04:00
// check match
2014-05-23 03:48:26 -04:00
if ( (pp - path) > 0) {
restlen = pp_end - pp;
if (entry) {
str_array_append(entry->vars , zstrndup(path, pp - path));
}
if (restlen == 0) {
return e->child && e->child->endpoint > 0 ? e->child : NULL;
}
return r3_tree_matchl(e->child, pp, pp_end - pp, entry);
}
}
}
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) {
info("pcre matching %s on %s\n", n->combined_pattern, path);
2014-05-22 23:42:19 -04:00
ov_cnt = (1 + n->edge_len) * 3;
int ov[ ov_cnt ];
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 */
ov_cnt); /* number of elements in the output vector */
2014-05-16 02:05:51 -04:00
// info("rc: %d\n", rc );
2014-05-16 02:05:51 -04:00
if (rc < 0) {
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
}
// does not match all edges, return NULL;
return NULL;
}
2014-05-23 04:17:35 -04:00
char *substring_start;
int substring_length;
2014-05-16 02:05:51 -04:00
for (i = 1; i < rc; i++)
{
2014-05-23 04:17:35 -04:00
substring_start = path + ov[2*i];
substring_length = ov[2*i+1] - ov[2*i];
// info("%2d: %.*s\n", i, substring_length, substring_start);
2014-05-16 06:03:52 -04:00
2014-05-16 02:05:51 -04:00
if ( substring_length > 0) {
2014-05-23 03:48:26 -04:00
restlen = path_len - ov[1]; // fully match to the end
// info("matched item => restlen:%d edges:%d i:%d\n", restlen, n->edge_len, i);
2014-05-16 06:03:52 -04:00
e = n->edges[i - 1];
if (entry && e->has_slug) {
2014-05-16 07:12:01 -04:00
// append captured token to entry
2014-05-21 04:36:50 -04:00
str_array_append(entry->vars , zstrndup(substring_start, substring_length));
2014-05-16 06:03:52 -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
}
// get the length of orginal string: $0
2014-05-22 23:42:19 -04:00
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
2014-05-16 09:02:02 -04: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;
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
}
return r3_tree_matchl(e->child, path + e->pattern_len, restlen, entry);
2014-05-16 02:05:51 -04:00
}
return NULL;
}
2014-05-20 13:22:49 -04:00
route * r3_tree_match_route(const node *tree, match_entry * entry) {
2014-05-18 22:34:48 -04:00
node *n;
n = r3_tree_match_entry(tree, entry);
2014-05-18 01:06:36 -04:00
if (n->routes && n->route_len > 0) {
2014-05-18 00:49:58 -04:00
int i;
2014-05-18 01:06:36 -04:00
for (i = 0; i < n->route_len ; i++ ) {
if ( r3_route_cmp(n->routes[i], entry) == 0 ) {
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
2014-05-20 13:22:49 -04:00
inline edge * r3_node_find_edge_str(const node * n, char * str, int str_len) {
2014-05-16 06:57:36 -04:00
int i = 0;
2014-05-23 01:49:18 -04:00
int matched_idx = -1;
2014-05-20 13:20:18 -04:00
char firstbyte = *str;
for (; i < n->edge_len ; i++ ) {
2014-05-20 13:20:18 -04:00
if ( firstbyte == *(n->edges[i]->pattern) ) {
2014-05-23 01:49:18 -04:00
info("matching '%s' with '%s'\n", str, node_edge_pattern(n,i) );
if ( strncmp( node_edge_pattern(n,i), str, node_edge_pattern_len(n,i) ) == 0 ) {
return n->edges[i];
}
return NULL;
2014-05-16 02:05:51 -04:00
}
}
return NULL;
2014-05-15 08:38:07 -04:00
}
2014-05-16 06:57:36 -04:00
node * r3_node_create() {
node * n = (node*) zmalloc( sizeof(node) );
2014-05-16 03:29:25 -04:00
n->edges = NULL;
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;
return n;
}
2014-05-15 06:26:41 -04:00
route * r3_route_create(char * path) {
return r3_route_createl(path, strlen(path));
2014-05-18 00:24:07 -04:00
}
void r3_route_free(route * route) {
2014-05-22 10:26:14 -04:00
zfree(route);
2014-05-18 00:24:07 -04:00
}
route * r3_route_createl(char * path, int path_len) {
route * info = zmalloc(sizeof(route));
2014-05-18 00:24:07 -04:00
info->path = path;
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-20 12:47:09 -04:00
node * r3_tree_insert_pathl(node *tree, char *path, int path_len, void * data)
2014-05-15 06:26:41 -04:00
{
2014-05-20 12:47:09 -04:00
return r3_tree_insert_pathl_(tree, path, path_len, NULL , data);
2014-05-15 06:26:41 -04:00
}
2014-05-18 00:24:07 -04:00
/**
* Return the last inserted node.
*/
2014-05-20 12:47:09 -04:00
node * r3_tree_insert_pathl_(node *tree, char *path, int path_len, route * route, void * data)
2014-05-15 06:26:41 -04:00
{
2014-05-16 03:29:25 -04:00
node * n = tree;
edge * e = NULL;
2014-05-15 10:57:13 -04:00
/* length of common prefix */
int prefix_len = 0;
for( int i = 0 ; i < n->edge_len ; i++ ) {
prefix_len = strndiff(path, n->edges[i]->pattern, n->edges[i]->pattern_len);
2014-05-15 10:57:13 -04:00
// printf("prefix_len: %d %s vs %s\n", prefix_len, path, n->edges[i]->pattern );
2014-05-15 10:57:13 -04:00
// no common, consider insert a new edge
if ( prefix_len > 0 ) {
2014-05-15 10:57:13 -04:00
e = n->edges[i];
break;
}
}
2014-05-15 12:00:19 -04:00
// branch the edge at correct position (avoid broken slugs)
char *slug_s;
if ( (slug_s = inside_slug(path, path_len, path + prefix_len)) != NULL ) {
prefix_len = slug_s - path;
2014-05-15 12:00:19 -04:00
}
2014-05-15 10:57:13 -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
2014-05-23 01:49:18 -04:00
int slug_cnt = slug_count(path, path_len);
if ( slug_cnt > 1 ) {
2014-05-20 09:38:37 -04:00
int slug_len;
2014-05-23 01:49:18 -04:00
char *p = slug_find_placeholder(path, &slug_len);
2014-05-18 00:28:12 -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-05-23 01:49:18 -04:00
p = slug_find_placeholder(p + slug_len + 1, NULL);
2014-05-20 09:38:37 -04:00
}
#ifdef DEBUG
assert(p);
#endif
// insert the first one edge, and break at "p"
node * child = r3_tree_create(3);
2014-05-23 01:49:18 -04:00
r3_node_connect(n, zstrndup(path, (int)(p - path)), child);
// and insert the rest part to the child
2014-05-20 12:47:09 -04:00
return r3_tree_insert_pathl_(child, p, path_len - (int)(p - path), route, data);
2014-05-23 01:49:18 -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;
char *slug_p = slug_find_placeholder(path, &slug_len);
int slug_pattern_len = 0;
char *slug_pattern = slug_find_pattern(slug_p, &slug_pattern_len);
int opcode = 0;
// if there is a pattern defined.
if (slug_pattern) {
char *cpattern = slug_compile(slug_pattern, slug_pattern_len);
opcode = r3_pattern_to_opcode(cpattern, strlen(cpattern));
zfree(cpattern);
} else {
opcode = OP_EXPECT_NOSLASH;
}
// found opcode
if (opcode) {
// if the slug starts after one+ charactor, for example foo{slug}
node *c1;
if (slug_p > path) {
c1 = r3_tree_create(3);
r3_node_connectl(n, path, slug_p - path, 1, c1); // duplicate
} else {
c1 = n;
}
node * c2 = r3_tree_create(3);
edge * op_edge = r3_node_connectl(c1, slug_p, slug_len , 1, c2);
op_edge->opcode = opcode;
// insert rest
int restlen = (path_len - (slug_p - path)) - slug_len;
if (restlen) {
return r3_tree_insert_pathl_(c2, slug_p + slug_len, restlen, route, data);
}
c2->data = data;
c2->endpoint++;
if (route) {
route->data = data;
r3_node_append_route(c2, route);
}
return c2;
}
}
// only one slug
node * child = r3_tree_create(3);
2014-05-23 01:49:18 -04:00
r3_node_connect(n, zstrndup(path, path_len) , child);
child->data = data;
child->endpoint++;
if (route) {
route->data = data;
r3_node_append_route(child, route);
}
return child;
2014-05-18 00:28:12 -04:00
}
} else if ( prefix_len == e->pattern_len ) { // fully-equal to the pattern of the edge
char * subpath = path + prefix_len;
int subpath_len = path_len - prefix_len;
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-05-20 12:47:09 -04:00
return r3_tree_insert_pathl_(e->child, subpath, subpath_len, route, data);
2014-05-15 10:57:13 -04:00
} else {
2014-05-18 22:49:47 -04:00
// there are no more path to insert
// see if there is an endpoint already
2014-05-20 13:15:54 -04:00
if (e->child->endpoint > 0) {
2014-05-18 22:49:47 -04:00
// XXX: return an error code instead of NULL
return NULL;
}
e->child->endpoint++; // make it as an endpoint
2014-05-17 23:06:24 -04:00
e->child->data = data;
2014-05-18 01:06:36 -04:00
if (route) {
route->data = data;
r3_node_append_route(e->child, route);
2014-05-18 00:28:12 -04:00
}
2014-05-15 10:57:13 -04:00
return e->child;
}
} 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...
*/
char * s2 = path + prefix_len;
int s2_len = path_len - prefix_len;
r3_edge_branch(e, prefix_len);
2014-05-20 12:47:09 -04:00
return r3_tree_insert_pathl_(e->child, s2 , s2_len, route , data);
2014-05-15 10:57:13 -04:00
} else {
2014-05-18 01:06:36 -04:00
printf("unexpected route.");
2014-05-15 10:57:13 -04:00
return NULL;
}
return n;
2014-05-15 06:26:41 -04:00
}
2014-05-16 06:57:36 -04:00
bool r3_node_has_slug_edges(node *n) {
2014-05-16 00:33:59 -04:00
bool found = FALSE;
2014-05-16 03:29:25 -04:00
edge *e;
for ( int i = 0 ; i < n->edge_len ; i++ ) {
2014-05-16 00:33:59 -04:00
e = n->edges[i];
e->has_slug = contains_slug(e->pattern);
if (e->has_slug)
2014-05-16 00:33:59 -04:00
found = TRUE;
}
return found;
}
2014-05-16 03:29:25 -04:00
2014-05-15 06:02:10 -04:00
2014-05-16 06:57:36 -04:00
void r3_tree_dump(node * n, int level) {
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++ ) {
edge * e = n->edges[i];
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 ) {
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
/**
* return 0 == equal
*
2014-05-18 01:06:36 -04:00
* -1 == different route
*/
int r3_route_cmp(route *r1, 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;
}
}
if ( r1->path && r2->path ) {
2014-05-18 00:56:53 -04:00
if ( strcmp(r1->path, r2->path) != 0 ) {
return -1;
}
}
if ( r1->host && r2->host ) {
2014-05-18 00:56:53 -04:00
if (strcmp(r1->host, r2->host) != 0 ) {
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 ) {
return -1;
}
}
return 0;
}
2014-05-18 00:24:07 -04:00
/**
*
2014-05-18 00:24:07 -04:00
*/
void r3_node_append_route(node * n, route * 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;
n->routes = zmalloc(sizeof(route) * 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;
n->routes = zrealloc(n->routes, sizeof(route) * n->route_cap);
2014-05-18 00:24:07 -04:00
}
n->routes[ n->route_len++ ] = r;
2014-05-18 00:24:07 -04:00
}