Skip to content

Commit

Permalink
feat: set proxy_request_buffering dynamically (#6075)
Browse files Browse the repository at this point in the history
  • Loading branch information
spacewander committed Jan 14, 2022
1 parent 7a31ec7 commit 5428972
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 0 deletions.
65 changes: 65 additions & 0 deletions apisix/plugins/proxy-control.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
--
-- 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.
--
local require = require
local core = require("apisix.core")
local ok, apisix_ngx_client = pcall(require, "resty.apisix.client")


local schema = {
type = "object",
properties = {
request_buffering = {
type = "boolean",
default = true,
},
},
}


local plugin_name = "proxy-control"
local _M = {
version = 0.1,
priority = 21990,
name = plugin_name,
schema = schema,
}


function _M.check_schema(conf)
return core.schema.check(schema, conf)
end


-- we want to control proxy behavior before auth, so put the code under rewrite method
function _M.rewrite(conf, ctx)
if not ok then
core.log.error("need to build APISIX-OpenResty to support proxy control")
return 501
end

local request_buffering = conf.request_buffering
if request_buffering ~= nil then
local ok, err = apisix_ngx_client.set_proxy_request_buffering(request_buffering)
if not ok then
core.log.error("failed to set request_buffering: ", err)
return 503
end
end
end


return _M
1 change: 1 addition & 0 deletions conf/config-default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ graphql:
plugins: # plugin list (sorted by priority)
- real-ip # priority: 23000
- client-control # priority: 22000
- proxy-control # priority: 21990
- ext-plugin-pre-req # priority: 12000
- zipkin # priority: 11011
- request-id # priority: 11010
Expand Down
1 change: 1 addition & 0 deletions docs/en/latest/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"plugins/api-breaker",
"plugins/traffic-split",
"plugins/request-id",
"plugins/proxy-control",
"plugins/client-control"
]
},
Expand Down
95 changes: 95 additions & 0 deletions docs/en/latest/plugins/proxy-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: proxy-control
---

<!--
#
# 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.
#
-->

## Summary

- [**Name**](#name)
- [**Attributes**](#attributes)
- [**How To Enable**](#how-to-enable)
- [**Test Plugin**](#test-plugin)
- [**Disable Plugin**](#disable-plugin)

## Name

The `proxy-control` plugin dynamically controls the behavior of Nginx to proxy.

**This plugin requires APISIX to run on [APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).**

## Attributes

| Name | Type | Requirement | Default | Valid | Description |
| --------- | ------------- | ----------- | ---------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| request_buffering | boolean | optional | true | | dynamically set the `proxy_request_buffering` directive |

## How To Enable

Here's an example, enable this plugin on the specified route:

```shell
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/upload",
"plugins": {
"proxy-control": {
"request_buffering": false
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
```

## Test Plugin

Use curl to access:

```shell
curl -i http://127.0.0.1:9080/upload -d @very_big_file
```

It's expected not to find "a client request body is buffered to a temporary file" in the error log.

## Disable Plugin

When you want to disable this plugin, it is very simple,
you can delete the corresponding json configuration in the plugin configuration,
no need to restart the service, it will take effect immediately:

```shell
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/upload",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
```

This plugin has been disabled now. It works for other plugins.
1 change: 1 addition & 0 deletions t/admin/plugins.t
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ GET /t
--- response_body
real-ip
client-control
proxy-control
ext-plugin-pre-req
zipkin
request-id
Expand Down
138 changes: 138 additions & 0 deletions t/plugin/proxy-control.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#
# 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;

my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
my $version = eval { `$nginx_binary -V 2>&1` };

if ($version !~ m/\/apisix-nginx-module/) {
plan(skip_all => "apisix-nginx-module not installed");
} else {
plan('no_plan');
}

repeat_each(1);
log_level('info');
no_root_location();
no_shuffle();

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: proxy_request_buffering off
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local json = require("toolkit.json")
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"plugins": {
"proxy-control": {
"request_buffering": false
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: hit, only the upstream server will buffer the request
--- request eval
"POST /hello
" . "12345" x 10240
--- grep_error_log eval
qr/a client request body is buffered to a temporary file/
--- grep_error_log_out
a client request body is buffered to a temporary file
=== TEST 3: proxy_request_buffering on
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local json = require("toolkit.json")
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"plugins": {
"proxy-control": {
"request_buffering": true
}
}
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: hit
--- request eval
"POST /hello
" . "12345" x 10240
--- grep_error_log eval
qr/a client request body is buffered to a temporary file/
--- grep_error_log_out
a client request body is buffered to a temporary file
a client request body is buffered to a temporary file

0 comments on commit 5428972

Please sign in to comment.