You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/03/31 01:40:43 UTC

[apisix] branch release/2.10 updated: feat: release 2.10.5 (#6752)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch release/2.10
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/release/2.10 by this push:
     new e40197c  feat: release 2.10.5 (#6752)
e40197c is described below

commit e40197c814b285e1779dcf4d65b41f840d06e675
Author: leslie <59...@users.noreply.github.com>
AuthorDate: Thu Mar 31 09:40:38 2022 +0800

    feat: release 2.10.5 (#6752)
    
    Co-authored-by: qizhendong <qi...@cmss.chinamobile.com>
    Co-authored-by: flynn.yu <fl...@ucloud.cn>
    Co-authored-by: spacewander <sp...@gmail.com>
    Co-authored-by: tangzhenhuang <ta...@cvte.com>
    Co-authored-by: 高亮亮 <gl...@alibaba-inc.com>
    Co-authored-by: seven dickens <la...@163.com>
    Co-authored-by: Zhendong Qi <88...@users.noreply.github.com>
    Co-authored-by: 大仁庄小余 <sh...@gmail.com>
    Co-authored-by: 疯狂的马骝 <tz...@qq.com>
    Co-authored-by: 罗泽轩 <sp...@gmail.com>
    Co-authored-by: Gaoll <lx...@126.com>
    Co-authored-by: 吴治国 <ch...@startdt.com>
---
 CHANGELOG.md                                       |  17 ++
 apisix/core/json.lua                               |   4 +-
 apisix/core/request.lua                            |   4 +-
 apisix/core/table.lua                              |   5 +
 apisix/core/version.lua                            |   2 +-
 apisix/plugins/example-plugin.lua                  |   2 +-
 apisix/plugins/hmac-auth.lua                       |   9 +
 apisix/plugins/limit-req.lua                       |   2 +-
 apisix/plugins/proxy-rewrite.lua                   |  44 +++--
 apisix/plugins/request-id.lua                      |   5 +-
 apisix/plugins/request-validation.lua              |  25 ++-
 apisix/plugins/traffic-split.lua                   |   1 +
 apisix/utils/batch-processor.lua                   |  22 ++-
 docs/en/latest/config.json                         |   2 +-
 docs/en/latest/how-to-build.md                     |  14 +-
 docs/en/latest/plugins/proxy-rewrite.md            |   1 +
 docs/zh/latest/CHANGELOG.md                        |  17 ++
 docs/zh/latest/config.json                         |   2 +-
 docs/zh/latest/how-to-build.md                     |  14 +-
 docs/zh/latest/plugins/proxy-rewrite.md            |   1 +
 ...-master-0.rockspec => apisix-2.10.5-0.rockspec} |   7 +-
 rockspec/apisix-master-0.rockspec                  |   3 +-
 t/core/request.t                                   | 108 ++++++-----
 t/core/table.t                                     |  25 +++
 t/lib/server.lua                                   |   1 +
 t/node/upstream-domain.t                           |  48 +++++
 t/plugin/error-log-logger-skywalking.t             |   2 +-
 t/plugin/hmac-auth3.t                              |  61 +++++++
 t/plugin/prometheus3.t                             | 121 +++++++++++-
 t/plugin/proxy-rewrite3.t                          | 202 +++++++++++++++++++++
 t/plugin/request-id.t                              | 123 ++++++-------
 t/plugin/request-validation.t                      |  56 ++++++
 t/plugin/{prometheus3.t => request-validation2.t}  |  91 +++++-----
 t/plugin/serverless.t                              |  50 +++++
 t/plugin/traffic-split5.t                          | 106 +++++++++++
 35 files changed, 966 insertions(+), 231 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cff0d88..4af5d46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@ title: Changelog
 
 ## Table of Contents
 
+- [2.10.5](#2105)
 - [2.10.4](#2104)
 - [2.10.3](#2103)
 - [2.10.2](#2102)
@@ -50,6 +51,22 @@ title: Changelog
 - [0.7.0](#070)
 - [0.6.0](#060)
 
+## 2.10.5
+
+### Bugfix
+
+- fix: resolve unambiguous fully-qualified domain name(FQDN), i.e. ended in a dot [#5999](https://github.com/apache/apisix/pull/5999)
+- fix(batch-processor): the prometheus labels are inconsistent when using the same batch-processor instance on multi plugins [#6055](https://github.com/apache/apisix/pull/6055)
+- fix(request-id): if the header_name is already present in the request uuid_val include_in_response should keep source [#6160](https://github.com/apache/apisix/pull/6160)
+- fix(traffic-split): failed to match rule when the first rule failed [#6292](https://github.com/apache/apisix/pull/6292)
+- fix(proxy-rewrite): when conf.headers are missing,conf.method can make effect [#6300](https://github.com/apache/apisix/pull/6300)
+- fix(hmac-auth): hmac-auth plugin sort array param [#6314](https://github.com/apache/apisix/pull/6314)
+- fix(core.request): should not limit the header number [#6379](https://github.com/apache/apisix/pull/6379)
+- fix(request-validation): should not limit the urlencoded post args number [#6396](https://github.com/apache/apisix/pull/6396)
+- fix(deps): use fixed version of net-url [#6446](https://github.com/apache/apisix/pull/6446)
+- fix(core.table): deepcopy doesn't copy the metatable [#6623](https://github.com/apache/apisix/pull/6623)
+- fix(request-validate): handle duplicate key in JSON [#6625](https://github.com/apache/apisix/pull/6625)
+
 ## 2.10.4
 
 ### Bugfix
diff --git a/apisix/core/json.lua b/apisix/core/json.lua
index 7fad872..8756b2f 100644
--- a/apisix/core/json.lua
+++ b/apisix/core/json.lua
@@ -14,7 +14,8 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
-local json_encode = require("cjson.safe").encode
+local cjson = require("cjson.safe")
+local json_encode = cjson.encode
 local clear_tab = require("table.clear")
 local ngx = ngx
 local tostring = tostring
@@ -25,6 +26,7 @@ local cached_tab = {}
 
 local _M = {
     version = 0.1,
+    array_mt = cjson.array_mt,
     decode = require("cjson.safe").decode,
 }
 
diff --git a/apisix/core/request.lua b/apisix/core/request.lua
index 4adac0a..6f5746a 100644
--- a/apisix/core/request.lua
+++ b/apisix/core/request.lua
@@ -42,7 +42,7 @@ local function _headers(ctx)
     end
     local headers = ctx.headers
     if not headers then
-        headers = get_headers()
+        headers = get_headers(0)
         ctx.headers = headers
     end
 
@@ -179,8 +179,6 @@ end
 
 
 function _M.get_body(max_size, ctx)
-    -- TODO: improve the check with set client_max_body dynamically
-    -- which requires to change Nginx source code
     if max_size then
         local var = ctx and ctx.var or ngx.var
         local content_length = tonumber(var.http_content_length)
diff --git a/apisix/core/table.lua b/apisix/core/table.lua
index d0bc1b2..2af50b1 100644
--- a/apisix/core/table.lua
+++ b/apisix/core/table.lua
@@ -114,6 +114,11 @@ do
             end
         end
 
+        local mt = getmetatable(orig)
+        if mt ~= nil then
+            setmetatable(copy, mt)
+        end
+
         return copy
     end
 
diff --git a/apisix/core/version.lua b/apisix/core/version.lua
index 2b1c5b2..0e9aa07 100644
--- a/apisix/core/version.lua
+++ b/apisix/core/version.lua
@@ -15,5 +15,5 @@
 -- limitations under the License.
 --
 return {
-    VERSION = "2.10.4"
+    VERSION = "2.10.5"
 }
diff --git a/apisix/plugins/example-plugin.lua b/apisix/plugins/example-plugin.lua
index f1d1cc3..07e8024 100644
--- a/apisix/plugins/example-plugin.lua
+++ b/apisix/plugins/example-plugin.lua
@@ -44,7 +44,7 @@ local plugin_name = "example-plugin"
 
 local _M = {
     version = 0.1,
-    priority = 0,        -- TODO: add a type field, may be a good idea
+    priority = 0,
     name = plugin_name,
     schema = schema,
     metadata_schema = metadata_schema,
diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua
index 73cb947..9a63db9 100644
--- a/apisix/plugins/hmac-auth.lua
+++ b/apisix/plugins/hmac-auth.lua
@@ -244,7 +244,16 @@ local function generate_signature(ctx, secret_key, params)
 
             -- whether to encode the uri parameters
             if type(param) == "table" then
+                local vals = {}
                 for _, val in pairs(param) do
+                    if type(val) == "boolean" then
+                        val = ""
+                    end
+                    core.table.insert(vals, val)
+                end
+                core.table.sort(vals)
+
+                for _, val in pairs(vals) do
                     core.table.insert(query_tab, encode_or_not(key) .. "=" .. encode_or_not(val))
                 end
             else
diff --git a/apisix/plugins/limit-req.lua b/apisix/plugins/limit-req.lua
index bd7ef4a..115c8e1 100644
--- a/apisix/plugins/limit-req.lua
+++ b/apisix/plugins/limit-req.lua
@@ -50,7 +50,7 @@ local schema = {
 
 local _M = {
     version = 0.1,
-    priority = 1001,        -- TODO: add a type field, may be a good idea
+    priority = 1001,
     name = plugin_name,
     schema = schema,
 }
diff --git a/apisix/plugins/proxy-rewrite.lua b/apisix/plugins/proxy-rewrite.lua
index 4ad7c07..c1d7ec4 100644
--- a/apisix/plugins/proxy-rewrite.lua
+++ b/apisix/plugins/proxy-rewrite.lua
@@ -24,6 +24,19 @@ local re_sub      = ngx.re.sub
 local sub_str     = string.sub
 local str_find    = core.string.find
 
+local switch_map = {GET = ngx.HTTP_GET, POST = ngx.HTTP_POST, PUT = ngx.HTTP_PUT,
+                    HEAD = ngx.HTTP_HEAD, DELETE = ngx.HTTP_DELETE,
+                    OPTIONS = ngx.HTTP_OPTIONS, MKCOL = ngx.HTTP_MKCOL,
+                    COPY = ngx.HTTP_COPY, MOVE = ngx.HTTP_MOVE,
+                    PROPFIND = ngx.HTTP_PROPFIND, LOCK = ngx.HTTP_LOCK,
+                    UNLOCK = ngx.HTTP_UNLOCK, PATCH = ngx.HTTP_PATCH,
+                    TRACE = ngx.HTTP_TRACE,
+                }
+local schema_method_enum = {}
+for key in pairs(switch_map) do
+    core.table.insert(schema_method_enum, key)
+end
+
 local schema = {
     type = "object",
     properties = {
@@ -34,6 +47,11 @@ local schema = {
             maxLength   = 4096,
             pattern     = [[^\/.*]],
         },
+        method = {
+            description = "proxy route method",
+            type        = "string",
+            enum        = schema_method_enum
+        },
         regex_uri = {
             description = "new uri that substitute from client uri " ..
                           "for upstream, lower priority than uri property",
@@ -177,24 +195,24 @@ function _M.rewrite(conf, ctx)
         ctx.var.upstream_uri = upstream_uri
     end
 
-    if not conf.headers then
-        return
-    end
+    if conf.headers then
+        if not conf.headers_arr then
+            conf.headers_arr = {}
 
-    -- reform header from object into array, so can avoid use pairs,
-    -- which is NYI
-    if not conf.headers_arr then
-        conf.headers_arr = {}
+            for field, value in pairs(conf.headers) do
+                core.table.insert_tail(conf.headers_arr, field, value)
+            end
+        end
 
-        for field, value in pairs(conf.headers) do
-            core.table.insert_tail(conf.headers_arr, field, value)
+        local field_cnt = #conf.headers_arr
+        for i = 1, field_cnt, 2 do
+            core.request.set_header(ctx, conf.headers_arr[i],
+                                    core.utils.resolve_var(conf.headers_arr[i+1], ctx.var))
         end
     end
 
-    local field_cnt = #conf.headers_arr
-    for i = 1, field_cnt, 2 do
-        core.request.set_header(ctx, conf.headers_arr[i],
-                                core.utils.resolve_var(conf.headers_arr[i+1], ctx.var))
+    if conf.method then
+        ngx.req.set_method(switch_map[conf.method])
     end
 end
 
diff --git a/apisix/plugins/request-id.lua b/apisix/plugins/request-id.lua
index 57512dc..183374e 100644
--- a/apisix/plugins/request-id.lua
+++ b/apisix/plugins/request-id.lua
@@ -211,9 +211,12 @@ end
 
 function _M.rewrite(conf, ctx)
     local headers = ngx.req.get_headers()
-    local uuid_val = get_request_id(conf.algorithm)
+    local uuid_val
     if not headers[conf.header_name] then
+        uuid_val = get_request_id(conf.algorithm)
         core.request.set_header(ctx, conf.header_name, uuid_val)
+    else
+        uuid_val = headers[conf.header_name]
     end
 
     if conf.include_in_response then
diff --git a/apisix/plugins/request-validation.lua b/apisix/plugins/request-validation.lua
index a2af3cb..267fb11 100644
--- a/apisix/plugins/request-validation.lua
+++ b/apisix/plugins/request-validation.lua
@@ -75,8 +75,8 @@ function _M.check_schema(conf)
 end
 
 
-function _M.rewrite(conf)
-    local headers = ngx.req.get_headers()
+function _M.rewrite(conf, ctx)
+    local headers = core.request.headers(ctx)
 
     if conf.header_schema then
         local ok, err = core.schema.check(conf.header_schema, headers)
@@ -103,21 +103,32 @@ function _M.rewrite(conf)
             body = fd:read('*a')
         end
 
+        local body_is_json = true
         if headers["content-type"] == "application/x-www-form-urlencoded" then
-            req_body, error = ngx.decode_args(body)
+            -- use 0 to avoid truncated result and keep the behavior as the
+            -- same as other platforms
+            req_body, error = ngx.decode_args(body, 0)
+            body_is_json = false
         else -- JSON as default
             req_body, error = core.json.decode(body)
         end
 
         if not req_body then
-          core.log.error('failed to decode the req body', error)
-          return 400, error
+            core.log.error('failed to decode the req body: ', error)
+            return 400, error
         end
 
         local ok, err = core.schema.check(conf.body_schema, req_body)
         if not ok then
-          core.log.error("req schema validation failed", err)
-          return 400, err
+            core.log.error("req schema validation failed: ", err)
+            return 400, err
+        end
+
+        if body_is_json then
+            -- ensure the JSON we check is the JSON we pass to the upstream,
+            -- see https://bishopfox.com/blog/json-interoperability-vulnerabilities
+            req_body = core.json.encode(req_body)
+            ngx.req.set_body_data(req_body)
         end
     end
 end
diff --git a/apisix/plugins/traffic-split.lua b/apisix/plugins/traffic-split.lua
index 028cc97..9ba0997 100644
--- a/apisix/plugins/traffic-split.lua
+++ b/apisix/plugins/traffic-split.lua
@@ -238,6 +238,7 @@ function _M.access(conf, ctx)
 
     for _, rule in ipairs(conf.rules) do
         if not rule.match then
+            match_passed = true
             weighted_upstreams = rule.weighted_upstreams
             break
         end
diff --git a/apisix/utils/batch-processor.lua b/apisix/utils/batch-processor.lua
index 0de2fc6..59efa49 100644
--- a/apisix/utils/batch-processor.lua
+++ b/apisix/utils/batch-processor.lua
@@ -53,6 +53,15 @@ local function schedule_func_exec(self, delay, batch)
 end
 
 
+local function set_metrics(self, count)
+    -- add batch metric for every route
+    if batch_metrics and self.name and self.route_id and self.server_addr then
+        self.label = {self.name, self.route_id, self.server_addr}
+        batch_metrics:set(count, self.label)
+    end
+end
+
+
 function execute_func(premature, self, batch)
     if premature then
         return
@@ -117,7 +126,7 @@ function batch_processor:new(func, config)
         return nil, err
     end
 
-    if not(type(func) == "function") then
+    if type(func) ~= "function" then
         return nil, "Invalid argument, arg #1 must be a function"
     end
 
@@ -159,11 +168,7 @@ function batch_processor:push(entry)
 
     local entries = self.entry_buffer.entries
     table.insert(entries, entry)
-    -- add batch metric for every route
-    if batch_metrics  then
-        self.label = {self.name, self.route_id, self.server_addr}
-        batch_metrics:set(#entries, self.label)
-    end
+    set_metrics(self, #entries)
 
     if #entries == 1 then
         self.first_entry_t = now()
@@ -189,10 +194,7 @@ function batch_processor:process_buffer()
             "buffercount[", #self.entry_buffer.entries ,"]")
         self.batch_to_process[#self.batch_to_process + 1] = self.entry_buffer
         self.entry_buffer = {entries = {}, retry_count = 0}
-        if batch_metrics then
-            self.label = {self.name, self.route_id, self.server_addr}
-            batch_metrics:set(0, self.label)
-        end
+        set_metrics(self, 0)
     end
 
     for _, batch in ipairs(self.batch_to_process) do
diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json
index ccce56f..73673b1 100644
--- a/docs/en/latest/config.json
+++ b/docs/en/latest/config.json
@@ -1,5 +1,5 @@
 {
-  "version": "2.10.4",
+  "version": "2.10.5",
   "sidebar": [
     {
       "type": "category",
diff --git a/docs/en/latest/how-to-build.md b/docs/en/latest/how-to-build.md
index bc31fad..39db15e 100644
--- a/docs/en/latest/how-to-build.md
+++ b/docs/en/latest/how-to-build.md
@@ -58,7 +58,7 @@ sudo yum install -y https://repos.apiseven.com/packages/centos/apache-apisix-rep
 This installation method is suitable for CentOS 7, please run the following command to install Apache APISIX.
 
 ```shell
-sudo yum install -y https://repos.apiseven.com/packages/centos/7/x86_64/apisix-2.10.4-0.el7.x86_64.rpm
+sudo yum install -y https://repos.apiseven.com/packages/centos/7/x86_64/apisix-2.10.5-0.el7.x86_64.rpm
 ```
 
 ### Installation via Docker
@@ -71,16 +71,16 @@ Please refer to: [Installing Apache APISIX with Helm Chart](https://github.com/a
 
 ### Installation via Source Release Package
 
-1. Create a directory named `apisix-2.10.4`.
+1. Create a directory named `apisix-2.10.5`.
 
   ```shell
-  mkdir apisix-2.10.4
+  mkdir apisix-2.10.5
   ```
 
 2. Download Apache APISIX Release source package.
 
   ```shell
-  wget https://downloads.apache.org/apisix/2.10.4/apache-apisix-2.10.4-src.tgz
+  wget https://downloads.apache.org/apisix/2.10.5/apache-apisix-2.10.5-src.tgz
   ```
 
   You can also download the Apache APISIX Release source package from the Apache APISIX website. The [Apache APISIX Official Website - Download Page](https://apisix.apache.org/downloads/) also provides source packages for Apache APISIX, APISIX Dashboard and APISIX Ingress Controller.
@@ -88,14 +88,14 @@ Please refer to: [Installing Apache APISIX with Helm Chart](https://github.com/a
 3. Unzip the Apache APISIX Release source package.
 
   ```shell
-  tar zxvf apache-apisix-2.10.4-src.tgz -C apisix-2.10.4
+  tar zxvf apache-apisix-2.10.5-src.tgz -C apisix-2.10.5
   ```
 
 4. Install the runtime dependent Lua libraries.
 
   ```shell
-  # Switch to the apisix-2.10.4 directory
-  cd apisix-2.10.4
+  # Switch to the apisix-2.10.5 directory
+  cd apisix-2.10.5
   # Create dependencies
   make deps
   # Install apisix command
diff --git a/docs/en/latest/plugins/proxy-rewrite.md b/docs/en/latest/plugins/proxy-rewrite.md
index f5adf1f..a538ecb 100644
--- a/docs/en/latest/plugins/proxy-rewrite.md
+++ b/docs/en/latest/plugins/proxy-rewrite.md
@@ -39,6 +39,7 @@ The `proxy-rewrite` is an upstream proxy information rewriting plugin, which sup
 | --------- | ------------- | ----------- | ------- | ----------------- | ------------------------------------------------------------ |
 | scheme    | string        | optional    | "http"  | ["http", "https"] | Upstream new `schema` forwarding protocol. This option is deprecated. It's recommended to set the proxy `scheme` in the Upstream object's `scheme` field instead.|
 | uri       | string        | optional    |         |                   | Upstream new `uri` forwarding address. Supports the use of [Nginx variables](https://nginx.org/en/docs/http/ngx_http_core_module.html). Variables must start with `$`, such as `$arg_name`. |
+| method    | string        | optional    |         | ["GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS","MKCOL", "COPY", "MOVE", "PROPFIND", "PROPFIND","LOCK", "UNLOCK", "PATCH", "TRACE"] | rewrite the HTTP method.|
 | regex_uri | array[string] | optional    |         |                   | Upstream new `uri` forwarding address. Use regular expression to match URL from client, when the match is successful, the URL template will be forwarded upstream. If the match is not successful, the URL from the client will be forwarded to the upstream. When `uri` and `regex_uri` are both exist, `uri` is used first. For example: [" ^/iresty/(.*)/(.*)/(.*)", "/$1-$2-$3"], the first element represents the matching re [...]
 | host      | string        | optional    |         |                   | Upstream new `host` forwarding address, example `iresty.com`. |
 | headers   | object        | optional    |         |                   | Forward to the new `headers` of the upstream, can set up multiple. If it exists, will rewrite the header, otherwise will add the header. You can set the corresponding value to an empty string to remove a header. Support the use of Nginx variables. Need to start with `$`, such as `client_addr: $remote_addr`: it means that the request header `client_addr` is the client IP. |
diff --git a/docs/zh/latest/CHANGELOG.md b/docs/zh/latest/CHANGELOG.md
index c0e696a..f9c56ab 100644
--- a/docs/zh/latest/CHANGELOG.md
+++ b/docs/zh/latest/CHANGELOG.md
@@ -23,6 +23,7 @@ title: CHANGELOG
 
 ## Table of Contents
 
+- [2.10.5](#2105)
 - [2.10.4](#2104)
 - [2.10.3](#2103)
 - [2.10.2](#2102)
@@ -50,6 +51,22 @@ title: CHANGELOG
 - [0.7.0](#070)
 - [0.6.0](#060)
 
+## 2.10.5
+
+### Bugfix
+
+- 支持解析不完整的全限定域名(FQDN) 例如: 以 `.` 结尾 [#5999](https://github.com/apache/apisix/pull/5999)
+- 修正同时启用 error-log-logger 和 prometheus 时报告 labels inconsistent 的问题 [#6055](https://github.com/apache/apisix/pull/6055)
+- 修正 request-id 插件 uuid_val 与 include_in_response 被覆写的问题 [#6160](https://github.com/apache/apisix/pull/6160)
+- 修正 traffic-split 首条规则失败时无法匹配的问题 [#6292](https://github.com/apache/apisix/pull/6292)
+- 修正 proxy-rewrite 中,当 conf.headers 缺失时,conf.method 不生效的问题 [#6300](https://github.com/apache/apisix/pull/6300)
+- 修正 hmac-auth 插件对参数排列的问题 [#6314](https://github.com/apache/apisix/pull/6314)
+- 修正 core.request 未读取请求中所有 header 的问题  [#6379](https://github.com/apache/apisix/pull/6379)
+- 修正 request-validation 未验证所有 urlencoded post 参数问题 [#6396](https://github.com/apache/apisix/pull/6396)
+- 修正 net-url 库未使用固定版本的问题 [#6446](https://github.com/apache/apisix/pull/6446)
+- deepcopy 没有复制 metatable [#6623](https://github.com/apache/apisix/pull/6623)
+- request-validate 修复对 JSON 里面重复键的处理 [#6625](https://github.com/apache/apisix/pull/6625)
+
 ## 2.10.4
 
 ### Bugfix
diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json
index 7a5fec7..980d5a2 100644
--- a/docs/zh/latest/config.json
+++ b/docs/zh/latest/config.json
@@ -1,5 +1,5 @@
 {
-  "version": "2.10.4",
+  "version": "2.10.5",
   "sidebar": [
     {
       "type": "category",
diff --git a/docs/zh/latest/how-to-build.md b/docs/zh/latest/how-to-build.md
index 7e72630..0c3f68c 100644
--- a/docs/zh/latest/how-to-build.md
+++ b/docs/zh/latest/how-to-build.md
@@ -58,7 +58,7 @@ sudo yum install -y https://repos.apiseven.com/packages/centos/apache-apisix-rep
 这种安装方式适用于 CentOS 7 操作系统,请运行以下命令安装 Apache APISIX。
 
 ```shell
-sudo yum install -y https://repos.apiseven.com/packages/centos/7/x86_64/apisix-2.10.4-0.el7.x86_64.rpm
+sudo yum install -y https://repos.apiseven.com/packages/centos/7/x86_64/apisix-2.10.5-0.el7.x86_64.rpm
 ```
 
 ### 通过 Docker 安装
@@ -71,16 +71,16 @@ sudo yum install -y https://repos.apiseven.com/packages/centos/7/x86_64/apisix-2
 
 ### 通过源码包安装
 
-1. 创建一个名为 `apisix-2.10.4` 的目录。
+1. 创建一个名为 `apisix-2.10.5` 的目录。
 
   ```shell
-  mkdir apisix-2.10.4
+  mkdir apisix-2.10.5
   ```
 
 2. 下载 Apache APISIX Release 源码包:
 
   ```shell
-  wget https://downloads.apache.org/apisix/2.10.4/apache-apisix-2.10.4-src.tgz
+  wget https://downloads.apache.org/apisix/2.10.5/apache-apisix-2.10.5-src.tgz
   ```
 
   您也可以通过 Apache APISIX 官网下载 Apache APISIX Release 源码包。 Apache APISIX 官网也提供了 Apache APISIX、APISIX Dashboard 和 APISIX Ingress Controller 的源码包,详情请参考[Apache APISIX 官网-下载页](https://apisix.apache.org/zh/downloads)。
@@ -88,14 +88,14 @@ sudo yum install -y https://repos.apiseven.com/packages/centos/7/x86_64/apisix-2
 3. 解压 Apache APISIX Release 源码包:
 
   ```shell
-  tar zxvf apache-apisix-2.10.4-src.tgz -C apisix-2.10.4
+  tar zxvf apache-apisix-2.10.5-src.tgz -C apisix-2.10.5
   ```
 
 4. 安装运行时依赖的 Lua 库:
 
   ```shell
-  # 切换到 apisix-2.10.4 目录
-  cd apisix-2.10.4
+  # 切换到 apisix-2.10.5 目录
+  cd apisix-2.10.5
   # 安装依赖
   LUAROCKS_SERVER=https://luarocks.cn make deps
   # 安装 apisix 命令
diff --git a/docs/zh/latest/plugins/proxy-rewrite.md b/docs/zh/latest/plugins/proxy-rewrite.md
index e544909..d30a270 100644
--- a/docs/zh/latest/plugins/proxy-rewrite.md
+++ b/docs/zh/latest/plugins/proxy-rewrite.md
@@ -39,6 +39,7 @@ proxy-rewrite 是上游代理信息重写插件,支持对 `scheme`、`uri`、`
 | --------- | ------------- | ----------- | ------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | scheme    | string        | 可选        | "http"  | ["http", "https"] | 不推荐使用。应该在 Upstream 的 scheme 字段设置上游的 scheme。
 | uri       | string        | 可选        |         |                   | 转发到上游的新 `uri` 地址。                                                                                                                                                                                                                                                                             |
+| method    | string        | 可选        |         | ["GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS","MKCOL", "COPY", "MOVE", "PROPFIND", "PROPFIND","LOCK", "UNLOCK", "PATCH", "TRACE"] | 将route的请求方法代理为该请求方法。                                                                                                                                                                                                                                                                             |
 | regex_uri | array[string] | 可选        |         |                   | 转发到上游的新 `uri` 地址, 使用正则表达式匹配来自客户端的uri,当匹配成功后使用模板替换转发到上游的uri, 未匹配成功时将客户端请求的uri转发至上游。当`uri`和`regex_uri`同时存在时,`uri`优先被使用。例如:["^/iresty/(.*)/(.*)/(.*)","/$1-$2-$3"] 第一个元素代表匹配来自客户端请求的uri正则表达式,第二个元素代表匹配成功后转发到上游的uri模板。 |
 | host      | string        | 可选        |         |                   | 转发到上游的新 `host` 地址,例如:`iresty.com` 。                                                                                                                                                                                                                                                        |
 | headers   | object        | 可选        |         |                   | 转发到上游的新`headers`,可以设置多个。头信息如果存在将重写,不存在则添加。想要删除某个 header 的话,把对应的值设置为空字符串即可。支持使用 Nginx 的变量,需要以 `$` 开头,如 `client_addr: $remote_addr` :表示请求头 `client_addr` 为客户端IP。                                                                               |
diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-2.10.5-0.rockspec
similarity index 96%
copy from rockspec/apisix-master-0.rockspec
copy to rockspec/apisix-2.10.5-0.rockspec
index 354ebfe..9f5447c 100644
--- a/rockspec/apisix-master-0.rockspec
+++ b/rockspec/apisix-2.10.5-0.rockspec
@@ -16,12 +16,12 @@
 --
 
 package = "apisix"
-version = "master-0"
+version = "2.10.5-0"
 supported_platforms = {"linux", "macosx"}
 
 source = {
     url = "git://github.com/apache/apisix",
-    branch = "master",
+    branch = "2.10.5",
 }
 
 description = {
@@ -32,7 +32,7 @@ description = {
 
 dependencies = {
     "lua-resty-ctxdump = 0.1-0",
-    "lua-resty-dns-client = 5.2.0",
+    "lua-resty-dns-client = 5.2.3",
     "lua-resty-template = 2.0",
     "lua-resty-etcd = 1.5.4",
     "api7-lua-resty-http = 0.2.0",
@@ -71,6 +71,7 @@ dependencies = {
     "casbin = 1.26.0",
     "api7-snowflake = 2.0-1",
     "inspect == 3.1.1",
+    "net-url = 0.9-1",
 }
 
 build = {
diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec
index 354ebfe..7fc4a7d 100644
--- a/rockspec/apisix-master-0.rockspec
+++ b/rockspec/apisix-master-0.rockspec
@@ -32,7 +32,7 @@ description = {
 
 dependencies = {
     "lua-resty-ctxdump = 0.1-0",
-    "lua-resty-dns-client = 5.2.0",
+    "lua-resty-dns-client = 5.2.3",
     "lua-resty-template = 2.0",
     "lua-resty-etcd = 1.5.4",
     "api7-lua-resty-http = 0.2.0",
@@ -71,6 +71,7 @@ dependencies = {
     "casbin = 1.26.0",
     "api7-snowflake = 2.0-1",
     "inspect == 3.1.1",
+    "net-url = 0.9-1",
 }
 
 build = {
diff --git a/t/core/request.t b/t/core/request.t
index fb4c4c6..76926e2 100644
--- a/t/core/request.t
+++ b/t/core/request.t
@@ -21,13 +21,25 @@ no_long_string();
 no_root_location();
 log_level("info");
 
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+});
+
 run_tests;
 
 __DATA__
 
 === TEST 1: get_ip
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -51,20 +63,16 @@ __DATA__
             ngx.say(ip)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 127.0.0.1
---- no_error_log
-[error]
 
 
 
 === TEST 2: get_ip
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -88,20 +96,16 @@ X-Real-IP: 10.0.0.1
             ngx.say(ip)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 127.0.0.1
---- no_error_log
-[error]
 
 
 
 === TEST 3: get_ip and X-Forwarded-For
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Forwarded-For;
 
         set_real_ip_from 0.0.0.0/0;
@@ -125,20 +129,16 @@ X-Real-IP: 10.0.0.1
             ngx.say(ip)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Forwarded-For: 10.0.0.1
 --- response_body
 127.0.0.1
---- no_error_log
-[error]
 
 
 
 === TEST 4: get_remote_client_ip
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -162,20 +162,16 @@ X-Forwarded-For: 10.0.0.1
             ngx.say(ip)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 10.0.0.1
---- no_error_log
-[error]
 
 
 
 === TEST 5: get_remote_client_ip and X-Forwarded-For
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Forwarded-For;
         set_real_ip_from 0.0.0.0/0;
         set_real_ip_from ::/0;
@@ -198,20 +194,16 @@ X-Real-IP: 10.0.0.1
             ngx.say(ip)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Forwarded-For: 10.0.0.1
 --- response_body
 10.0.0.1
---- no_error_log
-[error]
 
 
 
 === TEST 6: get_host
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -235,20 +227,16 @@ X-Forwarded-For: 10.0.0.1
             ngx.say(host)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 localhost
---- no_error_log
-[error]
 
 
 
 === TEST 7: get_scheme
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -272,20 +260,16 @@ localhost
             ngx.say(scheme)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 http
---- no_error_log
-[error]
 
 
 
 === TEST 8: get_port
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -309,20 +293,16 @@ http
             ngx.say(port)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 1984
---- no_error_log
-[error]
 
 
 
 === TEST 9: get_http_version
 --- config
-    location = /hello {
+    location /t {
         real_ip_header X-Real-IP;
 
         set_real_ip_from 0.0.0.0/0;
@@ -346,20 +326,16 @@ X-Real-IP: 10.0.0.1
             ngx.say(http_version)
         }
     }
---- request
-GET /hello
 --- more_headers
 X-Real-IP: 10.0.0.1
 --- response_body
 1.1
---- no_error_log
-[error]
 
 
 
 === TEST 10: set header
 --- config
-    location = /hello {
+    location /t {
         content_by_lua_block {
             local core = require("apisix.core")
             ngx.ctx.api_ctx = {}
@@ -371,10 +347,42 @@ X-Real-IP: 10.0.0.1
             ngx.say(h2)
         }
     }
---- request
-GET /hello
 --- response_body
 nil
 t
---- no_error_log
-[error]
+
+
+
+=== TEST 11: get_method
+--- config
+    location /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.request.get_method())
+        }
+    }
+--- request
+POST /t
+--- response_body
+POST
+
+
+
+=== TEST 12: get header
+--- config
+    location /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            ngx.say(core.request.header(ngx.ctx, "X-101"))
+        }
+    }
+--- more_headers eval
+my $i = 1;
+my $s;
+while ($i <= 101) {
+    $s .= "X-$i:$i\n";
+    $i++;
+}
+$s
+--- response_body
+101
diff --git a/t/core/table.t b/t/core/table.t
index 2e1b233..c5e18a5 100644
--- a/t/core/table.t
+++ b/t/core/table.t
@@ -204,3 +204,28 @@ ok
 GET /t
 --- no_error_log
 [error]
+
+
+
+=== TEST 7: deepcopy should keep metatable
+--- config
+    location /t {
+        content_by_lua_block {
+            local core = require("apisix.core")
+            local deepcopy = core.table.deepcopy
+            local t = setmetatable({}, core.json.array_mt)
+            local actual = core.json.encode(deepcopy(t))
+            local expect = "[]"
+            if actual ~= expect then
+                ngx.say("expect ", expect, ", actual ", actual)
+                return
+            end
+            ngx.say("ok")
+        }
+    }
+--- request
+GET /t
+--- response_body
+ok
+--- no_error_log
+[error]
diff --git a/t/lib/server.lua b/t/lib/server.lua
index d72a308..02550d6 100644
--- a/t/lib/server.lua
+++ b/t/lib/server.lua
@@ -76,6 +76,7 @@ function _M.plugin_proxy_rewrite()
     ngx.say("uri: ", ngx.var.uri)
     ngx.say("host: ", ngx.var.host)
     ngx.say("scheme: ", ngx.var.scheme)
+    ngx.log(ngx.WARN, "plugin_proxy_rewrite get method: ", ngx.req.get_method())
 end
 
 
diff --git a/t/node/upstream-domain.t b/t/node/upstream-domain.t
index 63069fd..2cf71dd 100644
--- a/t/node/upstream-domain.t
+++ b/t/node/upstream-domain.t
@@ -399,3 +399,51 @@ GET /t
 1980, 1981, 1981
 --- no_error_log
 [error]
+
+
+
+=== TEST 15: set route(with upstream)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "nodes": {
+                            "foo.com.": 0,
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin",
+                        "desc": "new upstream"
+                    },
+                    "service_id": "1"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 16: hit routes, parse the domain of upstream node
+--- request
+GET /hello
+--- response_body
+hello world
+--- error_log eval
+qr/dns resolver domain: foo.com. to \d+.\d+.\d+.\d+/
+--- no_error_log
+[error]
diff --git a/t/plugin/error-log-logger-skywalking.t b/t/plugin/error-log-logger-skywalking.t
index 0e5f5dc..16572a5 100644
--- a/t/plugin/error-log-logger-skywalking.t
+++ b/t/plugin/error-log-logger-skywalking.t
@@ -84,7 +84,7 @@ plugins:
 GET /tg
 --- response_body
 --- error_log eval
-qr/.*\[lua\] batch-processor.lua:63: Batch Processor\[error-log-logger\] failed to process entries: error while sending data to skywalking\[http:\/\/127.0.0.1:1988\/log\] connection refused, context: ngx.timer/
+qr/.*\[lua\] batch-processor.lua:72: Batch Processor\[error-log-logger\] failed to process entries: error while sending data to skywalking\[http:\/\/127.0.0.1:1988\/log\] connection refused, context: ngx.timer/
 --- wait: 3
 
 
diff --git a/t/plugin/hmac-auth3.t b/t/plugin/hmac-auth3.t
index bc66dc6..d90dcbc 100644
--- a/t/plugin/hmac-auth3.t
+++ b/t/plugin/hmac-auth3.t
@@ -532,3 +532,64 @@ plugin_attr:
     }
 --- response_body
 passed
+
+
+
+=== TEST 10: Test sort table param.
+--- config
+    location /t {
+        content_by_lua_block {
+            local ngx_time = ngx.time
+            local ngx_http_time = ngx.http_time
+            local core = require("apisix.core")
+            local t = require("lib.test_admin")
+            local hmac = require("resty.hmac")
+            local ngx_encode_base64 = ngx.encode_base64
+
+            local secret_key = "my-secret-key"
+            local timestamp = ngx_time()
+            local gmt = ngx_http_time(timestamp)
+            local access_key = "my-access-key"
+            local custom_header_a = "asld$%dfasf"
+            local custom_header_b = "23879fmsldfk"
+            local body = "{\"name\": \"world\"}"
+
+            local signing_string = {
+                "POST",
+                "/hello",
+                "a=&a=1&a=2&a1a=123&c=&name=123",
+                access_key,
+                gmt,
+                "x-custom-header-a:" .. custom_header_a,
+                "x-custom-header-b:" .. custom_header_b
+            }
+            signing_string = core.table.concat(signing_string, "\n") .. "\n"
+            core.log.info("signing_string:", signing_string)
+
+            local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string)
+            local body_digest = hmac:new(secret_key, hmac.ALGOS.SHA256):final(body)
+
+            core.log.info("signature:", ngx_encode_base64(signature))
+            local headers = {}
+            headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
+            headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
+            headers["Date"] = gmt
+            headers["X-HMAC-DIGEST"] = ngx_encode_base64(body_digest)
+            headers["X-HMAC-ACCESS-KEY"] = access_key
+            headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b"
+            headers["x-custom-header-a"] = custom_header_a
+            headers["x-custom-header-b"] = custom_header_b
+
+            local code, body = t.test('/hello?c=&a1a=123&name=123&a&a=2&a=1',
+                ngx.HTTP_POST,
+                body,
+                nil,
+                headers
+            )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
diff --git a/t/plugin/prometheus3.t b/t/plugin/prometheus3.t
index eb11d37..4f3df1f 100644
--- a/t/plugin/prometheus3.t
+++ b/t/plugin/prometheus3.t
@@ -30,6 +30,19 @@ repeat_each(1);
 no_long_string();
 no_shuffle();
 no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+});
+
 run_tests;
 
 __DATA__
@@ -74,12 +87,8 @@ __DATA__
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
@@ -88,5 +97,105 @@ passed
 ["GET /hello", "GET /apisix/prometheus/metrics"]
 --- error_code eval
 [200, 200]
---- no_error_log
-[error]
+
+
+
+=== TEST 3: apisix_batch_process_entries, mess with global rules
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "prometheus": {}
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/batch-process-metrics-aa"
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/plugin_metadata/error-log-logger',
+                ngx.HTTP_PUT,
+                [[{
+                    "tcp": {
+                        "host": "127.0.0.1",
+                        "port": 1999
+                    },
+                    "max_retry_count": 1000,
+                    "level": "NOTICE"
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            local code, body = t('/apisix/admin/global_rules/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "http-logger": {
+                                "uri": "http://127.0.0.1:1979"
+                            }
+                        }
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 4: check metrics
+--- yaml_config
+plugins:
+  - error-log-logger
+  - prometheus
+  - http-logger
+--- request
+GET /t
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+            local httpc = http.new()
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/batch-process-metrics-aa"
+            local res, err = httpc:request_uri(uri, {method = "GET"})
+            if not res then
+                ngx.say(err)
+                return
+            end
+
+            ngx.sleep(2)
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/apisix/prometheus/metrics"
+            local res, err = httpc:request_uri(uri, {method = "GET"})
+            if not res then
+                ngx.say(err)
+                return
+            end
+            ngx.say(res.body)
+        }
+    }
+--- response_body_like eval
+qr/apisix_batch_process_entries\{name="http logger",route_id="1",server_addr="127.0.0.1"\} \d+/
diff --git a/t/plugin/proxy-rewrite3.t b/t/plugin/proxy-rewrite3.t
new file mode 100644
index 0000000..f98de52
--- /dev/null
+++ b/t/plugin/proxy-rewrite3.t
@@ -0,0 +1,202 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+no_shuffle();
+log_level("info");
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (!$block->no_error_log && !$block->error_log) {
+        $block->set_value("no_error_log", "[error]\n[alert]");
+    }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: set route(rewrite method)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "proxy-rewrite": {
+                                "uri": "/plugin_proxy_rewrite",
+                                "method": "POST",
+                                "scheme": "http",
+                                "host": "apisix.iresty.com"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 2: hit route(upstream uri: should be /hello)
+--- request
+GET /hello
+--- error_log
+plugin_proxy_rewrite get method: POST
+
+
+
+=== TEST 3: set route(update rewrite method)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "proxy-rewrite": {
+                                "uri": "/plugin_proxy_rewrite",
+                                "method": "GET",
+                                "scheme": "http",
+                                "host": "apisix.iresty.com"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 4: hit route(upstream uri: should be /hello)
+--- request
+GET /hello
+--- error_log
+plugin_proxy_rewrite get method: GET
+
+
+
+=== TEST 5: wrong value of method key
+--- config
+    location /t {
+        content_by_lua_block {
+            local plugin = require("apisix.plugins.proxy-rewrite")
+            local ok, err = plugin.check_schema({
+                uri = '/apisix/home',
+                method = 'GET1',
+                host = 'apisix.iresty.com',
+                scheme = 'http'
+            })
+            if not ok then
+                ngx.say(err)
+            end
+
+            ngx.say("done")
+        }
+    }
+--- response_body
+property "method" validation failed: matches none of the enum values
+done
+
+
+
+=== TEST 6: set route(rewrite method with headers)
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "methods": ["GET"],
+                        "plugins": {
+                            "proxy-rewrite": {
+                                "uri": "/plugin_proxy_rewrite",
+                                "method": "POST",
+                                "scheme": "http",
+                                "host": "apisix.iresty.com",
+                                "headers":{
+                                    "x-api-version":"v1"
+                                }
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 7: hit route(with header)
+--- request
+GET /hello
+--- error_log
+plugin_proxy_rewrite get method: POST
diff --git a/t/plugin/request-id.t b/t/plugin/request-id.t
index 61ebc01..2888870 100644
--- a/t/plugin/request-id.t
+++ b/t/plugin/request-id.t
@@ -21,6 +21,19 @@ worker_connections(1024);
 repeat_each(1);
 no_long_string();
 no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+});
+
 run_tests;
 
 __DATA__
@@ -38,12 +51,8 @@ __DATA__
             ngx.say("done")
         }
     }
---- request
-GET /t
 --- response_body
 done
---- no_error_log
-[error]
 
 
 
@@ -60,13 +69,9 @@ done
             ngx.say("done")
         }
     }
---- request
-GET /t
 --- response_body
 property "include_in_response" validation failed: wrong type: expected boolean, got string
 done
---- no_error_log
-[error]
 
 
 
@@ -117,12 +122,8 @@ done
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
@@ -148,12 +149,8 @@ passed
             end
         }
     }
---- request
-GET /t
 --- response_body
 request header present
---- no_error_log
-[error]
 
 
 
@@ -201,13 +198,9 @@ request header present
             ngx.say("true")
         }
     }
---- request
-GET /t
 --- wait: 5
 --- response_body
 true
---- no_error_log
-[error]
 
 
 
@@ -261,12 +254,8 @@ true
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
@@ -292,12 +281,8 @@ passed
             end
         }
     }
---- request
-GET /t
 --- response_body
 request header present
---- no_error_log
-[error]
 
 
 
@@ -350,12 +335,8 @@ request header present
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
@@ -381,12 +362,8 @@ passed
             end
         }
     }
---- request
-GET /t
 --- response_body
 request header not present
---- no_error_log
-[error]
 
 
 
@@ -433,12 +410,8 @@ request header not present
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
@@ -464,12 +437,8 @@ passed
             end
         }
     }
---- request
-GET /t
 --- response_body
 X-Request-Id and Custom-Header-Name are different
---- no_error_log
-[error]
 
 
 
@@ -504,12 +473,8 @@ location /t {
         ngx.say("ok")
     }
 }
---- request
-GET /t
 --- response_body
 ok
---- no_error_log
-[error]
 
 
 
@@ -525,13 +490,9 @@ ok
             ngx.say("done")
         }
     }
---- request
-GET /t
 --- response_body
 property "algorithm" validation failed: matches none of the enum values
 done
---- no_error_log
-[error]
 
 
 
@@ -583,12 +544,8 @@ done
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
@@ -640,13 +597,9 @@ plugin_attr:
             ngx.say("true")
         }
     }
---- request
-GET /t
 --- wait: 5
 --- response_body
 true
---- no_error_log
-[error]
 
 
 
@@ -704,13 +657,9 @@ plugin_attr:
             ngx.say("true")
         }
     }
---- request
-GET /t
 --- wait: 5
 --- response_body
 true
---- no_error_log
-[error]
 
 
 
@@ -729,9 +678,51 @@ plugin_attr:
             ngx.say("done")
         }
     }
---- request
-GET /t
 --- response_body
 done
 --- error_log
 ailed to check the plugin_attr[request-id]: property "snowflake" validation failed: property "delta_offset" validation failed: matches none of the enum values
+
+
+
+=== TEST 18: add plugin with include_in_response true
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                        "plugins": {
+                            "request-id": {
+                                "include_in_response": true
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1982": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/opentracing"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 19: echo back the client's header if given
+--- request
+GET /opentracing
+--- more_headers
+X-Request-ID: 123
+--- response_headers
+X-Request-ID: 123
diff --git a/t/plugin/request-validation.t b/t/plugin/request-validation.t
index f870768..a2666aa 100644
--- a/t/plugin/request-validation.t
+++ b/t/plugin/request-validation.t
@@ -1449,3 +1449,59 @@ passed
 200
 --- no_error_log
 [error]
+
+
+
+=== TEST 35: add route for urlencoded post data validation
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "request-validation": {
+                            "body_schema": {
+                                "type": "object",
+                                "required": ["required_payload"],
+                                "properties": {
+                                    "required_payload": {"type": "string"}
+                                },
+                                "rejected_msg": "customize reject message"
+                            }
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/echo"
+                }]])
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 36: test urlencoded post data
+--- more_headers
+Content-Type: application/x-www-form-urlencoded
+--- request eval
+"POST /echo
+" . "a=b&" x 101 . "required_payload=101-hello"
+--- response_body eval
+qr/101-hello/
+--- no_error_log
+[error]
diff --git a/t/plugin/prometheus3.t b/t/plugin/request-validation2.t
similarity index 50%
copy from t/plugin/prometheus3.t
copy to t/plugin/request-validation2.t
index eb11d37..945b98b 100644
--- a/t/plugin/prometheus3.t
+++ b/t/plugin/request-validation2.t
@@ -14,59 +14,55 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-BEGIN {
-    if ($ENV{TEST_NGINX_CHECK_LEAK}) {
-        $SkipReason = "unavailable for the hup tests";
 
-    } else {
-        $ENV{TEST_NGINX_USE_HUP} = 1;
-        undef $ENV{TEST_NGINX_USE_STAP};
+use t::APISIX 'no_plan';
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
     }
-}
 
-use t::APISIX 'no_plan';
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+});
 
-repeat_each(1);
-no_long_string();
-no_shuffle();
-no_root_location();
-run_tests;
+run_tests();
 
 __DATA__
 
-=== TEST 1: use original etcd modified index
+=== TEST 1: json body with duplicate key
 --- config
     location /t {
         content_by_lua_block {
+            local json = require("toolkit.json")
             local t = require("lib.test_admin").test
-            local code, body = t('/apisix/admin/plugin_configs/1',
-                ngx.HTTP_PUT,
-                [[{
-                    "plugins": {
-                        "prometheus":{}
+            local data = {
+                plugins = {
+                    ["request-validation"] = {
+                        body_schema = {
+                            type = "object",
+                            properties = {
+                                k = {pattern = "^good$"}
+                            }
+                        }
                     }
-                }]]
-                )
-
-            if code >= 300 then
-                ngx.status = code
-                ngx.say(body)
-                return
-            end
-
-            local code, body = t('/apisix/admin/routes/1',
-                ngx.HTTP_PUT,
-                [[{
-                    "plugin_config_id": 1,
-                    "upstream": {
-                        "nodes": {
-                            "127.0.0.1:1980": 1
-                        },
-                        "type": "roundrobin"
+                },
+                upstream = {
+                    nodes = {
+                        ["127.0.0.1:1980"] = 1
                     },
-                    "uri": "/hello"
-                }]]
-                )
+                    type = "roundrobin"
+                },
+                uri = "/echo"
+            }
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 json.encode(data)
+            )
 
             if code >= 300 then
                 ngx.status = code
@@ -74,19 +70,14 @@ __DATA__
             ngx.say(body)
         }
     }
---- request
-GET /t
 --- response_body
 passed
---- no_error_log
-[error]
 
 
 
 === TEST 2: hit
---- pipelined_requests eval
-["GET /hello", "GET /apisix/prometheus/metrics"]
---- error_code eval
-[200, 200]
---- no_error_log
-[error]
+--- request
+POST /echo
+{"k":"bad","k":"good"}
+--- response_body chomp
+{"k":"good"}
diff --git a/t/plugin/serverless.t b/t/plugin/serverless.t
index 7a1b01b..28c3161 100644
--- a/t/plugin/serverless.t
+++ b/t/plugin/serverless.t
@@ -835,3 +835,53 @@ qr/(run balancer phase with [\d.]+)/
 --- grep_error_log_out
 run balancer phase with 127.0.0.2
 run balancer phase with 127.0.0.1
+
+
+
+=== TEST 29: add args parse test for serverless
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "serverless-post-function": {
+                        "functions" : ["return function(conf, ctx) local net_url = require(\"net.url\");
+                                        local args = ngx.var.args;
+                                        ngx.print(net_url.parse(args).path);
+                                        end"]
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/echo"
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 30: check args parse test
+--- request
+GET /echo?args=%40%23%24%25%5E%26
+--- response_body chomp
+args=@#$%^&
+--- no_error_log
+[error]
diff --git a/t/plugin/traffic-split5.t b/t/plugin/traffic-split5.t
index 9e01ac8..aee660b 100644
--- a/t/plugin/traffic-split5.t
+++ b/t/plugin/traffic-split5.t
@@ -311,3 +311,109 @@ passed
     }
 --- response_body
 1970, 1970, 1971, 1972, 1972, 1973
+
+
+
+=== TEST 5: set upstream(multiple rules, the first rule has the match attribute and the second rule does not) and add route
+--- config
+    location /t {
+        content_by_lua_block {
+            local json = require("toolkit.json")
+            local t = require("lib.test_admin").test
+            local data = {
+                uri = "/hello",
+                plugins = {
+                    ["traffic-split"] = {
+                        rules = {
+                            {
+                                match = { {
+                                    vars = { { "arg_id", "==", "1" } }
+                                } },
+                                weighted_upstreams = {
+                                    {
+                                        upstream = {
+                                            name = "upstream_A",
+                                            type = "roundrobin",
+                                            nodes = {
+                                                ["127.0.0.1:1970"] = 1
+                                            }
+                                        },
+                                        weight = 1
+                                    }
+                               }
+                            },
+                            {
+                                weighted_upstreams = {
+                                    {
+                                        upstream = {
+                                            name = "upstream_B",
+                                            type = "roundrobin",
+                                            nodes = {
+                                                ["127.0.0.1:1971"] = 1
+                                            }
+                                        },
+                                        weight = 1
+                                    },
+                                    {
+                                        weight = 1
+                                    }
+                               }
+                            }
+                        }
+                    }
+                },
+                upstream = {
+                    type = "roundrobin",
+                    nodes = {
+                        ["127.0.0.1:1972"] = 1
+                    }
+                }
+            }
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                json.encode(data)
+            )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 6: first rule match failed and the second rule match success
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+            local httpc = http.new()
+
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello?id=1"
+            local ports = {}
+            local res, err
+            for i = 1, 2 do
+                res, err = httpc:request_uri(uri)
+                local port = tonumber(res.body)
+                ports[i] = port
+            end
+
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello?id=2"
+            for i = 3, 4 do
+                res, err = httpc:request_uri(uri)
+                local port = tonumber(res.body)
+                ports[i] = port
+            end
+            table.sort(ports)
+
+            ngx.say(table.concat(ports, ", "))
+        }
+    }
+--- response_body
+1970, 1970, 1971, 1972