Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for customizing ConfigurableRequestItemParser for SC gateway and Zuul adapter #2542

Merged
merged 4 commits into from
Jan 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright 1999-2022 Alibaba Group Holding Ltd.
*
* Licensed 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
*
* https://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 com.alibaba.csp.sentinel.adapter.gateway.common.param;
brotherlu-xcq marked this conversation as resolved.
Show resolved Hide resolved

import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
brotherlu-xcq marked this conversation as resolved.
Show resolved Hide resolved
* delegate RequestItemParser, support add extractors to customize request item parse.
* <p>
* example:
* if you want to get client real ip in multi nginx proxy, you can register SentinelGatewayFilter bean as follows
*
* ConfigurableRequestItemParser<ServerWebExchange> parser = new ConfigurableRequestItemParser<>(new ServerWebExchangeItemParser());
* List<String> headerNames = Arrays.asList("X-Real-IP", "Client-IP");
* parser.addRemoteAddressExtractor(serverWebExchange -> {
* for (String headerKey : headerNames) {
* String remoteAddress = serverWebExchange.getRequest().getHeaders().getFirst(headerKey);
* if (StringUtils.hasLength(remoteAddress)) {
* return remoteAddress;
* }
* }
* return null;
* });
* return new SentinelGatewayFilter(parser);
*
* @author icodening
* @date 2022.01.14
*/
public class ConfigurableRequestItemParser<T> implements RequestItemParser<T> {

private final List<Function<T, String>> pathExtractors = new ArrayList<>(2);

private final List<Function<T, String>> remoteAddressExtractors = new ArrayList<>(2);

private final List<BiFunction<T, String, String>> headerExtractors = new ArrayList<>(2);

private final List<BiFunction<T, String, String>> urlParamExtractors = new ArrayList<>(2);

private final List<BiFunction<T, String, String>> cookieValueExtractors = new ArrayList<>(2);

private final RequestItemParser<T> delegate;

public ConfigurableRequestItemParser(RequestItemParser<T> delegate) {
AssertUtil.notNull(delegate, "delegate can not be null");
this.delegate = delegate;
}

@Override
public String getPath(T request) {
for (Function<T, String> extractor : pathExtractors) {
String pathValue = extractor.apply(request);
if (StringUtil.isNotBlank(pathValue)) {
return pathValue;
}
}
return delegate.getPath(request);
}

@Override
public String getRemoteAddress(T request) {
for (Function<T, String> extractor : remoteAddressExtractors) {
String remoteAddress = extractor.apply(request);
if (StringUtil.isNotBlank(remoteAddress)) {
return remoteAddress;
}
}
return delegate.getRemoteAddress(request);
}

@Override
public String getHeader(T request, String key) {
for (BiFunction<T, String, String> extractor : headerExtractors) {
String headerValue = extractor.apply(request, key);
if (StringUtil.isNotBlank(headerValue)) {
return headerValue;
}
}
return delegate.getHeader(request, key);
}

@Override
public String getUrlParam(T request, String paramName) {
for (BiFunction<T, String, String> extractor : urlParamExtractors) {
String urlParam = extractor.apply(request, paramName);
if (StringUtil.isNotBlank(urlParam)) {
return urlParam;
}
}
return delegate.getUrlParam(request, paramName);
brotherlu-xcq marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public String getCookieValue(T request, String cookieName) {
for (BiFunction<T, String, String> extractor : cookieValueExtractors) {
String cookie = extractor.apply(request, cookieName);
if (StringUtil.isNotBlank(cookie)) {
return cookie;
}
}
return delegate.getCookieValue(request, cookieName);
}

public ConfigurableRequestItemParser<T> addPathExtractor(Function<T, String> extractor) {
if (extractor == null) {
return this;
}
pathExtractors.add(extractor);
return this;
}

public ConfigurableRequestItemParser<T> addRemoteAddressExtractor(Function<T, String> extractor) {
if (extractor == null) {
return this;
}
remoteAddressExtractors.add(extractor);
return this;
}

public ConfigurableRequestItemParser<T> addHeaderExtractor(BiFunction<T, String, String> extractor) {
if (extractor == null) {
return this;
}
headerExtractors.add(extractor);
return this;
}

public ConfigurableRequestItemParser<T> addUrlParamExtractor(BiFunction<T, String, String> extractor) {
if (extractor == null) {
return this;
}
urlParamExtractors.add(extractor);
return this;
}

public ConfigurableRequestItemParser<T> addCookieValueExtractor(BiFunction<T, String, String> extractor) {
if (extractor == null) {
return this;
}
cookieValueExtractors.add(extractor);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,18 @@
*/
package com.alibaba.csp.sentinel.adapter.gateway.sc;

import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
import com.alibaba.csp.sentinel.adapter.gateway.sc.api.GatewayApiMatcherManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.api.matcher.WebExchangeApiMatcher;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.reactor.ContextConfig;
import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig;
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
import com.alibaba.csp.sentinel.adapter.gateway.sc.api.GatewayApiMatcherManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.api.matcher.WebExchangeApiMatcher;

import com.alibaba.csp.sentinel.util.AssertUtil;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
Expand All @@ -39,6 +36,10 @@
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
* @author Eric Zhao
* @since 1.6.0
Expand All @@ -47,16 +48,25 @@ public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter, Order

private final int order;

private final GatewayParamParser<ServerWebExchange> paramParser;

public SentinelGatewayFilter() {
this(Ordered.HIGHEST_PRECEDENCE);
}

public SentinelGatewayFilter(int order) {
this.order = order;
this(order, new ServerWebExchangeItemParser());
}

private final GatewayParamParser<ServerWebExchange> paramParser = new GatewayParamParser<>(
new ServerWebExchangeItemParser());
public SentinelGatewayFilter(RequestItemParser<ServerWebExchange> serverWebExchangeItemParser) {
this(Ordered.HIGHEST_PRECEDENCE, serverWebExchangeItemParser);
}

public SentinelGatewayFilter(int order, RequestItemParser<ServerWebExchange> requestItemParser) {
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
this.order = order;
this.paramParser = new GatewayParamParser<>(requestItemParser);
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.zuul.RequestContextItemParser;
import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulGatewayApiMatcherManager;
Expand All @@ -37,6 +38,7 @@
import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackProvider;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.util.function.Predicate;

Expand All @@ -59,15 +61,20 @@ public class SentinelZuulPreFilter extends ZuulFilter {

private final int order;

private final GatewayParamParser<RequestContext> paramParser = new GatewayParamParser<>(
new RequestContextItemParser());
private final GatewayParamParser<RequestContext> paramParser;

public SentinelZuulPreFilter() {
this(10000);
}

public SentinelZuulPreFilter(int order) {
this(order, new RequestContextItemParser());
}

public SentinelZuulPreFilter(int order, RequestItemParser<RequestContext> requestItemParser) {
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
this.order = order;
this.paramParser = new GatewayParamParser<>(requestItemParser);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.HttpRequestMessageItemParser;
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.ZuulGatewayApiMatcherManager;
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMessageApiMatcher;
Expand Down Expand Up @@ -67,8 +68,7 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter {
private final boolean fastError;
private final Function<HttpRequestMessage, String> routeExtractor;

private final GatewayParamParser<HttpRequestMessage> paramParser = new GatewayParamParser<>(
new HttpRequestMessageItemParser());
private final GatewayParamParser<HttpRequestMessage> paramParser;

/**
* Constructor of the inbound filter, which extracts the route from the context route VIP attribute by default.
Expand Down Expand Up @@ -98,13 +98,20 @@ public SentinelZuulInboundFilter(int order, Executor executor, Function<HttpRequ
*/
public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError,
Function<HttpRequestMessage, String> routeExtractor) {
this(order, blockedEndpointName, executor, fastError, routeExtractor, new HttpRequestMessageItemParser());
}

public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError,
Function<HttpRequestMessage, String> routeExtractor, RequestItemParser<HttpRequestMessage> requestItemParser) {
AssertUtil.notEmpty(blockedEndpointName, "blockedEndpointName cannot be empty");
AssertUtil.notNull(routeExtractor, "routeExtractor cannot be null");
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
this.order = order;
this.blockedEndpointName = blockedEndpointName;
this.executor = executor;
this.fastError = fastError;
this.routeExtractor = routeExtractor;
this.paramParser = new GatewayParamParser<>(requestItemParser);
}

@Override
Expand Down