Compare commits
	
		
			2 Commits 
		
	
	
		
			6eb965adcd
			...
			cf4451740f
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						cf4451740f | |
| 
							
							
								
									
								
								 | 
						2ce49a3347 | 
| 
						 | 
				
			
			@ -49,11 +49,23 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
 | 
			
		|||
                            lnm_http_method method, const char *path);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add all of the child router's routes to the parent router, under the given
 | 
			
		||||
 * route prefix.
 | 
			
		||||
 * Checks whether the two routers have any conflicting parts.
 | 
			
		||||
 */
 | 
			
		||||
lnm_err lnm_http_router_nest(lnm_http_router *parent,
 | 
			
		||||
                             const lnm_http_router *child, const char *prefix);
 | 
			
		||||
bool lnm_http_router_conflicts(const lnm_http_router *r1,
 | 
			
		||||
                               const lnm_http_router *r2);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Merge two routers, with the result ending up in r1. This is equivalent to
 | 
			
		||||
 * nesting a router on '/'.
 | 
			
		||||
 */
 | 
			
		||||
lnm_err lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Integrate the child router into the parent routing, mounting its paths on the
 | 
			
		||||
 * given prefix.
 | 
			
		||||
 */
 | 
			
		||||
lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child,
 | 
			
		||||
                             const char *prefix);
 | 
			
		||||
 | 
			
		||||
lnm_http_route_err lnm_http_router_route(lnm_http_route_match *out,
 | 
			
		||||
                                         const lnm_http_router *router,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ void lnm_http_router_free(lnm_http_router *router) {
 | 
			
		|||
 | 
			
		||||
  for (size_t i = 0; i < lnm_http_method_total; i++) {
 | 
			
		||||
    lnm_http_route_free(router->routes[i]);
 | 
			
		||||
    lnm_http_route_free(router->multi_segment_routes[i]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  free(router);
 | 
			
		||||
| 
						 | 
				
			
			@ -287,3 +288,95 @@ lnm_http_route_match_get(lnm_http_route_match *match, const char *key) {
 | 
			
		|||
 | 
			
		||||
  return &match->key_segments[trie->index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool lnm_http_router_conflicts(const lnm_http_router *r1,
 | 
			
		||||
                               const lnm_http_router *r2) {
 | 
			
		||||
  // First check the literal routes for the routers
 | 
			
		||||
  for (lnm_http_method m = 0; m < lnm_http_method_total; m++) {
 | 
			
		||||
    if ((r1->routes[m] != NULL && r2->routes[m] != NULL) ||
 | 
			
		||||
        (r1->multi_segment_routes[m] != NULL &&
 | 
			
		||||
         r2->multi_segment_routes[m] != NULL)) {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((r1->single_segment_child != NULL && r2->single_segment_child != NULL) &&
 | 
			
		||||
      lnm_http_router_conflicts(r1->single_segment_child,
 | 
			
		||||
                                r2->single_segment_child)) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (unsigned char c = 0; c < 128; c++) {
 | 
			
		||||
    if ((r1->exact_children[c] != NULL && r2->exact_children[c] != NULL) &&
 | 
			
		||||
        lnm_http_router_conflicts(r1->exact_children[c],
 | 
			
		||||
                                  r2->exact_children[c])) {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2) {
 | 
			
		||||
  for (lnm_http_method m = 0; m < lnm_http_method_total; m++) {
 | 
			
		||||
    if (r2->routes[m] != NULL) {
 | 
			
		||||
      r1->routes[m] = r2->routes[m];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (r2->multi_segment_routes[m] != NULL) {
 | 
			
		||||
      r1->multi_segment_routes[m] = r2->multi_segment_routes[m];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (r1->single_segment_child != NULL && r2->single_segment_child != NULL) {
 | 
			
		||||
    __lnm_http_router_merge(r1->single_segment_child, r2->single_segment_child);
 | 
			
		||||
  } else if (r2->single_segment_child != NULL) {
 | 
			
		||||
    r1->single_segment_child = r2->single_segment_child;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (unsigned char c = 0; c < 128; c++) {
 | 
			
		||||
    if (r1->exact_children[c] != NULL && r2->exact_children[c] != NULL) {
 | 
			
		||||
      __lnm_http_router_merge(r1->exact_children[c], r2->exact_children[c]);
 | 
			
		||||
    } else if (r2->exact_children[c] != NULL) {
 | 
			
		||||
      r1->exact_children[c] = r2->exact_children[c];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  r1->represents_route = r1->represents_route || r2->represents_route;
 | 
			
		||||
 | 
			
		||||
  free(r2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lnm_err lnm_http_router_merge(lnm_http_router *r1, lnm_http_router *r2) {
 | 
			
		||||
  if (lnm_http_router_conflicts(r1, r2)) {
 | 
			
		||||
    return lnm_err_overlapping_route;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  __lnm_http_router_merge(r1, r2);
 | 
			
		||||
 | 
			
		||||
  return lnm_err_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lnm_err lnm_http_router_nest(lnm_http_router *parent, lnm_http_router *child,
 | 
			
		||||
                             const char *prefix) {
 | 
			
		||||
  if (!is_ascii(prefix) || prefix[0] != '/') {
 | 
			
		||||
    return lnm_err_invalid_route;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lnm_http_router *router = parent;
 | 
			
		||||
 | 
			
		||||
  // The child router's routes are also mounted on '/', so we stop one earlier
 | 
			
		||||
  // as we need to merge it with the router representing the '/'
 | 
			
		||||
  while (*prefix != '\0') {
 | 
			
		||||
    unsigned char c = *prefix;
 | 
			
		||||
 | 
			
		||||
    if (router->exact_children[c] == NULL) {
 | 
			
		||||
      LNM_RES(lnm_http_router_init(&router->exact_children[c]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    router = router->exact_children[c];
 | 
			
		||||
    prefix++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return lnm_http_router_merge(router, child);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
#include "lnm/common.h"
 | 
			
		||||
#include "lnm/http/route.h"
 | 
			
		||||
#include "test.h"
 | 
			
		||||
 | 
			
		||||
#include "lnm/http/loop.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -51,10 +53,56 @@ void test_routing_star() {
 | 
			
		|||
  TEST_CHECK(lnm_http_router_route(&match, router, lnm_http_method_get, "/hello/world") == lnm_http_route_err_match);
 | 
			
		||||
  TEST_CHECK(match.key_segments[0].start == 1);
 | 
			
		||||
  TEST_CHECK(match.key_segments[0].len == 11);
 | 
			
		||||
 | 
			
		||||
  lnm_http_router_free(router);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_routing_merge() {
 | 
			
		||||
  lnm_http_router *rtr1, *rtr2;
 | 
			
		||||
  lnm_http_route *rt1, *rt2;
 | 
			
		||||
  lnm_http_route_match match;
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_init(&rtr1) == lnm_err_ok);
 | 
			
		||||
  TEST_CHECK(lnm_http_router_init(&rtr2) == lnm_err_ok);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_add(&rt1, rtr1, lnm_http_method_get, "/*key") == lnm_err_ok);
 | 
			
		||||
  TEST_CHECK(lnm_http_router_add(NULL, rtr1, lnm_http_method_get, "/:key/hello") == lnm_err_ok);
 | 
			
		||||
  TEST_CHECK(lnm_http_router_add(&rt2, rtr2, lnm_http_method_get, "/test2") == lnm_err_ok);
 | 
			
		||||
  TEST_CHECK(lnm_http_router_add(NULL, rtr2, lnm_http_method_get, "/:key/hello2") == lnm_err_ok);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_merge(rtr1, rtr2) == lnm_err_ok);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_route(&match, rtr1, lnm_http_method_get, "/some/thing") == lnm_http_route_err_match);
 | 
			
		||||
  TEST_CHECK(match.route == rt1);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_route(&match, rtr1, lnm_http_method_get, "/test2") == lnm_http_route_err_match);
 | 
			
		||||
  TEST_CHECK(match.route == rt2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_routing_nest() {
 | 
			
		||||
  lnm_http_router *r1, *r2;
 | 
			
		||||
  lnm_http_route_match match;
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_init(&r1) == lnm_err_ok);
 | 
			
		||||
  TEST_CHECK(lnm_http_router_init(&r2) == lnm_err_ok);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_add(NULL, r1, lnm_http_method_get, "/*key") == lnm_err_ok);
 | 
			
		||||
  TEST_CHECK(lnm_http_router_add(NULL, r2, lnm_http_method_get, "/test/test2") == lnm_err_ok);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_nest(r2, r1, "/test") == lnm_err_ok);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_route(&match, r2, lnm_http_method_get, "/test/test_var/secondvar") == lnm_http_route_err_match);
 | 
			
		||||
  TEST_CHECK(match.key_segments[0].start == 6);
 | 
			
		||||
  TEST_CHECK(match.key_segments[0].len == 18);
 | 
			
		||||
 | 
			
		||||
  TEST_CHECK(lnm_http_router_route(&match, r2, lnm_http_method_get, "/test/test2") == lnm_http_route_err_match);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_LIST = {
 | 
			
		||||
  { "routing simple", test_routing_simple },
 | 
			
		||||
  { "routing star", test_routing_star },
 | 
			
		||||
  { "routing merge", test_routing_merge },
 | 
			
		||||
  { "routing nest", test_routing_nest },
 | 
			
		||||
  { NULL, NULL }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue