Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Body transformer plugin #8766

Merged
merged 19 commits into from
Feb 10, 2023

Conversation

kingluo
Copy link
Contributor

@kingluo kingluo commented Feb 1, 2023

Description

This plugin is used to transform the request or response body from one
format to another format, e.g. JSON to XML.

It depends on xml2lua and lua-resty-template.

Use cases:

  • simple SOAP proxy
  • generic template-based transform

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (If not, please discuss on the APISIX mailing list first)

@kingluo kingluo marked this pull request as ready for review February 6, 2023 09:27


-- luacheck: ignore
function string.escape_xml(s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we register these methods before we use them and deregister once the job is done? So we can avoid affecting other modules.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

luacheck has no inline option to depress "setting undefine field of global" warning. But depress the whole lua file is not proper. And escape_* has no conflict so far, so I think it's ok to use these names.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solution is not about making luacheck happy, but limiting the scope of the injection. If we don't deregister the methods, it will affect other modules including those not written by us.
Anyway, it is not a good idea to allow the monkey patch of the std lib outside our scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I know. But setting methods on demand on the global object will make luacheck warns: setting undefined field escape_xml of global string, which could not be depressed via inline comment, so ignore the whole file in .luacheckrc instead?

function _M.body_filter(conf, ctx)
if conf.response then
local body = core.response.hold_body_chunk(ctx)
if ngx.arg[2] == false and not body then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ngx.arg[2] == false and not body then
if not body then

hold_body_chunk returns a body only when ngx.arg[2] is true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this plugiin needs to handle body=nil case.

if type(k) == "string" then
k = k:match(".*:(.*)")
if k then
tbl[k] = v
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this code just inserts a new k

end


function _M.rewrite(conf, ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rewrite is only for plugins that require running before other plugins. For example, plugins to select consumers, plugins for tracing, plugins to set real IP, and so on. Most of the other plugin use access phase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This plugin needs to use original content-type header before proxy-rewrite plugin overrrdes it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this plugin is just doing rewrite job, just like proxy-rewrite plugin.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to have a discussion to unify the "rewrite/access" methods in 2020.
Since this is not the only plugin that has inconsistent "rewrite/access" methods, and you insist on it, I am OK to accept it.



local function set_input_format(conf, typ, ct)
if conf[typ].input_format == nil and ct then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conf is shared between reqs. Is there a race if the previous request set the input format different from the current one?

local httpc = http.new()
local res, err = httpc:request_uri(uri, opt)
if not res then
ngx.say(err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check --- response_body to ensure no err happens. Please update other similar places.

}
}
--- error_log
attempt to call global 'name' (a string value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add log prefix to ensure it comes from an expected log

local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/foobar?name=hello"
local opt = {method = "GET"}
local httpc = http.new()
httpc:request_uri(uri, opt)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to check the res here?

| ----------- | ----------- | ----------- | ----------- |
| `request` | object | False | request body transformation configuration |
| `request.input_format` | string | False | request body original format, if not specified, it would be determined from `Content-Type` header. |
| `request.template` | string | False | request body transformation template |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template is required

| `request.template` | string | False | request body transformation template |
| `response` | object | False | response body transformation configuration |
| `response.input_format` | string | False | response body original format |
| `response.template` | string | False | response body transformation template, if not specified, it would be determined from `Content-Type` header. |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean determining the input_format from Content-Type header?


function _M.body_filter(_, ctx)
local conf = ctx.body_transformer_conf
if conf.response then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems if neither conf.request nor conf.response are given, the conf will be nil here.
We should check if one of them is configured at least in check_schema.

@spacewander spacewander merged commit cfeadfb into apache:master Feb 10, 2023
hongbinhsu added a commit to fitphp/apix that referenced this pull request Feb 13, 2023
* upstream/master:
  docs: change the file name to 'create-ssl.py'.If 'ssl.py' is used as … (apache#8623)
  feat: Body transformer plugin (apache#8766)
  fix: mocking plugin panic when response_example contain $ (apache#8810) (apache#8816)
  feat: file logger plugin support response body in variable (apache#8711)
  docs(getting-started): rewrite the install section (apache#8807)
  feat: allow each logger to define custom log format in its conf (apache#8806)
  fix(etcd): reloaded data may be in res.body.node (apache#8736)
  fix: fix fetch all service info from consul (apache#8651)
  docs: fix global-rule.md wrong curl  address (apache#8793)
  feat: stream subsystem support consul service discovery (apache#8696)
  chore(kafka-logger): support configuration `meta_refresh_interval` parameter (apache#8762)
  feat: ready to release 2.15.2 (apache#8783)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants