Feat: new plugin design #222
This commit is contained in:
@@ -17,19 +17,13 @@
|
||||
|
||||
package we.filter;
|
||||
|
||||
import com.alibaba.nacos.api.config.annotation.NacosValue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.plugin.FixedPluginFilter;
|
||||
import we.plugin.PluginConfig;
|
||||
import we.plugin.FizzPluginFilterChain;
|
||||
import we.plugin.PluginFilter;
|
||||
import we.plugin.auth.ApiConfig;
|
||||
import we.plugin.auth.ApiConfigService;
|
||||
@@ -40,7 +34,6 @@ import we.util.WebUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -52,16 +45,10 @@ import java.util.function.Function;
|
||||
@Order(10)
|
||||
public class PreprocessFilter extends FizzWebFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PreprocessFilter.class);
|
||||
|
||||
public static final String PREPROCESS_FILTER = "preprocessFilter";
|
||||
|
||||
private static final FilterResult succFr = FilterResult.SUCCESS(PREPROCESS_FILTER);
|
||||
|
||||
@NacosValue(value = "${spring.profiles.active}")
|
||||
@Value("${spring.profiles.active}")
|
||||
private String profile;
|
||||
|
||||
@Resource(name = StatPluginFilter.STAT_PLUGIN_FILTER)
|
||||
private StatPluginFilter statPluginFilter;
|
||||
|
||||
@@ -85,18 +72,14 @@ public class PreprocessFilter extends FizzWebFilter {
|
||||
if (authRes instanceof ApiConfig) {
|
||||
ApiConfig ac = (ApiConfig) authRes;
|
||||
afterAuth(exchange, ac);
|
||||
// m = executeFixedPluginFilters(exchange);
|
||||
// m = m.defaultIfEmpty(ReactorUtils.NULL);
|
||||
if (ac.pluginConfigs == null || ac.pluginConfigs.isEmpty()) {
|
||||
return m.flatMap(func(exchange, chain));
|
||||
} else {
|
||||
return m.flatMap(e -> {return executeManagedPluginFilters(exchange, ac.pluginConfigs);})
|
||||
.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
eas.put(FizzPluginFilterChain.WEB_FILTER_CHAIN, chain);
|
||||
return FizzPluginFilterChain.next(exchange);
|
||||
}
|
||||
} else if (authRes == ApiConfigService.Access.YES) {
|
||||
afterAuth(exchange, null);
|
||||
// m = executeFixedPluginFilters(exchange);
|
||||
// return m.defaultIfEmpty(ReactorUtils.NULL).flatMap(func(exchange, chain));
|
||||
return m.flatMap(func(exchange, chain));
|
||||
} else {
|
||||
String err = null;
|
||||
@@ -152,33 +135,4 @@ public class PreprocessFilter extends FizzWebFilter {
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
|
||||
// private Mono<Void> executeFixedPluginFilters(ServerWebExchange exchange) {
|
||||
// Mono vm = Mono.empty();
|
||||
// List<FixedPluginFilter> fixedPluginFilters = FixedPluginFilter.getPluginFilters();
|
||||
// for (byte i = 0; i < fixedPluginFilters.size(); i++) {
|
||||
// FixedPluginFilter fpf = fixedPluginFilters.get(i);
|
||||
// vm = vm.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
// v -> {
|
||||
// return fpf.filter(exchange, null, null);
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// return vm;
|
||||
// }
|
||||
|
||||
private Mono<Void> executeManagedPluginFilters(ServerWebExchange exchange, List<PluginConfig> pluginConfigs) {
|
||||
Mono vm = Mono.empty();
|
||||
ApplicationContext app = exchange.getApplicationContext();
|
||||
for (byte i = 0; i < pluginConfigs.size(); i++) {
|
||||
PluginConfig pc = pluginConfigs.get(i);
|
||||
PluginFilter pf = app.getBean(pc.plugin, PluginFilter.class);
|
||||
vm = vm.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return pf.filter(exchange, pc.config, pc.fixedConfig);
|
||||
}
|
||||
);
|
||||
}
|
||||
return vm;
|
||||
}
|
||||
}
|
||||
|
||||
33
fizz-core/src/main/java/we/plugin/FizzPluginFilter.java
Normal file
33
fizz-core/src/main/java/we/plugin/FizzPluginFilter.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package we.plugin;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
*/
|
||||
|
||||
public interface FizzPluginFilter {
|
||||
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config);
|
||||
|
||||
}
|
||||
88
fizz-core/src/main/java/we/plugin/FizzPluginFilterChain.java
Normal file
88
fizz-core/src/main/java/we/plugin/FizzPluginFilterChain.java
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2020 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package we.plugin;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.FizzAppContext;
|
||||
import we.util.ReactorUtils;
|
||||
import we.util.WebUtils;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
*/
|
||||
|
||||
public final class FizzPluginFilterChain {
|
||||
|
||||
private static final String pluginConfigsIt = "pcsit";
|
||||
|
||||
public static final String WEB_FILTER_CHAIN = "wfc";
|
||||
|
||||
private FizzPluginFilterChain() {
|
||||
}
|
||||
|
||||
public static Mono<Void> next(ServerWebExchange exchange) {
|
||||
Map<String, Object> attris = exchange.getAttributes();
|
||||
List<PluginConfig> pcs = WebUtils.getApiConfig(exchange).pluginConfigs;
|
||||
Iterator<PluginConfig> it = (Iterator<PluginConfig>) attris.get(pluginConfigsIt);
|
||||
if (it == null) {
|
||||
it = pcs.iterator();
|
||||
attris.put(pluginConfigsIt, it);
|
||||
}
|
||||
if (it.hasNext()) {
|
||||
PluginConfig pc = it.next();
|
||||
FizzPluginFilter pf = FizzAppContext.appContext.getBean(pc.plugin, FizzPluginFilter.class);
|
||||
Mono m = pf.filter(exchange, pc.config);
|
||||
if (pf instanceof PluginFilter) {
|
||||
boolean f = false;
|
||||
while (it.hasNext()) {
|
||||
PluginConfig pc0 = it.next();
|
||||
FizzPluginFilter pf0 = FizzAppContext.appContext.getBean(pc0.plugin, FizzPluginFilter.class);
|
||||
m = m.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return pf0.filter(exchange, pc0.config);
|
||||
}
|
||||
);
|
||||
if (pf0 instanceof PluginFilter) {
|
||||
} else {
|
||||
f = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!it.hasNext() && !f) {
|
||||
WebFilterChain chain = (WebFilterChain) attris.get(WEB_FILTER_CHAIN);
|
||||
m = m.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
return m;
|
||||
} else {
|
||||
// attris.remove(pluginFilterConfigsIt);
|
||||
WebFilterChain chain = (WebFilterChain) attris.get(WEB_FILTER_CHAIN);
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import we.util.JacksonUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,8 @@ import java.util.Map;
|
||||
|
||||
public class PluginConfig {
|
||||
|
||||
public static final String FIXED_CONFIG = "$fc";
|
||||
|
||||
public String plugin; // tb_plugin.eng_name
|
||||
|
||||
public String fixedConfig;
|
||||
@@ -38,7 +41,21 @@ public class PluginConfig {
|
||||
// @JsonProperty(value = "config", access = JsonProperty.Access.WRITE_ONLY)
|
||||
public void setConfig(String confJson) {
|
||||
if (StringUtils.isNotBlank(confJson)) {
|
||||
config = JacksonUtils.readValue(confJson, Map.class);
|
||||
Map m = JacksonUtils.readValue(confJson, Map.class);
|
||||
if (config == Collections.EMPTY_MAP) {
|
||||
config = m;
|
||||
} else {
|
||||
config.putAll(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setFixedConfig(String fixedConfig) {
|
||||
if (StringUtils.isNotBlank(fixedConfig)) {
|
||||
if (config == Collections.EMPTY_MAP) {
|
||||
config = new HashMap<>();
|
||||
}
|
||||
config.put(FIXED_CONFIG, fixedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,13 +31,24 @@ import we.util.WebUtils;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @apiNote Custom plugin should implement FizzPluginFilter directly
|
||||
* <p/>
|
||||
*
|
||||
* @author hongqiaowei
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
public abstract class PluginFilter {
|
||||
@Deprecated
|
||||
public abstract class PluginFilter implements FizzPluginFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PluginFilter.class);
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
String fixedConfig = (String) config.get(PluginConfig.FIXED_CONFIG);
|
||||
return filter(exchange, config, fixedConfig);
|
||||
}
|
||||
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
FilterResult pfr = WebUtils.getPrevFilterResult(exchange);
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@@ -244,7 +244,7 @@ public class ApiConfigService {
|
||||
}
|
||||
}
|
||||
|
||||
private ApiConfig getApiConfig(String app, String service, HttpMethod method, String path) {
|
||||
public ApiConfig getApiConfig(String app, String service, HttpMethod method, String path) {
|
||||
ApiConfig ac = null;
|
||||
for (String g : gatewayGroupService.currentGatewayGroupSet) {
|
||||
ac = getApiConfig(service, method, path, g, app);
|
||||
|
||||
189
fizz-core/src/test/java/we/plugin/PluginTests.java
Normal file
189
fizz-core/src/test/java/we/plugin/PluginTests.java
Normal file
@@ -0,0 +1,189 @@
|
||||
package we.plugin;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
import we.FizzAppContext;
|
||||
import we.filter.PreprocessFilter;
|
||||
import we.plugin.auth.ApiConfig;
|
||||
import we.plugin.auth.ApiConfigService;
|
||||
import we.plugin.auth.AuthPluginFilter;
|
||||
import we.plugin.stat.StatPluginFilter;
|
||||
import we.util.ReactorUtils;
|
||||
import we.util.ReflectionUtils;
|
||||
import we.util.WebUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author hongqiaowei
|
||||
*/
|
||||
|
||||
public class PluginTests {
|
||||
|
||||
StatPluginFilter statPluginFilter;
|
||||
|
||||
AuthPluginFilter authPluginFilter;
|
||||
|
||||
PreprocessFilter preprocessFilter;
|
||||
|
||||
ApiConfigService apiConfigService;
|
||||
|
||||
// @BeforeEach
|
||||
void beforeEach() {
|
||||
authPluginFilter = new AuthPluginFilter();
|
||||
statPluginFilter = new StatPluginFilter();
|
||||
preprocessFilter = new PreprocessFilter();
|
||||
apiConfigService = new ApiConfigService();
|
||||
ReflectionUtils.set(preprocessFilter, "statPluginFilter", statPluginFilter);
|
||||
ReflectionUtils.set(authPluginFilter, "apiConfigService", apiConfigService);
|
||||
ReflectionUtils.set(preprocessFilter, "authPluginFilter", authPluginFilter);
|
||||
|
||||
FizzAppContext.appContext = mock(ConfigurableApplicationContext.class);
|
||||
}
|
||||
|
||||
// @Test
|
||||
void legacyPluginFilterTest() {
|
||||
|
||||
String plugin = "legacyPlugin";
|
||||
PluginFilter legacyPlugin = new PluginFilter() {
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
return WebUtils.transmitSuccessFilterResultAndEmptyMono(exchange, plugin, Collections.singletonMap("123", "456"));
|
||||
}
|
||||
};
|
||||
|
||||
String plugin0 = "legacyPlugin0";
|
||||
PluginFilter legacyPlugin0 = new PluginFilter() {
|
||||
@Override
|
||||
public Mono<Void> doFilter(ServerWebExchange exchange, Map<String, Object> config, String fixedConfig) {
|
||||
String v = (String) WebUtils.getFilterResultDataItem(exchange, plugin, "123");
|
||||
return WebUtils.transmitSuccessFilterResultAndEmptyMono(exchange, plugin0, Collections.singletonMap(v, "789"));
|
||||
}
|
||||
};
|
||||
|
||||
when(FizzAppContext.appContext.getBean(plugin, FizzPluginFilter.class)).thenReturn(legacyPlugin);
|
||||
when(FizzAppContext.appContext.getBean(plugin0, FizzPluginFilter.class)).thenReturn(legacyPlugin0);
|
||||
|
||||
WebTestClient client = WebTestClient
|
||||
.bindToWebHandler(
|
||||
new WebHandler() {
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.setStatusCode(HttpStatus.OK);
|
||||
HttpHeaders headers = resp.getHeaders();
|
||||
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
|
||||
String v = (String) WebUtils.getFilterResultDataItem(exchange, plugin0, "456");
|
||||
headers.add(v, "abc");
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap("server response".getBytes())));
|
||||
}
|
||||
}
|
||||
)
|
||||
.webFilter(preprocessFilter)
|
||||
.build();
|
||||
|
||||
WebTestClient.ResponseSpec exchange = client.get().uri("/proxy/xservice/ypath").exchange();
|
||||
exchange.expectHeader().valueEquals("789", "abc");
|
||||
}
|
||||
|
||||
// @Test
|
||||
void fizzPluginFilterTest() {
|
||||
String plugin = "fizzPlugin";
|
||||
FizzPluginFilter fizzPlugin = new FizzPluginFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
exchange.getAttributes().put("11", "22");
|
||||
Mono next = FizzPluginFilterChain.next(exchange);
|
||||
return next.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
return Mono.empty();
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
String plugin0 = "fizzPlugin0";
|
||||
FizzPluginFilter fizzPlugin0 = new FizzPluginFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, Map<String, Object> config) {
|
||||
exchange.getAttributes().put("aa", "bb");
|
||||
Mono next = FizzPluginFilterChain.next(exchange);
|
||||
return next.defaultIfEmpty(ReactorUtils.NULL).flatMap(
|
||||
v -> {
|
||||
String val = (String) exchange.getAttributes().get("11");
|
||||
System.err.println(val + " === ");
|
||||
return Mono.empty();
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
when(FizzAppContext.appContext.getBean(plugin, FizzPluginFilter.class)).thenReturn(fizzPlugin);
|
||||
when(FizzAppContext.appContext.getBean(plugin0, FizzPluginFilter.class)).thenReturn(fizzPlugin0);
|
||||
|
||||
WebTestClient client = WebTestClient
|
||||
.bindToWebHandler(
|
||||
new WebHandler() {
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange) {
|
||||
ServerHttpResponse resp = exchange.getResponse();
|
||||
resp.setStatusCode(HttpStatus.OK);
|
||||
HttpHeaders headers = resp.getHeaders();
|
||||
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE);
|
||||
String v1 = exchange.getAttribute("11");
|
||||
String v2 = exchange.getAttribute("aa");
|
||||
headers.add(v1 + v2, "xx");
|
||||
return resp.writeWith(Mono.just(resp.bufferFactory().wrap("server response".getBytes())));
|
||||
}
|
||||
}
|
||||
)
|
||||
.webFilter(preprocessFilter)
|
||||
.build();
|
||||
|
||||
WebTestClient.ResponseSpec exchange = client.get().uri("/proxy/xservice/ypath").exchange();
|
||||
exchange.expectHeader().valueEquals("22bb", "xx");
|
||||
}
|
||||
|
||||
// @Test
|
||||
void legacyPluginFilter_Mix_FizzPluginFilterTest() {
|
||||
if (true) {
|
||||
ApiConfig ac = new ApiConfig();
|
||||
ac.type = ApiConfig.Type.SERVICE_DISCOVERY;
|
||||
ac.service = "xservice";
|
||||
ac.backendService = "xservice";
|
||||
ac.path = "/ypath";
|
||||
ac.backendPath = "/ypath";
|
||||
ac.pluginConfigs = new ArrayList<>();
|
||||
|
||||
// PluginConfig pc = new PluginConfig();
|
||||
// pc.plugin = "legacyPlugin";
|
||||
// ac.pluginConfigs.add(pc);
|
||||
// PluginConfig pc0 = new PluginConfig();
|
||||
// pc0.plugin = "legacyPlugin0";
|
||||
// ac.pluginConfigs.add(pc0);
|
||||
|
||||
PluginConfig pc = new PluginConfig();
|
||||
pc.plugin = "fizzPlugin";
|
||||
ac.pluginConfigs.add(pc);
|
||||
PluginConfig pc0 = new PluginConfig();
|
||||
pc0.plugin = "fizzPlugin0";
|
||||
ac.pluginConfigs.add(pc0);
|
||||
|
||||
// return Mono.just(ac);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user