笨辦法學C 練習47:一個快速的URL路由

練習47:一個快速的URL路由

原文: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編寫了動態回調處理器系統的所有。

相關文章
相關標籤/搜索