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

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


The following commit(s) were added to refs/heads/master by this push:
     new fc948f9  feautre: support for proxy mirror plugin. (#1288)
fc948f9 is described below

commit fc948f95787240d56cfd933b320e121adf7bef2c
Author: agile6v <agil...@agile6v.com>
AuthorDate: Thu Mar 19 08:10:03 2020 +0800

    feautre: support for proxy mirror plugin. (#1288)
---
 bin/apisix                          |  12 ++
 conf/config.yaml                    |   1 +
 doc/README.md                       |   1 +
 doc/README_CN.md                    |   1 +
 doc/plugins/proxy-mirror-cn.md      |  93 ++++++++++++
 doc/plugins/proxy-mirror.md         |  97 ++++++++++++
 lua/apisix/core/ctx.lua             |   2 +
 lua/apisix/plugins/proxy-mirror.lua |  59 ++++++++
 t/APISIX.pm                         |  12 ++
 t/admin/plugins.t                   |   2 +-
 t/debug/debug-mode.t                |   1 +
 t/plugin/proxy-mirror.t             | 291 ++++++++++++++++++++++++++++++++++++
 12 files changed, 571 insertions(+), 1 deletion(-)

diff --git a/bin/apisix b/bin/apisix
index 78c2914..6f0a0f1 100755
--- a/bin/apisix
+++ b/bin/apisix
@@ -373,6 +373,7 @@ http {
         }
 
         location / {
+            set $upstream_mirror_host        '';
             set $upstream_scheme             'http';
             set $upstream_host               $host;
             set $upstream_upgrade            '';
@@ -444,6 +445,7 @@ http {
             {% end %}
 
             proxy_pass      $upstream_scheme://apisix_backend$upstream_uri;
+            mirror          /proxy_mirror;
 
             header_filter_by_lua_block {
                 apisix.http_header_filter_phase()
@@ -480,6 +482,16 @@ http {
                 apisix.http_log_phase()
             }
         }
+
+        location = /proxy_mirror {
+            internal;
+
+            if ($upstream_mirror_host = "") {
+                return 200;
+            }
+
+            proxy_pass $upstream_mirror_host$request_uri;
+        }
     }
 }
 ]=]
diff --git a/conf/config.yaml b/conf/config.yaml
index e050092..65f79da 100644
--- a/conf/config.yaml
+++ b/conf/config.yaml
@@ -142,6 +142,7 @@ plugins:                          # plugin list
   - wolf-rbac
   - proxy-cache
   - tcp-logger
+  - proxy-mirror
 
 stream_plugins:
   - mqtt-proxy
diff --git a/doc/README.md b/doc/README.md
index cba2444..1854295 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -60,6 +60,7 @@ Plugins
 * [fault-injection](plugins/fault-injection.md): The specified response body, 
response code, and response time can be returned, which provides processing 
capabilities in different failure scenarios, such as service failure, service 
overload, and high service delay.
 * [proxy-cache](plugins/proxy-cache.md): Provides the ability to cache 
upstream response data.
 * [tcp-logger](plugins/tcp-logger.md): Log requests to TCP servers
+* [proxy-mirror](plugins/proxy-mirror.md): Provides the ability to mirror 
client requests.
 
 Deploy to the Cloud
 =======
diff --git a/doc/README_CN.md b/doc/README_CN.md
index 3fc9f9d..294727e 100644
--- a/doc/README_CN.md
+++ b/doc/README_CN.md
@@ -60,4 +60,5 @@ Reference document
 * [response-rewrite](plugins/response-rewrite-cn.md): 支持自定义修改返回内容的 `status 
code`、`body`、`headers`。
 * 
[fault-injection](plugins/fault-injection-cn.md):故障注入,可以返回指定的响应体、响应码和响应时间,从而提供了不同的失败场景下处理的能力,例如服务失败、服务过载、服务高延时等。
 * [proxy-cache](plugins/proxy-cache-cn.md):代理缓存插件提供缓存后端响应数据的能力。
+* [proxy-mirror](plugins/proxy-mirror-cn.md):代理镜像插件提供镜像客户端请求的能力。
 
diff --git a/doc/plugins/proxy-mirror-cn.md b/doc/plugins/proxy-mirror-cn.md
new file mode 100644
index 0000000..4b3f073
--- /dev/null
+++ b/doc/plugins/proxy-mirror-cn.md
@@ -0,0 +1,93 @@
+<!--
+#
+# 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.
+#
+-->
+
+[English](proxy-mirror.md)
+
+# proxy-mirror
+
+代理镜像插件,该插件提供了镜像客户端请求的能力。
+
+注:镜像请求返回的响应会被忽略。
+
+### 参数
+
+|名称    |必须|类型|描述|
+|------- |-----|------|------|
+|host|是|string|指定镜像服务地址,例如:http://127.0.0.1:9797(地址中需要包含 schema 
:http或https,不能包含 URI 部分)|
+
+### 示例
+
+#### 启用插件
+
+示例1:为特定路由启用 `proxy-mirror` 插件:
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d '
+{
+    "plugins": {
+        "proxy-mirror": {
+           "host": "http://127.0.0.1:9797";
+        }
+    },
+    "upstream": {
+        "nodes": {
+            "127.0.0.1:1999": 1
+        },
+        "type": "roundrobin"
+    },
+    "uri": "/hello"
+}'
+```
+
+测试:
+
+```shell
+$ curl http://127.0.0.1:9080/hello -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Content-Length: 12
+Connection: keep-alive
+Server: APISIX web server
+Date: Wed, 18 Mar 2020 13:01:11 GMT
+Last-Modified: Thu, 20 Feb 2020 14:21:41 GMT
+
+hello world
+```
+
+> 由于指定的 mirror 地址是127.0.0.1:9797,所以验证此插件是否已经正常工作需要在端口为9797的服务上确认,例如,我们可以通过 
python 启动一个简单的 server: python -m SimpleHTTPServer 9797。
+
+#### 禁用插件
+
+移除插件配置中相应的 JSON 配置可立即禁用该插件,无需重启服务:
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d '
+{
+    "uri": "/hello",
+    "plugins": {},
+    "upstream": {
+        "type": "roundrobin",
+        "nodes": {
+            "127.0.0.1:1999": 1
+        }
+    }
+}'
+```
+
+这时该插件已被禁用。
diff --git a/doc/plugins/proxy-mirror.md b/doc/plugins/proxy-mirror.md
new file mode 100644
index 0000000..3b6ee54
--- /dev/null
+++ b/doc/plugins/proxy-mirror.md
@@ -0,0 +1,97 @@
+<!--
+#
+# 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.
+#
+-->
+
+[Chinese](proxy-mirror-cn.md)
+
+# proxy-mirror
+
+The proxy-mirror plugin, which provides the ability to mirror client requests.
+
+*Note*:  The response returned by the mirror request is ignored.
+
+## Attributes
+
+|Name          |Requirement  | Type |Description|
+|------- |-----|------|------|
+|host|required|string|Specify a mirror service address, e.g. 
http://127.0.0.1:9797 (address needs to contain schema: http or https, not URI 
part)|
+
+
+### Examples
+
+#### Enable the plugin
+
+1:  enable the proxy-mirror plugin for a specific route :
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d '
+{
+    "plugins": {
+        "proxy-mirror": {
+           "host": "http://127.0.0.1:9797";
+        }
+    },
+    "upstream": {
+        "nodes": {
+            "127.0.0.1:1999": 1
+        },
+        "type": "roundrobin"
+    },
+    "uri": "/hello"
+}'
+```
+
+Test plugin:
+
+```shell
+$ curl http://127.0.0.1:9080/hello -i
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
+Content-Length: 12
+Connection: keep-alive
+Server: APISIX web server
+Date: Wed, 18 Mar 2020 13:01:11 GMT
+Last-Modified: Thu, 20 Feb 2020 14:21:41 GMT
+
+hello world
+```
+
+> Since the specified mirror address is 127.0.0.1:9797, so to verify whether 
this plugin is in effect, we need to confirm on the service with port 9797.
+> For example, we can start a simple server:  python -m SimpleHTTPServer 9797
+
+
+## Disable Plugin
+
+Remove the corresponding JSON in the plugin configuration to disable the 
plugin immediately without restarting the service:
+
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d '
+{
+    "uri": "/hello",
+    "plugins": {},
+    "upstream": {
+        "type": "roundrobin",
+        "nodes": {
+            "127.0.0.1:1999": 1
+        }
+    }
+}'
+```
+
+The plugin has been disabled now.
diff --git a/lua/apisix/core/ctx.lua b/lua/apisix/core/ctx.lua
index 31d0d89..1e5082b 100644
--- a/lua/apisix/core/ctx.lua
+++ b/lua/apisix/core/ctx.lua
@@ -48,6 +48,8 @@ do
         upstream_connection        = true,
         upstream_uri               = true,
 
+        upstream_mirror_host       = true,
+
         upstream_cache_zone        = true,
         upstream_cache_zone_info   = true,
         upstream_no_cache          = true,
diff --git a/lua/apisix/plugins/proxy-mirror.lua 
b/lua/apisix/plugins/proxy-mirror.lua
new file mode 100644
index 0000000..830f889
--- /dev/null
+++ b/lua/apisix/plugins/proxy-mirror.lua
@@ -0,0 +1,59 @@
+--
+-- 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 core          = require("apisix.core")
+
+local plugin_name   = "proxy-mirror"
+
+local schema = {
+    type = "object",
+    properties = {
+        host = {
+            type = "string",
+            pattern = [[^http(s)?:\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}]]
+                      .. [[(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:[0-9]{1,5})?$]],
+        },
+    },
+    required = {"host"},
+    minProperties = 1,
+}
+
+local _M = {
+    version = 0.1,
+    priority = 1010,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    local ok, err = core.schema.check(schema, conf)
+    if not ok then
+        return false, err
+    end
+
+    return true
+end
+
+
+function _M.rewrite(conf, ctx)
+    core.log.info("proxy mirror plugin rewrite phase, conf: ", 
core.json.delay_encode(conf))
+
+    ctx.var.upstream_mirror_host = conf.host
+end
+
+
+return _M
diff --git a/t/APISIX.pm b/t/APISIX.pm
index 4dd710d..af7f2b0 100644
--- a/t/APISIX.pm
+++ b/t/APISIX.pm
@@ -263,6 +263,7 @@ _EOC_
         }
 
         location / {
+            set \$upstream_mirror_host        '';
             set \$upstream_scheme             'http';
             set \$upstream_host               \$host;
             set \$upstream_upgrade            '';
@@ -306,6 +307,7 @@ _EOC_
             proxy_pass_header  Server;
             proxy_pass_header  Date;
             proxy_pass         
\$upstream_scheme://apisix_backend\$upstream_uri;
+            mirror             /proxy_mirror;
 
             header_filter_by_lua_block {
                 apisix.http_header_filter_phase()
@@ -341,6 +343,16 @@ _EOC_
                 apisix.http_log_phase()
             }
         }
+
+        location = /proxy_mirror {
+            internal;
+
+            if (\$upstream_mirror_host = "") {
+                return 200;
+            }
+
+            proxy_pass \$upstream_mirror_host\$request_uri;
+        }
 _EOC_
 
     $block->set_value("config", $config);
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index c5f34b7..b8771c7 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -30,7 +30,7 @@ __DATA__
 --- request
 GET /apisix/admin/plugins/list
 --- response_body_like eval
-qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection","udp-logger","wolf-rbac","proxy-cache","tcp-logger"\]/
+qr/\["limit-req","limit-count","limit-conn","key-auth","basic-auth","prometheus","node-status","jwt-auth","zipkin","ip-restriction","grpc-transcode","serverless-pre-function","serverless-post-function","openid-connect","proxy-rewrite","redirect","response-rewrite","fault-injection","udp-logger","wolf-rbac","proxy-cache","tcp-logger","proxy-mirror"\]/
 --- no_error_log
 [error]
 
diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t
index ead99df..d5a6c1d 100644
--- a/t/debug/debug-mode.t
+++ b/t/debug/debug-mode.t
@@ -63,6 +63,7 @@ loaded plugin and sort by priority: 2555 name: wolf-rbac
 loaded plugin and sort by priority: 2520 name: basic-auth
 loaded plugin and sort by priority: 2510 name: jwt-auth
 loaded plugin and sort by priority: 2500 name: key-auth
+loaded plugin and sort by priority: 1010 name: proxy-mirror
 loaded plugin and sort by priority: 1009 name: proxy-cache
 loaded plugin and sort by priority: 1008 name: proxy-rewrite
 loaded plugin and sort by priority: 1003 name: limit-conn
diff --git a/t/plugin/proxy-mirror.t b/t/plugin/proxy-mirror.t
new file mode 100644
index 0000000..728b6a6
--- /dev/null
+++ b/t/plugin/proxy-mirror.t
@@ -0,0 +1,291 @@
+#
+# 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 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_shuffle();
+no_root_location();
+log_level('info');
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $http_config = $block->http_config // <<_EOC_;
+
+    server {
+        listen 1986;
+        server_tokens off;
+
+        location / {
+            content_by_lua_block {
+                ngx.log(ngx.ERR, "uri: ", ngx.var.uri)
+                ngx.say("hello world")
+            }
+        }
+    }
+_EOC_
+
+    $block->set_value("http_config", $http_config);
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: sanity check (invalid schema)
+--- config
+       location /t {
+           content_by_lua_block {
+               local t = require("lib.test_admin").test
+               local code, body = t('/apisix/admin/routes/1',
+                    ngx.HTTP_PUT,
+                    [[{
+                        "plugins": {
+                            "proxy-mirror": {
+                               "host": "ftp://127.0.0.1:1999";
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/failed to check the configuration of plugin proxy-mirror/
+--- no_error_log
+[error]
+
+
+
+=== TEST 2: sanity check (invalid port format)
+--- config
+       location /t {
+           content_by_lua_block {
+               local t = require("lib.test_admin").test
+               local code, body = t('/apisix/admin/routes/1',
+                    ngx.HTTP_PUT,
+                    [[{
+                        "plugins": {
+                            "proxy-mirror": {
+                               "host": "http://127.0.0.1::1999";
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/failed to check the configuration of plugin proxy-mirror/
+--- no_error_log
+[error]
+
+
+
+=== TEST 3: sanity check (without schema)
+--- config
+       location /t {
+           content_by_lua_block {
+               local t = require("lib.test_admin").test
+               local code, body = t('/apisix/admin/routes/1',
+                    ngx.HTTP_PUT,
+                    [[{
+                        "plugins": {
+                            "proxy-mirror": {
+                               "host": "127.0.0.1:1999"
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/failed to check the configuration of plugin proxy-mirror/
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: sanity check (without port)
+--- config
+       location /t {
+           content_by_lua_block {
+               local t = require("lib.test_admin").test
+               local code, body = t('/apisix/admin/routes/1',
+                    ngx.HTTP_PUT,
+                    [[{
+                        "plugins": {
+                            "proxy-mirror": {
+                               "host": "http://127.0.0.1";
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 200
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+
+=== TEST 4: sanity check (include uri)
+--- config
+       location /t {
+           content_by_lua_block {
+               local t = require("lib.test_admin").test
+               local code, body = t('/apisix/admin/routes/1',
+                    ngx.HTTP_PUT,
+                    [[{
+                        "plugins": {
+                            "proxy-mirror": {
+                               "host": "http://127.0.0.1:1999/invalid_uri";
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 400
+--- response_body eval
+qr/failed to check the configuration of plugin proxy-mirror/
+--- no_error_log
+[error]
+
+
+
+=== TEST 5: sanity check (normal case)
+--- config
+       location /t {
+           content_by_lua_block {
+               local t = require("lib.test_admin").test
+               local code, body = t('/apisix/admin/routes/1',
+                    ngx.HTTP_PUT,
+                    [[{
+                        "plugins": {
+                            "proxy-mirror": {
+                               "host": "http://127.0.0.1:1986";
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uri": "/hello"
+                   }]]
+                   )
+
+               if code >= 300 then
+                   ngx.status = code
+               end
+               ngx.say(body)
+           }
+       }
+--- request
+GET /t
+--- error_code: 200
+--- response_body
+passed
+--- no_error_log
+[error]
+
+
+=== TEST 6: hit route
+--- request
+GET /hello
+--- error_code: 200
+--- response_body
+hello world
+--- error_log
+uri: /hello
+

Reply via email to