forked from apache/shenyu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ISSUE apache#4612] init shenyu-plugin-wasm-base module
- Loading branch information
1 parent
1a54081
commit 5504efc
Showing
15 changed files
with
828 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,3 +43,5 @@ Thumbs.db | |
# agent build ignore | ||
/agent/ | ||
|
||
# rust ignore | ||
*.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
~ 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. | ||
--> | ||
|
||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<groupId>org.apache.shenyu</groupId> | ||
<artifactId>shenyu-plugin</artifactId> | ||
<version>2.6.2-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
<artifactId>shenyu-plugin-wasm-base</artifactId> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.apache.shenyu</groupId> | ||
<artifactId>shenyu-plugin-base</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.github.kawamuray.wasmtime</groupId> | ||
<artifactId>wasmtime-java</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>ch.qos.logback</groupId> | ||
<artifactId>logback-classic</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.module</groupId> | ||
<artifactId>jackson-module-kotlin</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
</project> |
169 changes: 169 additions & 0 deletions
169
...-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractShenyuWasmPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* | ||
* 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.Extern; | ||
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.dto.RuleData; | ||
import org.apache.shenyu.common.dto.SelectorData; | ||
import org.apache.shenyu.plugin.api.ShenyuPluginChain; | ||
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum; | ||
import org.apache.shenyu.plugin.api.result.ShenyuResultWrap; | ||
import org.apache.shenyu.plugin.api.utils.WebFluxResultUtils; | ||
import org.apache.shenyu.plugin.base.AbstractShenyuPlugin; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* Complex plugins implemented in other languages should extend this class, we still need to write Java subclasses, | ||
* so we can reuse the convenient/powerful control of ShenYu, such as {@link #getOrder}/{@link #skip} | ||
* /{@link #skipExcept}/{@link #skipExceptHttpLike}. | ||
* | ||
* @see org.apache.shenyu.plugin.base.AbstractShenyuPlugin | ||
* @see io.github.kawamuray.wasmtime.WasmValType | ||
* @see org.apache.shenyu.plugin.wasm.base.WasmLoader | ||
*/ | ||
public abstract class AbstractShenyuWasmPlugin extends AbstractShenyuPlugin { | ||
|
||
protected static final Logger LOG = LoggerFactory.getLogger(AbstractShenyuWasmPlugin.class); | ||
|
||
protected static final Map<Long, Argument> ARGUMENTS = new ConcurrentHashMap<>(); | ||
|
||
protected static final String DO_EXECUTE_METHOD_NAME = "doExecute"; | ||
|
||
protected static final String BEFORE_METHOD_NAME = "before"; | ||
|
||
protected static final String AFTER_METHOD_NAME = "after"; | ||
|
||
private final WasmLoader wasmLoader; | ||
|
||
public AbstractShenyuWasmPlugin() { | ||
this.wasmLoader = new WasmLoader(this.getClass(), this::initWasmCallJavaFunc); | ||
} | ||
|
||
protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store) { | ||
return null; | ||
} | ||
|
||
@Override | ||
protected Mono<Void> doExecute(final ServerWebExchange exchange, | ||
final ShenyuPluginChain chain, | ||
final SelectorData selector, | ||
final RuleData rule) { | ||
return wasmLoader.getWasmExtern(DO_EXECUTE_METHOD_NAME).map(doExecute -> { | ||
final Long argumentId = callWASI(exchange, chain, selector, rule, doExecute); | ||
return doExecute(exchange, chain, selector, rule, argumentId); | ||
}).orElseGet(() -> { | ||
LOG.error("{} function not found in {}", DO_EXECUTE_METHOD_NAME, wasmLoader.getWasmName()); | ||
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); | ||
Object error = ShenyuResultWrap.error(exchange, ShenyuResultEnum.WASM_FUNC_NOT_FOUND); | ||
return WebFluxResultUtils.result(exchange, error); | ||
}); | ||
} | ||
|
||
/** | ||
* this is Template Method child has implements your own logic. | ||
* | ||
* @param exchange exchange the current server exchange {@linkplain ServerWebExchange} | ||
* @param chain chain the current chain {@linkplain ServerWebExchange} | ||
* @param selector selector {@linkplain SelectorData} | ||
* @param rule rule {@linkplain RuleData} | ||
* @param argumentId the argument id {@linkplain #getArgumentId} | ||
* @return {@code Mono<Void>} to indicate when request handling is complete | ||
*/ | ||
protected abstract Mono<Void> doExecute(ServerWebExchange exchange, ShenyuPluginChain chain, SelectorData selector, RuleData rule, Long argumentId); | ||
|
||
private Long callWASI(final ServerWebExchange exchange, | ||
final ShenyuPluginChain chain, | ||
final SelectorData selector, | ||
final RuleData rule, | ||
final Extern doExecute) { | ||
// WASI cannot easily pass Java objects like JNI, here we pass Long as arg | ||
// then we can get the argument by Long | ||
final Long argumentId = getArgumentId(exchange, chain, selector, rule); | ||
ARGUMENTS.put(argumentId, new Argument(exchange, chain, selector, rule)); | ||
// call WASI function | ||
WasmFunctions.consumer(wasmLoader.getStore(), doExecute.func(), WasmValType.I64) | ||
.accept(argumentId); | ||
ARGUMENTS.remove(argumentId); | ||
return argumentId; | ||
} | ||
|
||
protected abstract Long getArgumentId(ServerWebExchange exchange, | ||
ShenyuPluginChain chain, | ||
SelectorData selector, | ||
RuleData rule); | ||
|
||
@Override | ||
public void before(final ServerWebExchange exchange) { | ||
wasmLoader.getWasmExtern(BEFORE_METHOD_NAME) | ||
.ifPresent(before -> callWASI(exchange, null, null, null, before)); | ||
} | ||
|
||
@Override | ||
public void after(final ServerWebExchange exchange) { | ||
wasmLoader.getWasmExtern(AFTER_METHOD_NAME) | ||
.ifPresent(before -> callWASI(exchange, null, null, null, before)); | ||
} | ||
|
||
protected static final class Argument { | ||
|
||
private final ServerWebExchange exchange; | ||
|
||
private final ShenyuPluginChain chain; | ||
|
||
private final SelectorData selector; | ||
|
||
private final RuleData rule; | ||
|
||
private Argument(final ServerWebExchange exchange, | ||
final ShenyuPluginChain chain, | ||
final SelectorData selector, | ||
final RuleData rule) { | ||
this.exchange = exchange; | ||
this.chain = chain; | ||
this.selector = selector; | ||
this.rule = rule; | ||
} | ||
|
||
public ServerWebExchange getExchange() { | ||
return exchange; | ||
} | ||
|
||
public ShenyuPluginChain getChain() { | ||
return chain; | ||
} | ||
|
||
public SelectorData getSelector() { | ||
return selector; | ||
} | ||
|
||
public RuleData getRule() { | ||
return rule; | ||
} | ||
} | ||
} |
127 changes: 127 additions & 0 deletions
127
...plugin-wasm-base/src/main/java/org/apache/shenyu/plugin/wasm/base/AbstractWasmPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* | ||
* 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.Extern; | ||
import io.github.kawamuray.wasmtime.WasmFunctions; | ||
import io.github.kawamuray.wasmtime.WasmValType; | ||
import org.apache.shenyu.plugin.api.ShenyuPlugin; | ||
import org.apache.shenyu.plugin.api.ShenyuPluginChain; | ||
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum; | ||
import org.apache.shenyu.plugin.api.result.ShenyuResultWrap; | ||
import org.apache.shenyu.plugin.api.utils.WebFluxResultUtils; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* Simple plugins implemented in other languages should extend this class, we still need to write Java subclasses, | ||
* so we can reuse the convenient/powerful control of ShenYu, such as {@link #getOrder}/{@link #skip} | ||
* /{@link #skipExcept}/{@link #skipExceptHttpLike}. | ||
* | ||
* @see org.apache.shenyu.plugin.api.ShenyuPlugin | ||
* @see io.github.kawamuray.wasmtime.WasmValType | ||
* @see org.apache.shenyu.plugin.wasm.base.WasmLoader | ||
*/ | ||
public abstract class AbstractWasmPlugin extends WasmLoader implements ShenyuPlugin { | ||
|
||
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWasmPlugin.class); | ||
|
||
protected static final Map<Long, Argument> ARGUMENTS = new ConcurrentHashMap<>(); | ||
|
||
protected static final String EXECUTE_METHOD_NAME = "execute"; | ||
|
||
protected static final String BEFORE_METHOD_NAME = "before"; | ||
|
||
protected static final String AFTER_METHOD_NAME = "after"; | ||
|
||
@Override | ||
public Mono<Void> execute(final ServerWebExchange exchange, final ShenyuPluginChain chain) { | ||
return super.getWasmExtern(EXECUTE_METHOD_NAME).map(execute -> { | ||
final Long argumentId = callWASI(exchange, chain, execute); | ||
return doExecute(exchange, chain, argumentId); | ||
}).orElseGet(() -> { | ||
LOG.error("{} function not found in {}", EXECUTE_METHOD_NAME, super.getWasmName()); | ||
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); | ||
Object error = ShenyuResultWrap.error(exchange, ShenyuResultEnum.WASM_FUNC_NOT_FOUND); | ||
return WebFluxResultUtils.result(exchange, error); | ||
}); | ||
} | ||
|
||
/** | ||
* this is Template Method child has Implement your own logic. | ||
* | ||
* @param exchange exchange the current server exchange {@linkplain ServerWebExchange} | ||
* @param chain chain the current chain {@linkplain ServerWebExchange} | ||
* @param argumentId the argument id {@linkplain #getArgumentId} | ||
* @return {@code Mono<Void>} to indicate when request handling is complete | ||
*/ | ||
protected abstract Mono<Void> doExecute(ServerWebExchange exchange, ShenyuPluginChain chain, Long argumentId); | ||
|
||
private Long callWASI(final ServerWebExchange exchange, final ShenyuPluginChain chain, final Extern execute) { | ||
// WASI cannot easily pass Java objects like JNI, here we pass Long as arg | ||
// then we can get the argument by Long | ||
final Long argumentId = getArgumentId(exchange, chain); | ||
ARGUMENTS.put(argumentId, new Argument(exchange, chain)); | ||
// call WASI function | ||
WasmFunctions.consumer(super.getStore(), execute.func(), WasmValType.I64) | ||
.accept(argumentId); | ||
ARGUMENTS.remove(argumentId); | ||
return argumentId; | ||
} | ||
|
||
protected abstract Long getArgumentId(ServerWebExchange exchange, ShenyuPluginChain chain); | ||
|
||
@Override | ||
public void before(final ServerWebExchange exchange) { | ||
super.getWasmExtern(BEFORE_METHOD_NAME) | ||
.ifPresent(before -> callWASI(exchange, null, before)); | ||
} | ||
|
||
@Override | ||
public void after(final ServerWebExchange exchange) { | ||
super.getWasmExtern(AFTER_METHOD_NAME) | ||
.ifPresent(before -> callWASI(exchange, null, before)); | ||
} | ||
|
||
protected static final class Argument { | ||
|
||
private final ServerWebExchange exchange; | ||
|
||
private final ShenyuPluginChain chain; | ||
|
||
private Argument(final ServerWebExchange exchange, | ||
final ShenyuPluginChain chain) { | ||
this.exchange = exchange; | ||
this.chain = chain; | ||
} | ||
|
||
public ServerWebExchange getExchange() { | ||
return exchange; | ||
} | ||
|
||
public ShenyuPluginChain getChain() { | ||
return chain; | ||
} | ||
} | ||
} |
Oops, something went wrong.