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 {