From 9b2a77ecae8e3a5ece94fe8d9f1a6e4fe568a48b Mon Sep 17 00:00:00 2001 From: Indrek Juhkam Date: Tue, 11 Aug 2026 11:58:53 +0300 Subject: [PATCH] feat: support the HTTP QUERY method (RFC 10008) QUERY is a safe, idempotent request method that carries the query in the request content instead of the URI. Add it to the method bitmask table so routes can match on it, and correct the README method list, which was also missing PURGE. --- README.md | 2 +- lib/resty/radixtree.lua | 2 +- t/sanity.t | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index af3ced3..bb356b4 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Each route can have the following attributes: | paths | Required | List of request paths to match the route. By default does a full match. Adding `*` at the end will result in prefix match. For example, `/foo*` can match requests with paths `/foo/bar` and `/foo/car/far`. | {"/", "/foo", "/bar/\*"} | | hosts | Optional | List of host addresses to match the route. Supports wildcards. For example `*.bar.com` can match `foo.bar.com` and `car.bar.com`. | {"foo.com", "\*.bar.com"} | | remote_addrs | Optional | List of remote addresses (IPv4 or IPv6) to match the route. Supports CIDR format. | {"127.0.0.1", "192.0.0.0/8", "::1", "fe80::/32"} | -| methods | Optional | List of HTTP methods to match the route. Valid values: "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT" and "TRACE". | {"GET", "POST"} | +| methods | Optional | List of HTTP methods to match the route. Valid values: "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE" and "QUERY". | {"GET", "POST"} | | vars | Optional | DSL to evaluate with the provided `opts.vars` or `ngx.var`. See: [lua-resty-expr](https://github.com/api7/lua-resty-expr#new). | {{"arg_name", "==", "json"}, {"arg_age", ">", 18}} | | filter_fun | Optional | User defined filter function to match the route. Can be used for custom matching scenarios. `vars` and `opts` will be passed to the function when matching a route. | function(vars) return vars["arg_name"] == "json" end | | priority | Optional | Route priority. Defaults to 0. | priority = 100 | diff --git a/lib/resty/radixtree.lua b/lib/resty/radixtree.lua index 5be3463..c3ce8d1 100644 --- a/lib/resty/radixtree.lua +++ b/lib/resty/radixtree.lua @@ -127,7 +127,7 @@ ffi_cdef[[ local METHODS = {} for i, name in ipairs({"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", - "OPTIONS", "CONNECT", "TRACE", "PURGE"}) do + "OPTIONS", "CONNECT", "TRACE", "PURGE", "QUERY"}) do METHODS[name] = bit.lshift(1, i - 1) -- ngx.log(ngx.WARN, "name: ", name, " val: ", METHODS[name]) end diff --git a/t/sanity.t b/t/sanity.t index 931d837..0e77ec6 100644 --- a/t/sanity.t +++ b/t/sanity.t @@ -382,7 +382,45 @@ nil -=== TEST 11: Iterator +=== TEST 11: method: QUERY +--- config + location /t { + content_by_lua_block { + local radix = require("resty.radixtree") + local rx = radix.new({ + { + paths = {"/aa"}, + methods = {"QUERY"}, + metadata = "metadata /aa", + }, + { + paths = {"/aa*"}, + methods = {"PUT"}, + metadata = "metadata /aa*", + } + }) + + ngx.say(rx:match("/aa", {method = "QUERY"})) + ngx.say(rx:match("/aa/bb", {method = "QUERY"})) + + + ngx.say(rx:match("/aa", {method = "GET"})) + ngx.say(rx:match("/aa/bb", {method = "GET"})) + } + } +--- request +GET /t +--- no_error_log +[error] +--- response_body +metadata /aa +nil +nil +nil + + + +=== TEST 12: Iterator --- config location /t { content_by_lua_block {