diff --git a/apisix/plugins/batch-requests.lua b/apisix/plugins/batch-requests.lua index faeeb1181b70..71878218d8d7 100644 --- a/apisix/plugins/batch-requests.lua +++ b/apisix/plugins/batch-requests.lua @@ -14,12 +14,15 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -local core = require("apisix.core") -local http = require("resty.http") -local ngx = ngx -local io_open = io.open -local ipairs = ipairs -local pairs = pairs +local core = require("apisix.core") +local http = require("resty.http") +local ngx = ngx +local io_open = io.open +local ipairs = ipairs +local pairs = pairs +local str_find = string.find +local str_lower = string.lower + local plugin_name = "batch-requests" @@ -112,22 +115,42 @@ local function check_input(data) end end +local function lowercase_key_or_init(obj) + if not obj then + return {} + end -local function set_common_header(data) - local ck = core.request.header(nil, "Cookie") + local lowercase_key_obj = {} + for k, v in pairs(obj) do + lowercase_key_obj[str_lower(k)] = v + end + + return lowercase_key_obj +end + +local function ensure_header_lowercase(data) + data.headers = lowercase_key_or_init(data.headers) for i,req in ipairs(data.pipeline) do - if not req.headers then - req.headers = {} - end + req.headers = lowercase_key_or_init(req.headers) + end +end - if ck then - req.headers["Cookie"] = ck + +local function set_common_header(data) + local outer_headers = core.request.headers(nil) + for i,req in ipairs(data.pipeline) do + for k, v in pairs(data.headers) do + if not req.headers[k] then + req.headers[k] = v + end end - if data.headers then - for k, v in pairs(data.headers) do - if not req.headers[k] then + if outer_headers then + for k, v in pairs(outer_headers) do + local is_content_header = str_find(k, "content-", 1, true) == 1 + -- skip header start with "content-" + if not req.headers[k] and not is_content_header then req.headers[k] = v end end @@ -202,8 +225,10 @@ local function batch_requests() core.response.exit(500, {error_msg = "connect to apisix failed: " .. err}) end + ensure_header_lowercase(data) set_common_header(data) set_common_query(data) + local responses, err = httpc:request_pipeline(data.pipeline) if not responses then core.response.exit(400, {error_msg = "request failed: " .. err}) diff --git a/doc/plugins/batch-requests-cn.md b/doc/plugins/batch-requests-cn.md index 3120a22123b9..fb5d1994c227 100644 --- a/doc/plugins/batch-requests-cn.md +++ b/doc/plugins/batch-requests-cn.md @@ -32,6 +32,10 @@ `batch-requests` 插件可以一次接受多个请求并以 [http pipeline](https://en.wikipedia.org/wiki/HTTP_pipelining) 的方式在网关发起多个http请求,合并结果后再返回客户端,这在客户端需要访问多个接口时可以显著地提升请求性能。 +> **提示** +> +> 外层的 Http 请求头会自动设置到每一个独立请求中,如果独立请求中出现相同键值的请求头,那么只有独立请求的请求头会生效。 + ## 属性 无 diff --git a/doc/plugins/batch-requests.md b/doc/plugins/batch-requests.md index a715ef7cbb73..b40023b4a045 100644 --- a/doc/plugins/batch-requests.md +++ b/doc/plugins/batch-requests.md @@ -32,6 +32,10 @@ `batch-requests` can accept mutiple request and send them from `apisix` via [http pipeline](https://en.wikipedia.org/wiki/HTTP_pipelining),and return a aggregated response to client,this can significantly improve performance when the client needs to access multiple APIs. +> **Tips** +> +> The HTTP headers for the outer batch request, except for the Content- headers such as Content-Type, apply to every request in the batch. If you specify a given HTTP header in both the outer request and an individual call, then the individual call header's value overrides the outer batch request header's value. The headers for an individual call apply only to that call. + ## Attributes None @@ -41,7 +45,7 @@ None Default enbaled ## Batch Api Request/Response -The plugin will create a api in `apisix` to handle your aggregation request. +The plugin will create a api in `apisix` to handle your batch request. ### Batch Api Request: diff --git a/t/plugin/batch-requests.t b/t/plugin/batch-requests.t index 928ebfbbd39f..c409ceea4f46 100644 --- a/t/plugin/batch-requests.t +++ b/t/plugin/batch-requests.t @@ -40,7 +40,8 @@ __DATA__ }, "headers": { "Base-Header": "base", - "Conflict-Header": "header_value" + "ConflictHeader": "header_value", + "OuterConflict": "common_value" }, "pipeline":[ { @@ -48,7 +49,7 @@ __DATA__ "headers": { "Header1": "hello", "Header2": "world", - "Conflict-Header": "b-header-value" + "ConflictHeader": "b-header-value" } },{ "path": "/c", @@ -71,7 +72,8 @@ __DATA__ "X-Res": "B", "X-Header1": "hello", "X-Header2": "world", - "X-Conflict-Header": "b-header-value" + "X-Conflict-Header": "b-header-value", + "X-OuterConflict": "common_value" } }, { @@ -95,8 +97,11 @@ __DATA__ "X-Query-Conflict": "d_value" } } - ]]=] - ) + ]]=], + { + ConflictHeader = "outer_header", + OuterConflict = "outer_confliect" + }) ngx.status = code ngx.say(body) @@ -110,7 +115,8 @@ __DATA__ ngx.header["Base-Query"] = ngx.var.arg_base ngx.header["X-Header1"] = ngx.req.get_headers()["Header1"] ngx.header["X-Header2"] = ngx.req.get_headers()["Header2"] - ngx.header["X-Conflict-Header"] = ngx.req.get_headers()["Conflict-Header"] + ngx.header["X-Conflict-Header"] = ngx.req.get_headers()["ConflictHeader"] + ngx.header["X-OuterConflict"] = ngx.req.get_headers()["OuterConflict"] ngx.header["X-Res"] = "B" ngx.print("B") } @@ -687,7 +693,7 @@ passed -=== TEST 15: copy cookie to every request +=== TEST 15: copy all header to every request except Contenct- --- config client_body_in_file_only on; location = /aggregate { @@ -697,7 +703,7 @@ passed local code, body = t('/apisix/batch-requests', ngx.HTTP_POST, [=[{ - "timeout": 100, + "timeout": 1000, "pipeline":[ { "path": "/b", @@ -716,24 +722,28 @@ passed { "status": 200, "headers": { - "X-Cookie": "request-cookies-b" + "X-Cookie": "request-cookies-b", + "X-HeaderB": "request-header-b" } }, { "status": 201, "headers": { - "X-Cookie": "request-cookies-c" + "X-Cookie": "request-cookies-c", + "X-HeaderC": "request-header-c" } }, { "status": 202, "headers": { - "X-Cookie": "request-cookies-d" + "X-Cookie": "request-cookies-d", + "X-HeaderD": "request-header-d" } } ]]=], { - Cookie = "request-cookies" + Cookie = "request-cookies", + OuterHeader = "request-header" }) ngx.status = code @@ -745,18 +755,21 @@ passed content_by_lua_block { ngx.status = 200 ngx.header["X-Cookie"] = ngx.req.get_headers()["Cookie"] .. "-b" + ngx.header["X-HeaderB"] = ngx.req.get_headers()["OuterHeader"] .. "-b" } } location = /c { content_by_lua_block { ngx.status = 201 ngx.header["X-Cookie"] = ngx.req.get_headers()["Cookie"] .. "-c" + ngx.header["X-HeaderC"] = ngx.req.get_headers()["OuterHeader"] .. "-c" } } location = /d { content_by_lua_block { ngx.status = 202 ngx.header["X-Cookie"] = ngx.req.get_headers()["Cookie"] .. "-d" + ngx.header["X-HeaderD"] = ngx.req.get_headers()["OuterHeader"] .. "-d" } } --- request