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

shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new dc4d52b5d feat(batch-requests): bound pipeline item count, timeout, 
and request schema (#13492)
dc4d52b5d is described below

commit dc4d52b5d3da6e0cda5ee608f25beb896aeb1016
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Wed Jun 10 16:05:40 2026 +0800

    feat(batch-requests): bound pipeline item count, timeout, and request 
schema (#13492)
---
 apisix/plugins/batch-requests.lua        |  30 +++++-
 docs/en/latest/plugins/batch-requests.md |   7 +-
 docs/zh/latest/plugins/batch-requests.md |   7 +-
 t/plugin/batch-requests.t                | 177 ++++++++++++++++++++++++++++++-
 4 files changed, 211 insertions(+), 10 deletions(-)

diff --git a/apisix/plugins/batch-requests.lua 
b/apisix/plugins/batch-requests.lua
index fed04cb3b..8927b33b5 100644
--- a/apisix/plugins/batch-requests.lua
+++ b/apisix/plugins/batch-requests.lua
@@ -44,6 +44,7 @@ local schema = {
 }
 
 local default_max_body_size = 1024 * 1024 -- 1MiB
+local default_max_pipeline_items = 1000
 local metadata_schema = {
     type = "object",
     properties = {
@@ -53,6 +54,12 @@ local metadata_schema = {
             exclusiveMinimum = 0,
             default = default_max_body_size,
         },
+        max_pipeline_items = {
+            description = "max number of requests allowed in the pipeline",
+            type = "integer",
+            exclusiveMinimum = 0,
+            default = default_max_pipeline_items,
+        },
     },
 }
 
@@ -73,6 +80,7 @@ local req_schema = {
         timeout = {
             description = "pipeline timeout(ms)",
             type = "integer",
+            minimum = 1,
             default = 30000,
         },
         pipeline = {
@@ -80,6 +88,7 @@ local req_schema = {
             minItems = 1,
             items = {
                 type = "object",
+                additionalProperties = false,
                 properties = {
                     version = {
                         description = "HTTP version",
@@ -93,18 +102,23 @@ local req_schema = {
                         minLength = 1,
                     },
                     query = {
-                        description = "request header",
+                        description = "request query string",
                         type = "object",
                     },
                     headers = {
-                        description = "request query string",
+                        description = "request headers",
                         type = "object",
                     },
+                    body = {
+                        description = "request body",
+                        type = "string",
+                    },
                     ssl_verify = {
                         type = "boolean",
                         default = false
                     },
-                }
+                },
+                required = {"path"},
             }
         }
     },
@@ -215,10 +229,13 @@ local function batch_requests(ctx)
     core.log.info("metadata: ", core.json.delay_encode(metadata))
 
     local max_body_size
+    local max_pipeline_items
     if metadata then
         max_body_size = metadata.value.max_body_size
+        max_pipeline_items = metadata.value.max_pipeline_items or 
default_max_pipeline_items
     else
         max_body_size = default_max_body_size
+        max_pipeline_items = default_max_pipeline_items
     end
 
     local req_body, err = core.request.get_body(max_body_size, ctx)
@@ -245,6 +262,13 @@ local function batch_requests(ctx)
         return code, body
     end
 
+    if #data.pipeline > max_pipeline_items then
+        return 400, {
+            error_msg = "too many pipeline requests, " .. #data.pipeline ..
+                        " exceeds the maximum of " .. max_pipeline_items
+        }
+    end
+
     local httpc = http.new()
     httpc:set_timeout(data.timeout)
     local ok, err = httpc:connect("127.0.0.1", ngx.var.server_port)
diff --git a/docs/en/latest/plugins/batch-requests.md 
b/docs/en/latest/plugins/batch-requests.md
index b9945f410..fd5be40dd 100644
--- a/docs/en/latest/plugins/batch-requests.md
+++ b/docs/en/latest/plugins/batch-requests.md
@@ -89,9 +89,10 @@ curl 
http://127.0.0.1:9180/apisix/admin/plugin_metadata/batch-requests -H "X-API
 
 ## Metadata
 
-| Name          | Type    | Required | Default | Valid values | Description    
                            |
-| ------------- | ------- | -------- | ------- | ------------ | 
------------------------------------------ |
-| max_body_size | integer | True     | 1048576 | [1, ...]     | Maximum size 
of the request body in bytes. |
+| Name               | Type    | Required | Default | Valid values | 
Description                                              |
+| ------------------ | ------- | -------- | ------- | ------------ | 
-------------------------------------------------------- |
+| max_body_size      | integer | True     | 1048576 | [1, ...]     | Maximum 
size of the request body in bytes.               |
+| max_pipeline_items | integer | True     | 1000    | [1, ...]     | Maximum 
number of requests allowed in a single pipeline. |
 
 ## Request and response format
 
diff --git a/docs/zh/latest/plugins/batch-requests.md 
b/docs/zh/latest/plugins/batch-requests.md
index 045f3c288..d43e9abe3 100644
--- a/docs/zh/latest/plugins/batch-requests.md
+++ b/docs/zh/latest/plugins/batch-requests.md
@@ -91,9 +91,10 @@ curl 
http://127.0.0.1:9180/apisix/admin/plugin_metadata/batch-requests \
 
 ## 元数据
 
-| 名称          | 类型     | 必选项 | 默认值   | 有效值 | 描述                          |
-| ------------- | ------- | -------| ------- | ------ | 
---------------------------- |
-| max_body_size | integer | 是     | 1048576 |[1, ...]| 请求体的最大大小,单位:bytes。 |
+| 名称               | 类型     | 必选项 | 默认值   | 有效值 | 描述                          |
+| ------------------ | ------- | -------| ------- | ------ | 
---------------------------- |
+| max_body_size      | integer | 是     | 1048576 |[1, ...]| 请求体的最大大小,单位:bytes。 
|
+| max_pipeline_items | integer | 是     | 1000    |[1, ...]| 单个 pipeline 
中允许的最大请求数量。 |
 
 ## 请求和响应格式
 
diff --git a/t/plugin/batch-requests.t b/t/plugin/batch-requests.t
index 3a98e6a67..298f9df11 100644
--- a/t/plugin/batch-requests.t
+++ b/t/plugin/batch-requests.t
@@ -998,7 +998,182 @@ qr/\{"error_msg":"invalid configuration: property 
\\"max_body_size\\" validation
 
 
 
-=== TEST 24: keep environment clean
+=== TEST 24: reject unknown field in a pipeline request
+--- config
+    location = /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/batch-requests',
+                 ngx.HTTP_POST,
+                 [=[{
+                    "pipeline":[
+                    {
+                        "path": "/b",
+                        "unknown_field": "x"
+                    }
+                    ]
+                }]=]
+            )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/bad request body/
+
+
+
+=== TEST 25: accept and forward a request body in a pipeline request
+--- config
+    location = /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, message, res_body = t('/apisix/batch-requests',
+                 ngx.HTTP_POST,
+                 [=[{
+                    "pipeline":[
+                    {
+                        "method": "POST",
+                        "path": "/echo",
+                        "body": "hello"
+                    }
+                    ]
+                }]=]
+            )
+
+            ngx.status = code
+            ngx.say(res_body)
+        }
+    }
+    location = /echo {
+        content_by_lua_block {
+            ngx.req.read_body()
+            ngx.status = 200
+            ngx.print(ngx.req.get_body_data() or "")
+        }
+    }
+--- request
+GET /t
+--- response_body eval
+qr/"body":"hello"/
+
+
+
+=== TEST 26: reject timeout below the minimum
+--- config
+    location = /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/batch-requests',
+                 ngx.HTTP_POST,
+                 [=[{
+                    "timeout": 0,
+                    "pipeline":[
+                    {
+                        "path": "/b"
+                    }
+                    ]
+                }]=]
+            )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/bad request body/
+
+
+
+=== TEST 27: set a small max_pipeline_items
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = 
t('/apisix/admin/plugin_metadata/batch-requests',
+                ngx.HTTP_PUT,
+                [[{
+                    "max_pipeline_items": 2
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 28: reject a pipeline with too many requests
+--- config
+    location = /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/batch-requests',
+                 ngx.HTTP_POST,
+                 [=[{
+                    "pipeline":[
+                    {"path": "/b"},
+                    {"path": "/b"},
+                    {"path": "/b"}
+                    ]
+                }]=]
+            )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/too many pipeline requests, 3 exceeds the maximum of 2/
+
+
+
+=== TEST 29: reject a pipeline entry missing path
+--- config
+    location = /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/batch-requests',
+                 ngx.HTTP_POST,
+                 [=[{
+                    "pipeline":[
+                    {
+                        "method": "GET"
+                    }
+                    ]
+                }]=]
+            )
+
+            ngx.status = code
+            ngx.say(body)
+        }
+    }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/property \\"path\\" is required/
+
+
+
+=== TEST 30: keep environment clean
 --- config
     location /t {
         content_by_lua_block {

Reply via email to