You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2021/05/31 11:34:12 UTC
[apisix-java-plugin-runner] branch main updated: docs: add
the-internal-of-apisix-java-plugin-runner.md,
development.md and how-it-works.md (#19)
This is an automated email from the ASF dual-hosted git repository.
spacewander pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/apisix-java-plugin-runner.git
The following commit(s) were added to refs/heads/main by this push:
new ecf80b2 docs: add the-internal-of-apisix-java-plugin-runner.md, development.md and how-it-works.md (#19)
ecf80b2 is described below
commit ecf80b2f1838b7f367a2909d86461636d861f5c8
Author: tzssangglass <tz...@gmail.com>
AuthorDate: Mon May 31 19:34:02 2021 +0800
docs: add the-internal-of-apisix-java-plugin-runner.md, development.md and how-it-works.md (#19)
---
docs/development.md | 119 +++++++++++++++++
docs/how-it-works.md | 87 ++++++++++++
.../the-internal-of-apisix-java-plugin-runner.png | Bin 0 -> 58191 bytes
docs/the-internal-of-apisix-java-plugin-runner.md | 58 ++++++++
runner-core/pom.xml | 5 +
.../runner/codec/impl/FlatBuffersEncoderTest.java | 4 +-
.../plugin/runner/handler/A6ConfigHandlerTest.java | 14 +-
.../runner/handler/A6HttpCallHandlerTest.java | 47 +++----
.../apache/apisix/plugin/runner/HttpRequest.java | 148 +++++++++++++++++++--
.../apache/apisix/plugin/runner/HttpResponse.java | 53 +++++++-
.../apisix/plugin/runner/filter/PluginFilter.java | 3 +-
.../plugin/runner/filter/PluginFilterChain.java | 7 +-
runner-starter/src/main/resources/application.yaml | 4 +-
.../runner/filter/RewriteRequestDemoFilter.java | 33 +++--
.../runner/filter/StopRequestDemoFilter.java | 31 +++--
15 files changed, 531 insertions(+), 82 deletions(-)
diff --git a/docs/development.md b/docs/development.md
index e69de29..aaa5ae9 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -0,0 +1,119 @@
+Development
+
+This document explains how to get started to develop the apisix-java-plugin-runner.
+
+Prerequisites
+-------------
+
+* JDK 8
+* APISIX 2.6.0
+* Clone the [apisix-java-plugin-runner](https://github.com/apache/apisix-java-plugin-runner) project.
+* Refer to [Debug](how-it-works.md#debug) to build the debug environment.
+
+Install
+-------
+
+```shell
+cd /path/to/apisix-java-plugin-runner
+./mvnw install
+```
+
+Write Filter
+------------
+
+Refer to the code in the [sample](https://github.com/apache/apisix-java-plugin-runner/tree/main/sample)
+to learn how to extend `PluginFilter`, define the order, rewrite requests and stop requests.
+
+#### Code Location
+
+You need to put the code in [runner-plugin-sdk](https://github.com/apache/apisix-java-plugin-runner/tree/main/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner)
+so that the `apisix-java-plugin-runner.jar` will contain the filter implementation class you wrote when you package it.
+
+#### The order of filter execution
+
+The order of execution of the filter in the runner is determined by the index of the `conf` array in the `ext-plugin-pre-req` or `ext-plugin-post-req` configuration.
+
+#### The name of filter execution
+
+The requests go through filters that are dynamically configured on APISIX.
+For example, if the following configuration is done on APISIX
+
+```shell
+curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
+{
+ "uri":"/hello",
+ "plugins":{
+ "ext-plugin-pre-req":{
+ "conf":[
+ {
+ "name":"FooFilter",
+ "value":"bar"
+ }
+ ]
+ }
+ },
+ "upstream":{
+ "nodes":{
+ "127.0.0.1:1980":1
+ },
+ "type":"roundrobin"
+ }
+}'
+```
+
+apisix-java-plugin-runner will look for implementation classes named `FooFilter`,
+and the name of each filter's implementation class is the return value of its overridden function `public String name()`.
+
+
+#### Rewrite Request
+
+If you perform the following function call in the filter chain of the implementation class
+
+* request.setPath()
+* request.setHeader()
+* request.setArg()
+
+this means to rewrit the current request, the upstream server will receive
+the relevant parameters rewritten here.
+
+#### Stop Request
+
+If you perform the following function call in the filter chain of the implementation class
+
+* response.setStatusCode()
+* response.setHeader()
+* response.setBody()
+
+this means to stop the current request, the client will receive
+the relevant parameters generated here.
+
+Test
+----
+
+### Run Unit Test Suites
+
+```shell
+cd /path/to/apisix-java-plugin-runner
+ ./mvnw test
+```
+
+
+### Mimic practical environment
+
+If you want to mimic the practical environment, you need to configure the route on APISIX
+by having the request go through the filter you want to test, for example
+
+```json
+"plugins":{
+ "ext-plugin-pre-req":{
+ "conf":[
+ {
+ "name":"FooFilter",
+ "value":"bar"
+ }
+ ]
+ }
+}
+```
+
+and then make a request to APISIX to trigger the route.
diff --git a/docs/how-it-works.md b/docs/how-it-works.md
index e69de29..2ddf57c 100644
--- a/docs/how-it-works.md
+++ b/docs/how-it-works.md
@@ -0,0 +1,87 @@
+# How It Works
+
+This article explains how apisix-java-plugin-runner collaborate with [Apache APISIX](https://apisix.apache.org) to run plugins written in java.
+
+## Run Mode
+
+apisix-java-plugin-runner can be run alone or bundled with Apache APISIX.
+It depends on whether you need to debug it or run it.
+
+### Debug
+
+If you are developing a new plugin and need to debug the code, then you can run the main class
+[PluginRunnerApplication](https://github.com/apache/apisix-java-plugin-runner/blob/main/runner-starter/src/main/java/org/apache/apisix/plugin/runner/PluginRunnerApplication.java),
+and before start, you need to set the following two environment variables:
+
+- APISIX_LISTEN_ADDRESS: apisix-java-plugin-runner and APISIX for inter-process communication (Unix Domain Socket) socket type file address.
+ And do not need to actively create this file, apisix-java-plugin-runner will automatically create this file when it starts.
+- APISIX_CONF_EXPIRE_TIME: the time that APISIX's configuration is cached in the apisix-java-plugin-runner process.
+
+For example, if you start apisix-java-plugin-runner as a jar package, pass the environment variables as below
+
+```shell
+java -jar -DAPISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock -DAPISIX_CONF_EXPIRE_TIME=3600 /path/to/apisix-java-plugin-runner.jar
+```
+
+Note: Refer to [apisix-java-plugin-runner.jar](#run) to get it.
+
+and add the following configure in the `config.yaml` file of APISIX
+
+```yaml
+ext-plugin:
+ path_for_test: /tmp/runner.sock
+```
+
+The `/tmp/runner.sock` is the address of the file where apisix-java-plugin-runner
+and APISIX communicate between processes and must be consistent.
+
+Note: If you see some error logs like
+
+```
+phase_func(): failed to connect to the unix socket unix:/tmp/runner.sock: permission denied
+```
+
+in the `error.log` of APISIX, you can change the permissions of this file for debug, execute commands like
+
+```shell
+chmod 777 /tmp/runner.sock
+```
+
+### Run
+
+No environment variables need to be set in Run mode, execute
+
+```shell
+cd /path/to/apisix-java-plugin-runner
+ ./mvnw package
+```
+
+to built apisix-java-plugin-runner as a jar package, then you will see the `dist` directory, execute
+
+```
+cd dist
+tar -zxvf apache-apisix-runner-bin.tar.gz
+```
+
+the layout of files in the `dist` directory is as below
+
+```
+dist
+├── apache-apisix-runner-bin.tar.gz
+└── apisix-runner-bin
+ ├── apisix-java-plugin-runner.jar
+ ├── bin
+ │ ├── shutdown.sh
+ │ └── startup.sh
+ ├── LICENSE
+ ├── NOTICE
+ └── README.md
+
+```
+
+then add the following configure in the `config.yaml` file of APISIX
+
+```yaml
+ext-plugin:
+ cmd: ['java', '-jar', '-Xmx4g', '-Xms4g', '/path/to/apisix-runner-bin/apisix-java-plugin-runner.jar']
+```
diff --git a/docs/images/the-internal-of-apisix-java-plugin-runner.png b/docs/images/the-internal-of-apisix-java-plugin-runner.png
new file mode 100644
index 0000000..458e8a3
Binary files /dev/null and b/docs/images/the-internal-of-apisix-java-plugin-runner.png differ
diff --git a/docs/the-internal-of-apisix-java-plugin-runner.md b/docs/the-internal-of-apisix-java-plugin-runner.md
index e69de29..c8cf410 100644
--- a/docs/the-internal-of-apisix-java-plugin-runner.md
+++ b/docs/the-internal-of-apisix-java-plugin-runner.md
@@ -0,0 +1,58 @@
+The Internal of apisix java plugin runner
+
+This article explains the internal design of apisix-java-plugin-runner.
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Communication](#communication)
+- [Serialization](#serialization)
+- [Codec](#codec)
+
+## Overview
+
+The apisix-java-plugin-runner designed as a TCP server built using [reactor-netty](https://github.com/reactor/reactor-netty),
+it provides a `PluginFilter` interface for users to implement.
+
+Users only need to focus on their business logic, not on the details of how the apisix java plugin runner communicates with APISIX.
+
+The inter-process communication between them is depicted by the following diagram.
+
+![the-internal-of-apisix-java-plugin-runner](./images/the-internal-of-apisix-java-plugin-runner.png)
+
+## Communication
+
+apisix-java-plugin-runner and APISIX use the Unix Domain Socket for inter-process communication,
+so they need to be deployed in the same instance.
+
+apisix-java-plugin-runner is managed by APISIX. APISIX starts the apisix-java-plugin-runner when it starts and ends it when it
+ends. if the apisix-java-plugin-runner quits in the middle, APISIX will restart it automatically.
+
+## Serialization
+
+Refer to [flatbuffers](https://github.com/google/flatbuffers)
+
+FlatBuffers is a cross platform serialization library architected for maximum memory efficiency.
+It allows you to directly access serialized data without parsing/unpacking it first, while still having great forward/backward compatibility.
+
+You can refer to the [ext-plugin.fbs](https://github.com/api7/ext-plugin-proto/blob/main/ext-plugin.fbs)
+ schema file to see how Lua and Java layout the serialized objects.
+
+## Codec
+
+apisix-java-plugin-runner and APISIX use a private binary protocol for coding and decoding.
+The protocol format is
+
+```
+1 byte of type + 3 bytes of length + data
+```
+
+The type can be 0 ~ 7, and the length can be [0, 8M). The length of data is determined by length.
+
+The current type takes the following values
+
+* 0 means error
+* 1 means prepare_conf
+* 2 means http_req_call
+
+The binary data generated by the flatbuffer serialization is placed in the data segment.
diff --git a/runner-core/pom.xml b/runner-core/pom.xml
index 5997c7a..6594fd8 100644
--- a/runner-core/pom.xml
+++ b/runner-core/pom.xml
@@ -91,5 +91,10 @@
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/runner-core/src/test/java/org/apache/apisix/plugin/runner/codec/impl/FlatBuffersEncoderTest.java b/runner-core/src/test/java/org/apache/apisix/plugin/runner/codec/impl/FlatBuffersEncoderTest.java
index 8a9e38b..dbba24f 100644
--- a/runner-core/src/test/java/org/apache/apisix/plugin/runner/codec/impl/FlatBuffersEncoderTest.java
+++ b/runner-core/src/test/java/org/apache/apisix/plugin/runner/codec/impl/FlatBuffersEncoderTest.java
@@ -111,7 +111,7 @@ class FlatBuffersEncoderTest {
HttpResponse httpResponse = new HttpResponse(0L);
// set path, args, req header means rewrite request
httpResponse.setPath("/hello");
- httpResponse.setArgs("foo", "bar");
+ httpResponse.setArg("foo", "bar");
httpResponse.setReqHeader("Server", "APISIX");
ByteBuffer result = flatBuffersEncoder.encode(httpResponse);
result.position(4);
@@ -156,7 +156,7 @@ class FlatBuffersEncoderTest {
HttpResponse httpResponse = new HttpResponse(0L);
// set path, args, req header means rewrite request
httpResponse.setPath("/hello");
- httpResponse.setArgs("foo", "bar");
+ httpResponse.setArg("foo", "bar");
httpResponse.setReqHeader("Server", "APISIX");
// set status, body, resp header means stop request
diff --git a/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6ConfigHandlerTest.java b/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6ConfigHandlerTest.java
index 1f0d982..6ec9c12 100644
--- a/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6ConfigHandlerTest.java
+++ b/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6ConfigHandlerTest.java
@@ -65,11 +65,6 @@ class A6ConfigHandlerTest {
public Mono<Void> filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
return chain.filter(request, response);
}
-
- @Override
- public int getOrder() {
- return 0;
- }
});
filters.put("CatFilter", new PluginFilter() {
@@ -82,11 +77,6 @@ class A6ConfigHandlerTest {
public Mono<Void> filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
return chain.filter(request, response);
}
-
- @Override
- public int getOrder() {
- return 1;
- }
});
cache = CacheBuilder.newBuilder().expireAfterWrite(3600, TimeUnit.SECONDS).maximumSize(1000).build();
a6ConfigHandler = new A6ConfigHandler(cache, filters);
@@ -112,7 +102,7 @@ class A6ConfigHandlerTest {
A6Conf config = cache.getIfPresent(0L);
Assertions.assertNotNull(config.getChain());
Assertions.assertEquals(config.getChain().getFilters().size(), 1);
- Assertions.assertEquals(config.getChain().getFilters().get(0).getOrder(), 0);
+ Assertions.assertEquals(config.getChain().getIndex(), 0);
Assertions.assertEquals(config.get("FooFilter"), "Bar");
}
@@ -141,8 +131,6 @@ class A6ConfigHandlerTest {
A6Conf config = cache.getIfPresent(0L);
Assertions.assertEquals(config.getChain().getFilters().size(), 2);
- Assertions.assertEquals(config.getChain().getFilters().get(0).getOrder(), 0);
- Assertions.assertEquals(config.getChain().getFilters().get(1).getOrder(), 1);
}
@Test
diff --git a/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6HttpCallHandlerTest.java b/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6HttpCallHandlerTest.java
index f55b8d1..c323fb3 100644
--- a/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6HttpCallHandlerTest.java
+++ b/runner-core/src/test/java/org/apache/apisix/plugin/runner/handler/A6HttpCallHandlerTest.java
@@ -20,6 +20,7 @@ package org.apache.apisix.plugin.runner.handler;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.flatbuffers.FlatBufferBuilder;
+import com.google.gson.Gson;
import io.github.api7.A6.Err.Code;
import io.github.api7.A6.HTTPReqCall.Action;
import io.github.api7.A6.TextEntry;
@@ -67,10 +68,15 @@ class A6HttpCallHandlerTest {
}
@Override
+ @SuppressWarnings("unchecked")
public Mono<Void> filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
- logger.info("do filter: FooFilter, order: {}", getOrder());
+ logger.info("do filter: FooFilter, order: {}", chain.getIndex());
logger.info("do filter: FooFilter, config: {}", request.getConfig(this));
-
+ Gson gson = new Gson();
+ Map<String, Object> conf = new HashMap<>();
+ conf = gson.fromJson(request.getConfig(this), conf.getClass());
+ logger.info("do filter: FooFilter, conf_key1 value: {}", conf.get("conf_key1"));
+ logger.info("do filter: FooFilter, conf_key2 value: {}", conf.get("conf_key2"));
if (!Objects.isNull(request.getPath())) {
logger.info("do filter: path: {}", request.getPath());
}
@@ -82,8 +88,8 @@ class A6HttpCallHandlerTest {
}
}
- if (!Objects.isNull(request.getHeaders())) {
- for (Map.Entry<String, String> header : request.getHeaders().entrySet()) {
+ if (!Objects.isNull(request.getHeader())) {
+ for (Map.Entry<String, String> header : request.getHeader().entrySet()) {
logger.info("do filter: header key: {}", header.getKey());
logger.info("do filter: header value: {}", header.getValue());
}
@@ -96,10 +102,6 @@ class A6HttpCallHandlerTest {
return chain.filter(request, response);
}
- @Override
- public int getOrder() {
- return 0;
- }
});
filters.put("CatFilter", new PluginFilter() {
@@ -110,28 +112,25 @@ class A6HttpCallHandlerTest {
@Override
public Mono<Void> filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
- logger.info("do filter: CatFilter, order: {}", getOrder());
+ logger.info("do filter: CatFilter, order: {}", chain.getIndex());
logger.info("do filter: CatFilter, config: {}", request.getConfig(this));
response.setStatusCode(401);
return chain.filter(request, response);
}
- @Override
- public int getOrder() {
- return 1;
- }
});
cache = CacheBuilder.newBuilder().expireAfterWrite(3600, TimeUnit.SECONDS).maximumSize(1000).build();
FlatBufferBuilder builder = new FlatBufferBuilder();
- int cat = builder.createString("CatFilter");
- int dog = builder.createString("Dog");
- int filter2 = TextEntry.createTextEntry(builder, cat, dog);
int foo = builder.createString("FooFilter");
- int bar = builder.createString("Bar");
+ int bar = builder.createString("{\"conf_key1\":\"conf_value1\",\"conf_key2\":2}");
int filter1 = TextEntry.createTextEntry(builder, foo, bar);
+ int cat = builder.createString("CatFilter");
+ int dog = builder.createString("Dog");
+ int filter2 = TextEntry.createTextEntry(builder, cat, dog);
+
int confVector = io.github.api7.A6.PrepareConf.Req.createConfVector(builder, new int[]{filter1, filter2});
io.github.api7.A6.PrepareConf.Req.startReq(builder);
io.github.api7.A6.PrepareConf.Req.addConf(builder, confVector);
@@ -178,9 +177,11 @@ class A6HttpCallHandlerTest {
HttpRequest request = new HttpRequest(req);
HttpResponse response = new HttpResponse(1L);
a6HttpCallHandler.handle(request, response);
- Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, order: 0"));
- Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, config: Bar"));
- Assertions.assertTrue(capturedOutput.getOut().contains("do filter: CatFilter, order: 1"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, order: 1"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, config: {\"conf_key1\":\"conf_value1\",\"conf_key2\":2}"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, conf_key1 value: conf_value1"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, conf_key2 value: 2.0"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: CatFilter, order: 2"));
Assertions.assertTrue(capturedOutput.getOut().contains("do filter: CatFilter, config: Dog"));
}
@@ -215,8 +216,8 @@ class A6HttpCallHandlerTest {
HttpRequest request = new HttpRequest(req);
HttpResponse response = new HttpResponse(1L);
a6HttpCallHandler.handle(request, response);
- Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, order: 0"));
- Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, config: Bar"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, order: 1"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: FooFilter, config: {\"conf_key1\":\"conf_value1\",\"conf_key2\":2}"));
Assertions.assertTrue(capturedOutput.getOut().contains("do filter: path: /path"));
Assertions.assertTrue(capturedOutput.getOut().contains("do filter: arg key: argKey"));
@@ -225,7 +226,7 @@ class A6HttpCallHandlerTest {
Assertions.assertTrue(capturedOutput.getOut().contains("do filter: header value: headerValue"));
Assertions.assertTrue(capturedOutput.getOut().contains("do filter: method: GET"));
- Assertions.assertTrue(capturedOutput.getOut().contains("do filter: CatFilter, order: 1"));
+ Assertions.assertTrue(capturedOutput.getOut().contains("do filter: CatFilter, order: 2"));
Assertions.assertTrue(capturedOutput.getOut().contains("do filter: CatFilter, config: Dog"));
}
diff --git a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpRequest.java b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpRequest.java
index 1255507..c0d60b8 100644
--- a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpRequest.java
+++ b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpRequest.java
@@ -20,9 +20,9 @@ package org.apache.apisix.plugin.runner;
import io.github.api7.A6.HTTPReqCall.Req;
import io.github.api7.A6.TextEntry;
import org.apache.apisix.plugin.runner.filter.PluginFilter;
+import org.springframework.util.CollectionUtils;
import java.nio.ByteBuffer;
-import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -51,6 +51,12 @@ public class HttpRequest implements A6Request {
this.req = req;
}
+ /**
+ * Gets current filter config.
+ *
+ * @param filter the filter
+ * @return the config
+ */
public String getConfig(PluginFilter filter) {
for (int i = 0; i < config.confLength(); i++) {
TextEntry conf = config.conf(i);
@@ -69,6 +75,11 @@ public class HttpRequest implements A6Request {
return requestId;
}
+ /**
+ * Gets source ip.
+ *
+ * @return the source ip
+ */
public String getSourceIP() {
if (Objects.isNull(sourceIP)) {
StringBuilder builder = new StringBuilder();
@@ -81,6 +92,11 @@ public class HttpRequest implements A6Request {
return sourceIP;
}
+ /**
+ * Gets method.
+ *
+ * @return the method
+ */
public Method getMethod() {
if (Objects.isNull(method)) {
method = Method.values()[req.method()];
@@ -88,6 +104,11 @@ public class HttpRequest implements A6Request {
return method;
}
+ /**
+ * Gets path.
+ *
+ * @return the path
+ */
public String getPath() {
if (Objects.isNull(path)) {
path = req.path();
@@ -95,11 +116,27 @@ public class HttpRequest implements A6Request {
return path;
}
+ /**
+ * Rewrite path.
+ *
+ * @param path the path
+ */
public void setPath(String path) {
response.setPath(path);
}
- public Map<String, String> getHeaders() {
+ /**
+ * Gets all headers.
+ * <p>Examples:</p>
+ *
+ * <pre>
+ * {@code
+ * request.getHeaders()
+ * }
+ * </pre>
+ * @return the all headers
+ */
+ public Map<String, String> getHeader() {
if (Objects.isNull(headers)) {
headers = new HashMap<>();
for (int i = 0; i < req.headersLength(); i++) {
@@ -110,10 +147,61 @@ public class HttpRequest implements A6Request {
return headers;
}
+ /**
+ * Gets the specified header
+ *
+ * <p>Examples:</p>
+ *
+ * <pre>
+ * {@code
+ * request.getHeader("Content-Type");
+ * }
+ * </pre>
+ *
+ * @param headerName the header name
+ * @return the header value or null
+ */
+ public String getHeader(String headerName) {
+ Map<String, String> headers = getHeader();
+ if (!CollectionUtils.isEmpty(headers)) {
+ for (Map.Entry<String, String> header : headers.entrySet()) {
+ if (header.getKey().equals(headerName)) {
+ return header.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Add, rewrite or delete the specified header
+ * <p>Examples:</p>
+ *
+ * <pre>
+ * {@code
+ *
+ * add new header
+ * request.setHeader("New-Header", "new header value");
+ *
+ * overwrite existing header
+ * request.setHeader("Accept", "application/json");
+ *
+ * delete existing header
+ * request.setHeader("Accept", null);
+ * }
+ * </pre>
+ * @param headerKey the header key
+ * @param headerValue the header value
+ */
public void setHeader(String headerKey, String headerValue) {
response.setReqHeader(headerKey, headerValue);
}
+ /**
+ * Gets all args.
+ *
+ * @return the args
+ */
public Map<String, String> getArgs() {
if (Objects.isNull(args)) {
args = new HashMap<>();
@@ -125,20 +213,56 @@ public class HttpRequest implements A6Request {
return args;
}
- public void setArg(String argKey, String argValue) {
- response.setArgs(argKey, argValue);
- }
- public Map getParameterMap() {
- return null;
- }
-
- public Enumeration getParameters() {
+ /**
+ * Gets the specified arg.
+ *
+ * <p>Examples:</p>
+ *
+ * <pre>
+ * {@code
+ * request.getArg("foo");
+ * }
+ * </pre>
+ *
+ * @param argName the arg name
+ * @return the arg
+ */
+ public String getArg(String argName) {
+ Map<String, String> args = getArgs();
+ if (!CollectionUtils.isEmpty(args)) {
+ for (Map.Entry<String, String> arg : args.entrySet()) {
+ if (arg.getKey().equals(argName)) {
+ return arg.getValue();
+ }
+ }
+ }
return null;
}
- public String[] getParameterValues(String name) {
- return null; // todo
+ /**
+ * Add, rewrite or delete the specified header
+ * <p>Examples:</p>
+ *
+ *
+ * <pre>
+ * {@code
+ *
+ * add new arg
+ * request.setArg("foo", "bar");
+ *
+ * overwrite existing arg
+ * request.setArg("foo", "bar");
+ *
+ * delete existing header
+ * request.setArg("foo", null);
+ * }
+ * </pre>
+ * @param argKey the arg key
+ * @param argValue the arg value
+ */
+ public void setArg(String argKey, String argValue) {
+ response.setArg(argKey, argValue);
}
public long getConfToken() {
diff --git a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpResponse.java b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpResponse.java
index 2c861be..52eaa64 100644
--- a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpResponse.java
+++ b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/HttpResponse.java
@@ -73,7 +73,13 @@ public class HttpResponse implements A6Response {
reqHeaders.put(headerKey, headerValue);
}
- public void setArgs(String argKey, String argValue) {
+ /**
+ * Sets arg.
+ *
+ * @param argKey the arg key
+ * @param argValue the arg value
+ */
+ public void setArg(String argKey, String argValue) {
actionType = ActionType.Rewrite;
if (Objects.isNull(args)) {
args = new HashMap<>();
@@ -81,11 +87,22 @@ public class HttpResponse implements A6Response {
args.put(argKey, argValue);
}
+ /**
+ * Sets path.
+ *
+ * @param path the path
+ */
public void setPath(String path) {
actionType = ActionType.Rewrite;
this.path = path;
}
+ /**
+ * Sets header.
+ *
+ * @param headerKey the header key
+ * @param headerValue the header value
+ */
public void setHeader(String headerKey, String headerValue) {
actionType = ActionType.Stop;
if (Objects.isNull(respHeaders)) {
@@ -94,11 +111,21 @@ public class HttpResponse implements A6Response {
respHeaders.put(headerKey, headerValue);
}
+ /**
+ * Sets body.
+ *
+ * @param body the body(string)
+ */
public void setBody(String body) {
actionType = ActionType.Stop;
this.body = body;
}
+ /**
+ * Sets status code.
+ *
+ * @param statusCode the status code
+ */
public void setStatusCode(int statusCode) {
actionType = ActionType.Stop;
this.statusCode = statusCode;
@@ -186,8 +213,16 @@ public class HttpResponse implements A6Response {
for (Map.Entry<String, String> header : reqHeaders.entrySet()) {
int i = -1;
int key = builder.createString(header.getKey());
- int value = builder.createString(header.getValue());
- int text = TextEntry.createTextEntry(builder, key, value);
+ int value = 0;
+ if (!Objects.isNull(header.getValue())) {
+ value = builder.createString(header.getValue());
+ }
+ TextEntry.startTextEntry(builder);
+ TextEntry.addName(builder, key);
+ if (!Objects.isNull(header.getValue())) {
+ TextEntry.addValue(builder, value);
+ }
+ int text = TextEntry.endTextEntry(builder);
headerTexts[++i] = text;
}
headerIndex = Rewrite.createHeadersVector(builder, headerTexts);
@@ -199,8 +234,16 @@ public class HttpResponse implements A6Response {
for (Map.Entry<String, String> arg : args.entrySet()) {
int i = -1;
int key = builder.createString(arg.getKey());
- int value = builder.createString(arg.getValue());
- int text = TextEntry.createTextEntry(builder, key, value);
+ int value = 0;
+ if (!Objects.isNull(arg.getValue())) {
+ value = builder.createString(arg.getValue());
+ }
+ TextEntry.startTextEntry(builder);
+ TextEntry.addName(builder, key);
+ if (!Objects.isNull(arg.getValue())) {
+ TextEntry.addValue(builder, value);
+ }
+ int text = TextEntry.endTextEntry(builder);
argTexts[++i] = text;
}
argsIndex = Rewrite.createArgsVector(builder, argTexts);
diff --git a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilter.java b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilter.java
index a7109a6..22f1622 100644
--- a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilter.java
+++ b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilter.java
@@ -19,10 +19,9 @@ package org.apache.apisix.plugin.runner.filter;
import org.apache.apisix.plugin.runner.HttpRequest;
import org.apache.apisix.plugin.runner.HttpResponse;
-import org.springframework.core.Ordered;
import reactor.core.publisher.Mono;
-public interface PluginFilter extends Ordered {
+public interface PluginFilter {
String name();
diff --git a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilterChain.java b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilterChain.java
index e1a8960..0b15967 100644
--- a/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilterChain.java
+++ b/runner-plugin-sdk/src/main/java/org/apache/apisix/plugin/runner/filter/PluginFilterChain.java
@@ -19,7 +19,6 @@ package org.apache.apisix.plugin.runner.filter;
import org.apache.apisix.plugin.runner.HttpRequest;
import org.apache.apisix.plugin.runner.HttpResponse;
-import org.springframework.core.OrderComparator;
import reactor.core.publisher.Mono;
import java.util.List;
@@ -29,9 +28,11 @@ public class PluginFilterChain {
private final List<PluginFilter> filters;
+ public int getIndex() {
+ return index;
+ }
+
public PluginFilterChain(List<PluginFilter> filters) {
- // sort filters in order
- OrderComparator.sort(filters);
this.filters = filters;
this.index = 0;
}
diff --git a/runner-starter/src/main/resources/application.yaml b/runner-starter/src/main/resources/application.yaml
index 5f4d487..4e681b1 100644
--- a/runner-starter/src/main/resources/application.yaml
+++ b/runner-starter/src/main/resources/application.yaml
@@ -12,7 +12,9 @@
# 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.
-
+logging:
+ level:
+ root: debug
cache.config:
expired: ${APISIX_CONF_EXPIRE_TIME}
capacity: 1000
diff --git a/sample/src/main/java/org/apache/apisix/plugin/runner/filter/RewriteRequestDemoFilter.java b/sample/src/main/java/org/apache/apisix/plugin/runner/filter/RewriteRequestDemoFilter.java
index 7ad93da..406e605 100644
--- a/sample/src/main/java/org/apache/apisix/plugin/runner/filter/RewriteRequestDemoFilter.java
+++ b/sample/src/main/java/org/apache/apisix/plugin/runner/filter/RewriteRequestDemoFilter.java
@@ -17,11 +17,15 @@
package org.apache.apisix.plugin.runner.filter;
+import com.google.gson.Gson;
import org.apache.apisix.plugin.runner.HttpRequest;
import org.apache.apisix.plugin.runner.HttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
+import java.util.HashMap;
+import java.util.Map;
+
@Component
public class RewriteRequestDemoFilter implements PluginFilter {
@@ -56,25 +60,32 @@ public class RewriteRequestDemoFilter implements PluginFilter {
@Override
public Mono<Void> filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
+ /*
+ * If the conf you configured is of type json, you can convert it to Map or json.
+ */
+
+ String configStr = request.getConfig(this);
+ Gson gson = new Gson();
+ Map<String, Object> conf = new HashMap<>();
+ conf = gson.fromJson(configStr, conf.getClass());
+
+ /*
+ * You can use the parameters in the configuration.
+ */
+
// note: the path to the rewrite must start with '/'
- request.setPath("/get");
- request.setHeader("new-header", "header_by_runner");
+ request.setPath((String) conf.get("rewrite_path"));
+ request.setHeader((String) conf.get("conf_header_name"), (String) conf.get("conf_header_value"));
/* note: The value of the parameter is currently a string type.
If you need the json type, you need the upstream service to parse the string value to json.
- For example, if the arg is set as follows
+ For example, if the arg is set as below
request.setArg("new arg", "{\"key1\":\"value1\",\"key2\":2}");
- The arg received by the upstream service will be as follows
+ The arg received by the upstream service will be as below
"new arg": "{\"key1\":\"value1\",\"key2\":2}"
*/
- request.setArg("new arg", "{\"key1\":\"value1\",\"key2\":2}");
+ request.setArg((String) conf.get("conf_arg_name"), (String) conf.get("conf_arg_value"));
return chain.filter(request, response);
}
-
- @Override
- public int getOrder() {
- //The order of filter execution in runner is determined by the order here, the smaller the order number, the higher the execution order.
- return 0;
- }
}
diff --git a/sample/src/main/java/org/apache/apisix/plugin/runner/filter/StopRequestDemoFilter.java b/sample/src/main/java/org/apache/apisix/plugin/runner/filter/StopRequestDemoFilter.java
index d190d02..97ae800 100644
--- a/sample/src/main/java/org/apache/apisix/plugin/runner/filter/StopRequestDemoFilter.java
+++ b/sample/src/main/java/org/apache/apisix/plugin/runner/filter/StopRequestDemoFilter.java
@@ -17,11 +17,15 @@
package org.apache.apisix.plugin.runner.filter;
+import com.google.gson.Gson;
import org.apache.apisix.plugin.runner.HttpRequest;
import org.apache.apisix.plugin.runner.HttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
+import java.util.HashMap;
+import java.util.Map;
+
@Component
public class StopRequestDemoFilter implements PluginFilter {
@Override
@@ -31,17 +35,29 @@ public class StopRequestDemoFilter implements PluginFilter {
@Override
public Mono<Void> filter(HttpRequest request, HttpResponse response, PluginFilterChain chain) {
- response.setStatusCode(401);
- response.setHeader("new-header", "header_by_runner");
+ /*
+ * If the conf you configured is of type json, you can convert it to Map or json.
+ */
+
+ String configStr = request.getConfig(this);
+ Gson gson = new Gson();
+ Map<String, Object> conf = new HashMap<>();
+ conf = gson.fromJson(configStr, conf.getClass());
+
+ /*
+ * You can use the parameters in the configuration.
+ */
+ response.setStatusCode((Integer) conf.get("stop_response_code"));
+ response.setHeader((String) conf.get("stop_response_header_name"), (String) conf.get("stop_response_header_value"));
/* note: The body is currently a string type.
If you need the json type, you need to escape the json content here.
- For example, if the body is set as follows
+ For example, if the body is set as below
"{\"key1\":\"value1\",\"key2\":2}"
- The body received by the client will be as follows
+ The body received by the client will be as below
{"key1":"value1","key2":2}
*/
- response.setBody("{\"key1\":\"value1\",\"key2\":2}");
+ response.setBody((String) conf.get("stop_response_body"));
/* Using the above code, the client side receives the following
@@ -57,9 +73,4 @@ public class StopRequestDemoFilter implements PluginFilter {
*/
return chain.filter(request, response);
}
-
- @Override
- public int getOrder() {
- return 1;
- }
}