You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by li...@apache.org on 2024/02/27 14:24:53 UTC
(shenyu) branch master updated: [ISSUE #5427] add AbstractShenyuWasmPluginTest (#5450)
This is an automated email from the ASF dual-hosted git repository.
likeguo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 5d9fb45005 [ISSUE #5427] add AbstractShenyuWasmPluginTest (#5450)
5d9fb45005 is described below
commit 5d9fb450050e3e54bf6e5a758abb0dc2ce67fb1d
Author: loongs-zhang <zh...@apache.org>
AuthorDate: Tue Feb 27 22:24:47 2024 +0800
[ISSUE #5427] add AbstractShenyuWasmPluginTest (#5450)
* [ISSUE #5427] add AbstractShenyuWasmPluginTest
* fix checkstyle
* fix checkstyle
* fix checkstyle
---------
Co-authored-by: xiaoyu <xi...@apache.org>
---
.../plugin/wasm/base/AbstractShenyuWasmPlugin.java | 10 +
.../wasm/base/AbstractShenyuWasmPluginTest.java | 347 +++++++++++++++++++++
...tShenyuWasmPluginTest$TestShenyuWasmPlugin.wasm | Bin 0 -> 1679614 bytes
.../src/test/rust-shenyu-wasm-plugin/Cargo.toml | 26 ++
.../src/test/rust-shenyu-wasm-plugin/README.md | 19 ++
.../src/test/rust-shenyu-wasm-plugin/src/lib.rs | 38 +++
6 files changed, 440 insertions(+)
diff --git a/shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPlugin.java b/shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPlugin.java
index ae8198ebb0..1f8ddbabc8 100644
--- a/shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPlugin.java
+++ b/shenyu-plugin/shenyu-plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPlugin.java
@@ -36,6 +36,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
+import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -70,6 +71,15 @@ public abstract class AbstractShenyuWasmPlugin extends AbstractShenyuPlugin {
return null;
}
+ /**
+ * use this in wasmCallJavaFunc.
+ *
+ * @return the ByteBuffer
+ */
+ public ByteBuffer getBuffer() {
+ return wasmLoader.getBuffer();
+ }
+
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange,
final ShenyuPluginChain chain,
diff --git a/shenyu-plugin/shenyu-plugin-wasm-base/src/test/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPluginTest.java b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPluginTest.java
new file mode 100644
index 0000000000..4adf369869
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPluginTest.java
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ */
+
+package org.apache.shenyu.plugin.wasm.base;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+import org.apache.shenyu.common.config.ShenyuConfig;
+import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.PluginData;
+import org.apache.shenyu.common.dto.RuleData;
+import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.SelectorTypeEnum;
+import org.apache.shenyu.common.enums.TrieCacheTypeEnum;
+import org.apache.shenyu.common.enums.TrieMatchModeEnum;
+import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
+import org.apache.shenyu.plugin.base.cache.BaseDataCache;
+import org.apache.shenyu.plugin.base.cache.MatchDataCache;
+import org.apache.shenyu.plugin.base.trie.ShenyuTrie;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
+import org.springframework.mock.web.server.MockServerWebExchange;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * The Abstract shenyu plugin test.
+ */
+public final class AbstractShenyuWasmPluginTest {
+
+ private RuleData ruleData;
+
+ private PluginData pluginData;
+
+ private SelectorData selectorData;
+
+ private ConditionData conditionData;
+
+ private ServerWebExchange exchange;
+
+ private TestShenyuWasmPlugin testShenyuWasmPlugin;
+
+ private ShenyuPluginChain shenyuPluginChain;
+
+ @BeforeEach
+ public void setUp() {
+ mockShenyuConfig();
+ this.ruleData = RuleData.builder()
+ .id("1")
+ .pluginName("SHENYU")
+ .selectorId("1")
+ .enabled(true)
+ .loged(true)
+ .matchRestful(false)
+ .sort(1).build();
+ this.conditionData = new ConditionData();
+ this.conditionData.setOperator("match");
+ this.conditionData.setParamName("/");
+ this.conditionData.setParamType("uri");
+ this.conditionData.setParamValue("/http/**");
+ this.shenyuPluginChain = mock(ShenyuPluginChain.class);
+ this.pluginData = PluginData.builder()
+ .name("SHENYU")
+ .enabled(true).build();
+ this.selectorData = SelectorData.builder()
+ .id("1").pluginName("SHENYU")
+ .enabled(true)
+ .matchRestful(false)
+ .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()).build();
+ this.testShenyuWasmPlugin = spy(new TestShenyuWasmPlugin());
+ this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/http/SHENYU/SHENYU")
+ .build());
+ ShenyuContext context = mock(ShenyuContext.class);
+ exchange.getAttributes().put(Constants.CONTEXT, context);
+ clearCache();
+ when(shenyuPluginChain.execute(exchange)).thenReturn(Mono.empty());
+ }
+
+ /**
+ * The plugin is null test.
+ */
+ @Test
+ public void executePluginIsNullTest() {
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(shenyuPluginChain).execute(exchange);
+ }
+
+ /**
+ * The selector is null test.
+ */
+ @Test
+ public void executeSelectorIsNullTest() {
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(shenyuPluginChain).execute(exchange);
+ }
+
+ /**
+ * The selector data is null test.
+ */
+ @Test
+ public void executeSelectorDataIsNullTest() {
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ BaseDataCache.getInstance().cacheSelectData(selectorData);
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(shenyuPluginChain).execute(exchange);
+ }
+
+ /**
+ * The rule is null test.
+ */
+ @Test
+ public void executeRuleIsNullTest() {
+ List<ConditionData> conditionDataList = Collections.singletonList(conditionData);
+ this.selectorData.setMatchMode(0);
+ this.selectorData.setLogged(true);
+ this.selectorData.setMatchRestful(false);
+ this.selectorData.setConditionList(conditionDataList);
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ BaseDataCache.getInstance().cacheSelectData(selectorData);
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(shenyuPluginChain).execute(exchange);
+ }
+
+ /**
+ * The rule is not null test.
+ */
+ @Test
+ public void executeRuleIsNotNullTest() {
+ List<ConditionData> conditionDataList = Collections.singletonList(conditionData);
+ this.ruleData.setConditionDataList(conditionDataList);
+ this.ruleData.setMatchMode(0);
+ this.ruleData.setMatchRestful(false);
+ this.selectorData.setMatchMode(0);
+ this.selectorData.setMatchRestful(false);
+ this.selectorData.setLogged(true);
+ this.selectorData.setConditionList(conditionDataList);
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ BaseDataCache.getInstance().cacheSelectData(selectorData);
+ BaseDataCache.getInstance().cacheRuleData(ruleData);
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(testShenyuWasmPlugin).doExecute(exchange, shenyuPluginChain, selectorData, ruleData);
+ }
+
+ @Test
+ public void executeSelectorManyMatch() {
+ List<ConditionData> conditionDataList = Collections.singletonList(conditionData);
+ this.ruleData.setConditionDataList(conditionDataList);
+ this.ruleData.setMatchMode(0);
+ this.selectorData.setSort(1);
+ this.selectorData.setMatchMode(0);
+ this.selectorData.setLogged(true);
+ this.selectorData.setConditionList(conditionDataList);
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ BaseDataCache.getInstance().cacheSelectData(selectorData);
+ BaseDataCache.getInstance().cacheSelectData(SelectorData.builder()
+ .id("2").pluginName("SHENYU")
+ .enabled(true)
+ .matchMode(0)
+ .logged(true)
+ .sort(2)
+ .conditionList(conditionDataList)
+ .type(SelectorTypeEnum.CUSTOM_FLOW.getCode()).build());
+ BaseDataCache.getInstance().cacheRuleData(ruleData);
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(testShenyuWasmPlugin).doExecute(exchange, shenyuPluginChain, selectorData, ruleData);
+ }
+
+ @Test
+ public void executeRuleManyMatch() {
+ List<ConditionData> conditionDataList = Collections.singletonList(conditionData);
+ this.ruleData.setConditionDataList(conditionDataList);
+ this.ruleData.setMatchMode(0);
+ this.ruleData.setMatchRestful(false);
+ this.selectorData.setMatchMode(0);
+ this.selectorData.setLogged(true);
+ this.selectorData.setConditionList(conditionDataList);
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ BaseDataCache.getInstance().cacheSelectData(selectorData);
+
+ BaseDataCache.getInstance().cacheRuleData(RuleData.builder()
+ .id("1")
+ .pluginName("SHENYU")
+ .selectorId("1")
+ .enabled(true)
+ .loged(true)
+ .matchMode(0)
+ .matchRestful(false)
+ .conditionDataList(Collections.singletonList(conditionData))
+ .sort(1).build());
+
+ BaseDataCache.getInstance().cacheRuleData(RuleData.builder()
+ .id("2")
+ .pluginName("SHENYU")
+ .selectorId("1")
+ .enabled(true)
+ .loged(true)
+ .matchMode(0)
+ .matchRestful(false)
+ .conditionDataList(Collections.singletonList(conditionData))
+ .sort(2).build());
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(testShenyuWasmPlugin).doExecute(exchange, shenyuPluginChain, selectorData, ruleData);
+ }
+
+ /**
+ * The rule is full test.
+ */
+ @Test
+ public void executeRuleFullTest() {
+ List<ConditionData> conditionDataList = Collections.singletonList(conditionData);
+ this.ruleData.setConditionDataList(conditionDataList);
+ this.ruleData.setMatchMode(1);
+ this.ruleData.setMatchRestful(false);
+ this.selectorData.setMatchMode(0);
+ this.selectorData.setMatchRestful(false);
+ this.selectorData.setType(SelectorTypeEnum.FULL_FLOW.getCode());
+ this.selectorData.setLogged(true);
+ this.selectorData.setConditionList(conditionDataList);
+ BaseDataCache.getInstance().cachePluginData(pluginData);
+ BaseDataCache.getInstance().cacheSelectData(selectorData);
+ BaseDataCache.getInstance().cacheRuleData(ruleData);
+ StepVerifier.create(testShenyuWasmPlugin.execute(exchange, shenyuPluginChain)).expectSubscription().verifyComplete();
+ verify(testShenyuWasmPlugin).doExecute(exchange, shenyuPluginChain, selectorData, ruleData);
+ }
+
+ private void mockShenyuConfig() {
+ ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuConfig.class)).thenReturn(new ShenyuConfig());
+ when(context.getBean(TrieCacheTypeEnum.RULE.getTrieType())).thenReturn(new ShenyuTrie(100L, TrieMatchModeEnum.ANT_PATH_MATCH.getMatchMode()));
+ when(context.getBean(TrieCacheTypeEnum.SELECTOR.getTrieType())).thenReturn(new ShenyuTrie(100L, TrieMatchModeEnum.ANT_PATH_MATCH.getMatchMode()));
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ }
+
+ @AfterEach
+ public void clear() {
+ MatchDataCache.getInstance().cleanSelectorData();
+ MatchDataCache.getInstance().cleanRuleDataData();
+ }
+
+ private void clearCache() {
+ BaseDataCache.getInstance().cleanPluginData();
+ BaseDataCache.getInstance().cleanSelectorData();
+ BaseDataCache.getInstance().cleanRuleData();
+ }
+
+ static class TestShenyuWasmPlugin extends AbstractShenyuWasmPlugin {
+
+ private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+
+ @Override
+ protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store) {
+ Map<String, Func> funcMap = new HashMap<>();
+ funcMap.put("get_args", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+ (argId, addr, len) -> {
+ String config = "hello from java " + argId;
+ LOG.info("java side->" + config);
+ assertEquals("hello from java 0", config);
+ ByteBuffer buf = super.getBuffer();
+ for (int i = 0; i < len && i < config.length(); i++) {
+ buf.put(addr.intValue() + i, (byte) config.charAt(i));
+ }
+ return Math.min(config.length(), len);
+ }));
+ funcMap.put("put_result", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+ (argId, addr, len) -> {
+ ByteBuffer buf = super.getBuffer();
+ byte[] bytes = new byte[len];
+ for (int i = 0; i < len; i++) {
+ bytes[i] = buf.get(addr.intValue() + i);
+ }
+ String result = new String(bytes, StandardCharsets.UTF_8);
+ assertEquals("rust result", result);
+ RESULTS.put(argId, result);
+ LOG.info("java side->" + result);
+ return 0;
+ }));
+ return funcMap;
+ }
+
+ @Override
+ protected Mono<Void> doExecute(final ServerWebExchange exchange,
+ final ShenyuPluginChain chain,
+ final SelectorData selector,
+ final RuleData rule,
+ final Long argumentId) {
+ final String result = RESULTS.get(argumentId);
+ assertEquals("rust result", result);
+ return Mono.empty();
+ }
+
+ @Override
+ protected Long getArgumentId(final ServerWebExchange exchange,
+ final ShenyuPluginChain chain,
+ final SelectorData selector,
+ final RuleData rule) {
+ return 0L;
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+
+ @Override
+ public String named() {
+ return "SHENYU";
+ }
+ }
+}
diff --git a/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.AbstractShenyuWasmPluginTest$TestShenyuWasmPlugin.wasm b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.AbstractShenyuWasmPluginTest$TestShenyuWasmPlugin.wasm
new file mode 100755
index 0000000000..866f6da67b
Binary files /dev/null and b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/resources/org.apache.shenyu.plugin.wasm.base.AbstractShenyuWasmPluginTest$TestShenyuWasmPlugin.wasm differ
diff --git a/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/Cargo.toml b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/Cargo.toml
new file mode 100644
index 0000000000..054b86990d
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/Cargo.toml
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+[package]
+name = "rust-shenyu-wasm-plugin"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/README.md b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/README.md
new file mode 100644
index 0000000000..e4c1abde21
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/README.md
@@ -0,0 +1,19 @@
+# How to build the wasm file
+
+1. install rustup
+
+2. install rust
+
+3. generate the wasm file
+
+```shell
+cd {shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin
+cargo build --target wasm32-wasi --release
+```
+
+then you will see the wasm file
+in `{shenyu}/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/target/wasm32-wasi/release/rust_shenyu_wasm_plugin.wasm`
+
+4. rename the wasm file
+
+rename the file to `org.apache.shenyu.plugin.wasm.base.AbstractShenyuWasmPluginTest$TestShenyuWasmPlugin.wasm`
diff --git a/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/src/lib.rs b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/src/lib.rs
new file mode 100644
index 0000000000..b13e8d6bb0
--- /dev/null
+++ b/shenyu-plugin/shenyu-plugin-wasm-base/src/test/rust-shenyu-wasm-plugin/src/lib.rs
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#[link(wasm_import_module = "shenyu")]
+extern "C" {
+ fn get_args(arg_id: i64, addr: i64, len: i32) -> i32;
+
+ fn put_result(arg_id: i64, addr: i64, len: i32) -> i32;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doExecute(arg_id: i64) {
+ let mut buf = [0u8; 32];
+ let buf_ptr = buf.as_mut_ptr() as i64;
+ eprintln!("rust side-> buffer base address: {}", buf_ptr);
+ // get arg from java
+ let len = get_args(arg_id, buf_ptr, buf.len() as i32);
+ let java_arg = std::str::from_utf8(&buf[..len as usize]).unwrap();
+ eprintln!("rust side-> recv:{}", java_arg);
+ // pass rust result to java
+ let rust_result = "rust result".as_bytes();
+ let result_ptr = rust_result.as_ptr() as i64;
+ _ = put_result(arg_id, result_ptr, rust_result.len() as i32);
+}