From 9357f0def17fe0e4d86c5c1411782228370f26a5 Mon Sep 17 00:00:00 2001 From: Douglas-Lee Date: Sun, 17 Dec 2023 01:01:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=BC=80=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/resty/parser.lua | 15 ++++++++++++++ src/resty/parser_test.lua | 29 -------------------------- src/resty/router.lua | 3 +++ src/resty/sample.lua | 7 ++++--- src/resty/trie.lua | 43 ++++++++++++++++++++++++++++++++++----- 5 files changed, 60 insertions(+), 37 deletions(-) delete mode 100644 src/resty/parser_test.lua diff --git a/src/resty/parser.lua b/src/resty/parser.lua index 3360872..c01477f 100644 --- a/src/resty/parser.lua +++ b/src/resty/parser.lua @@ -14,6 +14,7 @@ local STATES = { none = 0, finish = 1, colon = 2, + asterisk = 3 } function Parser.new(path) @@ -28,6 +29,9 @@ function Parser.new(path) end -- /info/:user/project/:sub -> ["/info/", ":user", "/project/", ":sub"] +-- /api/v1/pay/* +-- /api/v1/pay/*path + -- /users/:id/xxx.* function Parser.next(self) @@ -52,6 +56,15 @@ function Parser.next(self) print("return: ", sub(self.path, anchor, self.pos - 1)) end return sub(self.path, anchor, self.pos - 1) + elseif c == BYTE_ASTERISK then + self.state = STATES.asterisk + local anchor = self.anchor + self.anchor = self.pos + -- TODO 下一次进入的时候还是重复的 + if debug then + print("return: ", sub(self.path, anchor, self.pos - 1)) + end + return sub(self.path, anchor, self.pos - 1) end elseif self.state == STATES.colon then if c == BYTE_SLASH then @@ -63,6 +76,8 @@ function Parser.next(self) end return sub(self.path, anchor, self.pos - 1) end + elseif self.state == STATES.asterisk then + -- do nothing end self.pos = self.pos + 1 end diff --git a/src/resty/parser_test.lua b/src/resty/parser_test.lua deleted file mode 100644 index ae8ed6f..0000000 --- a/src/resty/parser_test.lua +++ /dev/null @@ -1,29 +0,0 @@ -local P = require "parser" - - -local function test1() - local parser = P.new("/info/:user/project/:sub") - local token, err = parser:next() - assert(token == "/info/") - local token, err = parser:next() - assert(token == ":user") - local token, err = parser:next() - assert(token == "/project/") - local token, err = parser:next() - assert(token == ":sub") - local token, err = parser:next() - assert(token == nil) -end - -local function test2() - local mobdebug = require "mobdebug" - mobdebug.start("localhost", 28172) - local parser = P.new("/") - local token, err = parser:next() - assert(token == "/") - local token, err = parser:next() - assert(token == nil) -end - -test1() -test2() \ No newline at end of file diff --git a/src/resty/router.lua b/src/resty/router.lua index be01b4d..e0b33da 100644 --- a/src/resty/router.lua +++ b/src/resty/router.lua @@ -58,6 +58,9 @@ end -- function Router:match(path, ctx) ctx = ctx or EMPTY + if ctx.matched then + clear_table(ctx.matched) + end -- dirty Implementation local routes = self.static[path] if routes then diff --git a/src/resty/sample.lua b/src/resty/sample.lua index 63da4b6..b6824d1 100644 --- a/src/resty/sample.lua +++ b/src/resty/sample.lua @@ -104,12 +104,13 @@ local function test_example() local radix = require("router-tree") local rx = radix.new({ { - paths = {"/name/:name/id/:id"}, + paths = {"/name/*name"}, metadata = "metadata /name", }, }) - local ctx = { matched = {} } - print(rx:match("/name/json/id/1", ctx)) + + local ctx = {matched = {}} + local handler = rx:match("/name/", ctx) print(inspect(ctx)) end diff --git a/src/resty/trie.lua b/src/resty/trie.lua index dc23340..712a4b6 100644 --- a/src/resty/trie.lua +++ b/src/resty/trie.lua @@ -15,6 +15,7 @@ local clear_table = utils.clear_table local EMPTY = {} local BYTE_SLASH = byte("/") local BYTE_COLON = byte(":") +local BYTE_ASTERISK = byte("*") local TrieNode = {} @@ -25,6 +26,7 @@ local TYPES = { STATIC = 0, ROOT = 1, PARAM = 2, + CATCH_ALL = 3, } local debug = true @@ -55,11 +57,12 @@ local function insert_child(node, path, value) --print(token) node.path = token node.path_n = #token + node.type = TYPES.STATIC if byte(token) == BYTE_COLON then -- wildcard node.type = TYPES.PARAM - else - node.type = TYPES.STATIC + elseif byte(token) == BYTE_ASTERISK then + node.type = TYPES.CATCH_ALL end token = parser:next() if token then @@ -142,9 +145,6 @@ end local paths = {} function TrieNode:get(path, ctx) - if path == "/name/json/id/1" then - utils.breakpoint() - end local paths_i = 1 clear_table(paths) while true do @@ -211,6 +211,24 @@ function TrieNode:get(path, ctx) end return self.value end + + elseif self.type == TYPES.CATCH_ALL then + -- 前缀匹配 + if self.value then + paths[paths_i] = self.path + paths_i = paths_i + 1 + if ctx and ctx.matched then + local param_name = sub(self.path, 2) + if #param_name == 0 then + param_name = ":ext" + end + ctx.matched[param_name] = path + ctx.matched["_path"] = table.concat(paths) + end + return self.value + end + else + error("???") end end elseif path == prefix then @@ -220,6 +238,21 @@ function TrieNode:get(path, ctx) end return self.value end + + for _, child in ipairs(self.children or EMPTY) do + if child.type == TYPES.CATCH_ALL then + if ctx and ctx.matched then + local param_name = sub(child.path, 2) + if #param_name == 0 then + param_name = ":ext" + end + ctx.matched[param_name] = "" + paths[paths_i] = child.path + ctx.matched["_path"] = table.concat(paths) + end + return child.value + end + end end return nil