原文:Exercise 47: A Fast URL Routerhtml
译者:飞龙git
我如今打算向你展现使用TSTree
来建立服务器中的快速URL路由。它适用于应用中的简单的URL匹配,而不是在许多Web应用框架中的更复杂(一些状况下也没必要要)的路由发现功能。github
我打算编程一个小型命令行工具和路由交互,他叫作urlor
,读取简单的路由文件,以后提示用户输入要检索的URL。web
#include <lcthw/tstree.h> #include <lcthw/bstrlib.h> TSTree *add_route_data(TSTree *routes, bstring line) { struct bstrList *data = bsplit(line, ' '); check(data->qty == 2, "Line '%s' does not have 2 columns", bdata(line)); routes = TSTree_insert(routes, bdata(data->entry[0]), blength(data->entry[0]), bstrcpy(data->entry[1])); bstrListDestroy(data); return routes; error: return NULL; } TSTree *load_routes(const char *file) { TSTree *routes = NULL; bstring line = NULL; FILE *routes_map = NULL; routes_map = fopen(file, "r"); check(routes_map != NULL, "Failed to open routes: %s", file); while((line = bgets((bNgetc)fgetc, routes_map, '\n')) != NULL) { check(btrimws(line) == BSTR_OK, "Failed to trim line."); routes = add_route_data(routes, line); check(routes != NULL, "Failed to add route."); bdestroy(line); } fclose(routes_map); return routes; error: if(routes_map) fclose(routes_map); if(line) bdestroy(line); return NULL; } bstring match_url(TSTree *routes, bstring url) { bstring route = TSTree_search(routes, bdata(url), blength(url)); if(route == NULL) { printf("No exact match found, trying prefix.\n"); route = TSTree_search_prefix(routes, bdata(url), blength(url)); } return route; } bstring read_line(const char *prompt) { printf("%s", prompt); bstring result = bgets((bNgetc)fgetc, stdin, '\n'); check_debug(result != NULL, "stdin closed."); check(btrimws(result) == BSTR_OK, "Failed to trim."); return result; error: return NULL; } void bdestroy_cb(void *value, void *ignored) { (void)ignored; bdestroy((bstring)value); } void destroy_routes(TSTree *routes) { TSTree_traverse(routes, bdestroy_cb, NULL); TSTree_destroy(routes); } int main(int argc, char *argv[]) { bstring url = NULL; bstring route = NULL; check(argc == 2, "USAGE: urlor <urlfile>"); TSTree *routes = load_routes(argv[1]); check(routes != NULL, "Your route file has an error."); while(1) { url = read_line("URL> "); check_debug(url != NULL, "goodbye."); route = match_url(routes, url); if(route) { printf("MATCH: %s == %s\n", bdata(url), bdata(route)); } else { printf("FAIL: %s\n", bdata(url)); } bdestroy(url); } destroy_routes(routes); return 0; error: destroy_routes(routes); return 1; }
以后我建立了一个简单的文件,含有一些用于交互的伪造的路由:正则表达式
/ MainApp /hello Hello /hello/ Hello /signup Signup /logout Logout /album/ Album
一旦你使urlor
工做,而且建立了路由文件,你能够尝试这样:算法
$ ./bin/urlor urls.txt URL> / MATCH: / == MainApp URL> /hello MATCH: /hello == Hello URL> /hello/zed No exact match found, trying prefix. MATCH: /hello/zed == Hello URL> /album No exact match found, trying prefix. MATCH: /album == Album URL> /album/12345 No exact match found, trying prefix. MATCH: /album/12345 == Album URL> asdfasfdasfd No exact match found, trying prefix. FAIL: asdfasfdasfd URL> /asdfasdfasf No exact match found, trying prefix. MATCH: /asdfasdfasf == MainApp URL> $
你能够看到路由系统首先尝试精确匹配,以后若是找不到的话则会尝试前缀匹配。这主要是尝试这两者的不一样。根据你的URL的语义,你可能想要之中精确匹配,始终前缀匹配,或者执行两者并选出“最好”的那个。编程
URL很是古怪。由于人们想让它们神奇地处理它们的web应用所具备的,全部疯狂的事情,即便不是很合逻辑。在这个对如何将TSTree
用做路由的简单演示中,它具备一些人们不想要的缺陷。好比,它会把/al
匹配到Album
,它是人们一般不想要的。它们想要/album/*
匹配到Album
以及/al
匹配到404错误。服务器
这并不难以实现,由于你能够修改前缀算法来以你想要的任何方式匹配。若是你修改了匹配算法,来寻找全部匹配的前缀,以后选出“最好”的那个,你就能够轻易作到它。这种状况下,/al
回匹配MainApp
或者Album
。得到这些结果以后,就能够执行一些逻辑来决定哪一个“最好”。框架
另外一件你能在真正的路由系统里作的事情,就是使用TSTree
来寻找全部可能的匹配,可是这些匹配是须要检查的一些模式串。在许多web应用中,有一个正则表达式的列表,用于和每一个请求的URL进行匹配。匹配全部这些正则表达式很是花时间,因此你能够使用TSTree
来经过它们的前缀寻找全部可能的结果。因而你就能够缩小模式串的范围,更快速地作尝试。工具
使用这种方式,你的URL会精确匹配,由于你实际上运行了正则表达式,它们匹配起来更快,由于你经过可能的前缀来查找它们。
这种算法也可用于全部须要用户可视化的灵活路由机制。域名、IP地址、包注册器和目录,文件或者URL。
建立一个实际的引擎,使用Handler
结构储存应用,而不是仅仅储存应用的字符串。这个结构储存它所绑定的URL,名称和任何须要构建实际路由系统的东西。
将URL映射到.so
文件而不是任意的名字,而且使用dlopen
系统动态加载处理器,并执行它们所包含的回调。将这些回调放进你的Handler
结构体中,以后你就用C编写了动态回调处理器系统的所有。