feat(routing): multi-segment wildcard matches
							parent
							
								
									3ae1b62dd0
								
							
						
					
					
						commit
						115bf74456
					
				| 
						 | 
					@ -23,6 +23,25 @@ lnm_http_step_err print_step(lnm_http_conn *conn) {
 | 
				
			||||||
  return lnm_http_step_err_done;
 | 
					  return lnm_http_step_err_done;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lnm_http_step_err print_step2(lnm_http_conn *conn) {
 | 
				
			||||||
 | 
					  lnm_http_loop_ctx *ctx = conn->ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const char *key;
 | 
				
			||||||
 | 
					  size_t key_len = lnm_http_req_route_segment(&key, &ctx->req, "key") ;
 | 
				
			||||||
 | 
					  lnm_linfo("main", "yuhh key: %.*s", key_len, key);
 | 
				
			||||||
 | 
					  return lnm_http_step_err_done;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lnm_http_step_err print_step3(lnm_http_conn *conn) {
 | 
				
			||||||
 | 
					  lnm_http_loop_ctx *ctx = conn->ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const char *key;
 | 
				
			||||||
 | 
					  size_t key_len = lnm_http_req_route_segment(&key, &ctx->req, "cool") ;
 | 
				
			||||||
 | 
					  lnm_linfo("main", "cool: %.*s", key_len, key);
 | 
				
			||||||
 | 
					  return lnm_http_step_err_done;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main() {
 | 
					int main() {
 | 
				
			||||||
  lnm_http_loop *hl;
 | 
					  lnm_http_loop *hl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,8 +53,13 @@ int main() {
 | 
				
			||||||
  lnm_http_router_init(&router);
 | 
					  lnm_http_router_init(&router);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  lnm_http_route *route;
 | 
					  lnm_http_route *route;
 | 
				
			||||||
 | 
					  lnm_http_router_add(&route, router, lnm_http_method_get, "/emma");
 | 
				
			||||||
  lnm_http_router_add(&route, router, lnm_http_method_get, "/:key");
 | 
					  lnm_http_router_add(&route, router, lnm_http_method_get, "/:key");
 | 
				
			||||||
  lnm_http_route_step_append(route, print_step, false);
 | 
					  lnm_http_route_step_append(route, print_step, false);
 | 
				
			||||||
 | 
					  lnm_http_router_add(&route, router, lnm_http_method_get, "/:key/two");
 | 
				
			||||||
 | 
					  lnm_http_route_step_append(route, print_step2, false);
 | 
				
			||||||
 | 
					  lnm_http_router_add(&route, router, lnm_http_method_get, "/*cool");
 | 
				
			||||||
 | 
					  lnm_http_route_step_append(route, print_step3, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  lnm_http_loop_router_set(hl, router);
 | 
					  lnm_http_loop_router_set(hl, router);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,19 +5,25 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "lnm/http/loop.h"
 | 
					#include "lnm/http/loop.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct lnm_http_router {
 | 
					 | 
				
			||||||
  struct lnm_http_router *exact_children[128];
 | 
					 | 
				
			||||||
  struct lnm_http_router *single_segment_child;
 | 
					 | 
				
			||||||
  lnm_http_route *routes[lnm_http_method_total];
 | 
					 | 
				
			||||||
  bool represents_route;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct lnm_http_route_segment_trie {
 | 
					typedef struct lnm_http_route_segment_trie {
 | 
				
			||||||
  struct lnm_http_route_segment_trie *children[128];
 | 
					  struct lnm_http_route_segment_trie *children[128];
 | 
				
			||||||
  size_t index;
 | 
					  size_t index;
 | 
				
			||||||
  bool represents_segment;
 | 
					  bool represents_segment;
 | 
				
			||||||
} lnm_http_route_segment_trie;
 | 
					} lnm_http_route_segment_trie;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lnm_http_route {
 | 
				
			||||||
 | 
					  lnm_http_route_segment_trie *key_segments;
 | 
				
			||||||
 | 
					  lnm_http_step *step;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct lnm_http_router {
 | 
				
			||||||
 | 
					  struct lnm_http_router *exact_children[128];
 | 
				
			||||||
 | 
					  struct lnm_http_router *single_segment_child;
 | 
				
			||||||
 | 
					  lnm_http_route *multi_segment_routes[lnm_http_method_total];
 | 
				
			||||||
 | 
					  lnm_http_route *routes[lnm_http_method_total];
 | 
				
			||||||
 | 
					  bool represents_route;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lnm_err lnm_http_route_segment_trie_init(lnm_http_route_segment_trie **out);
 | 
					lnm_err lnm_http_route_segment_trie_init(lnm_http_route_segment_trie **out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void lnm_http_route_segment_trie_free(lnm_http_route_segment_trie *trie);
 | 
					void lnm_http_route_segment_trie_free(lnm_http_route_segment_trie *trie);
 | 
				
			||||||
| 
						 | 
					@ -39,11 +45,6 @@ typedef struct lnm_http_step {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
lnm_err lnm_http_step_init(lnm_http_step **out);
 | 
					lnm_err lnm_http_step_init(lnm_http_step **out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct lnm_http_route {
 | 
					 | 
				
			||||||
  lnm_http_route_segment_trie *key_segments;
 | 
					 | 
				
			||||||
  lnm_http_step *step;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
lnm_err lnm_http_route_init(lnm_http_route **out);
 | 
					lnm_err lnm_http_route_init(lnm_http_route **out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void lnm_http_route_free(lnm_http_route *route);
 | 
					void lnm_http_route_free(lnm_http_route *route);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -93,9 +93,42 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
 | 
				
			||||||
      http_router = http_router->single_segment_child;
 | 
					      http_router = http_router->single_segment_child;
 | 
				
			||||||
      path = new_path;
 | 
					      path = new_path;
 | 
				
			||||||
    } break;
 | 
					    } break;
 | 
				
			||||||
    case '*':
 | 
					    case '*': {
 | 
				
			||||||
      // TODO multi-segment wildcard
 | 
					      const char *next_slash_ptr = strchr(path + 1, '/');
 | 
				
			||||||
      break;
 | 
					
 | 
				
			||||||
 | 
					      // Star match should be at end of route
 | 
				
			||||||
 | 
					      if (next_slash_ptr != NULL) {
 | 
				
			||||||
 | 
					        res = lnm_err_invalid_route;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        goto end;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const char *end = strchr(path + 1, '\0');
 | 
				
			||||||
 | 
					      size_t key_len = end - (path + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (key_len == 0) {
 | 
				
			||||||
 | 
					        res = lnm_err_invalid_route;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        goto end;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      res = lnm_http_route_key_segment_insert(route, path + 1, key_len,
 | 
				
			||||||
 | 
					                                              key_segments_count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (res != lnm_err_ok) {
 | 
				
			||||||
 | 
					        goto end;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      key_segments_count++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (http_router->multi_segment_routes[method] != NULL) {
 | 
				
			||||||
 | 
					        res = lnm_err_overlapping_route;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      http_router->multi_segment_routes[method] = route;
 | 
				
			||||||
 | 
					      goto end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      if (http_router->exact_children[c] == NULL) {
 | 
					      if (http_router->exact_children[c] == NULL) {
 | 
				
			||||||
        res = lnm_http_router_init(&http_router->exact_children[c]);
 | 
					        res = lnm_http_router_init(&http_router->exact_children[c]);
 | 
				
			||||||
| 
						 | 
					@ -107,6 +140,7 @@ lnm_err lnm_http_router_add(lnm_http_route **out, lnm_http_router *http_router,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      http_router = http_router->exact_children[c];
 | 
					      http_router = http_router->exact_children[c];
 | 
				
			||||||
      path++;
 | 
					      path++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -195,6 +229,26 @@ static lnm_http_route_err __lnm_http_router_route(lnm_http_route_match *out,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const lnm_http_route *multi_segment_route =
 | 
				
			||||||
 | 
					      router->multi_segment_routes[method];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (multi_segment_route != NULL) {
 | 
				
			||||||
 | 
					    const char *end_ptr = strchr(path + path_index, '\0');
 | 
				
			||||||
 | 
					    size_t segment_len = end_ptr - (path + path_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO do 405's make sense for star matches?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (segment_len > 0) {
 | 
				
			||||||
 | 
					      if (out != NULL) {
 | 
				
			||||||
 | 
					        out->route = multi_segment_route;
 | 
				
			||||||
 | 
					        out->key_segments[matched_key_segments].start = path_index;
 | 
				
			||||||
 | 
					        out->key_segments[matched_key_segments].len = segment_len;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return lnm_http_route_err_match;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return res;
 | 
					  return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue