Skip to content
Open
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
6 changes: 6 additions & 0 deletions sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.alibaba.csp.sentinel.adapter.spring.webflux;

import java.util.Optional;
import java.util.function.Function;

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
Expand All @@ -38,11 +39,24 @@ public class SentinelWebFluxFilter implements WebFilter {

public static final String SENTINEL_SPRING_WEBFLUX_CONTEXT_NAME = "sentinel_spring_webflux_context";

private static final Function<ServerWebExchange, String> DEFAULT_EXTRACTOR = (exchange) -> null;

private final Function<ServerWebExchange,String> bestMatchingPatternExtractor;

public SentinelWebFluxFilter() {
this(DEFAULT_EXTRACTOR);
}

public SentinelWebFluxFilter(Function<ServerWebExchange,String> bestMatchingPatternExtractor) {
this.bestMatchingPatternExtractor = bestMatchingPatternExtractor;
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
// Maybe we can get the URL pattern elsewhere via:
// exchange.getAttributeOrDefault(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, path)
String path = exchange.getRequest().getPath().value();
String bestMatchingPattern = bestMatchingPatternExtractor.apply(exchange);
String path = bestMatchingPattern != null ? bestMatchingPattern : exchange.getRequest().getPath().value();

String finalPath = WebFluxCallbackManager.getUrlCleaner().apply(exchange, path);
if (StringUtil.isEmpty(finalPath)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.spring.webflux.support;

import com.alibaba.csp.sentinel.util.AssertUtil;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;

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

/**
* default extractor for "BEST_MATCHING_HANDLER_ATTRIBUTE"
*
* @author icodening
* @date 2022.03.29
*/
public class DefaultBestMatchingPatternExtractor implements Function<ServerWebExchange, String> {

private final List<HandlerMapping> handlerMappings;

private final List<HandlerMappingBestMatchingPatternExtractor> handlerMappingBestMatchingPatternExtractors;

public DefaultBestMatchingPatternExtractor(List<HandlerMapping> handlerMappings,
List<HandlerMappingBestMatchingPatternExtractor> handlerMappingBestMatchingPatternExtractors) {
AssertUtil.notNull(handlerMappings, "handlerMappings cannot be null");
AssertUtil.notNull(handlerMappingBestMatchingPatternExtractors, "handlerMappingBestMatchingPatternExtractors cannot be null");
this.handlerMappings = handlerMappings;
this.handlerMappingBestMatchingPatternExtractors = handlerMappingBestMatchingPatternExtractors;
}

@Override
public String apply(ServerWebExchange exchange) {
for (HandlerMapping handlerMapping : handlerMappings) {
for (HandlerMappingBestMatchingPatternExtractor handlerMappingBestMatchingPatternExtractor : handlerMappingBestMatchingPatternExtractors) {
if (!handlerMappingBestMatchingPatternExtractor.supportExtract(handlerMapping)) {
continue;
}
String bestMatchingPath = handlerMappingBestMatchingPatternExtractor.extract(handlerMapping, exchange);
if (bestMatchingPath != null) {
return bestMatchingPath;
}
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.spring.webflux.support;

import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;

/**
* "BEST_MATCHING_HANDLER_ATTRIBUTE" extractor
*
* @author icodening
* @date 2022.03.29
* @see HandlerMapping#BEST_MATCHING_HANDLER_ATTRIBUTE
*/
public interface HandlerMappingBestMatchingPatternExtractor {

/**
* whether to process the given HandlerMapping
*
* @param handlerMapping Spring HandlerMapping
* @return "true" is support extract
*/
boolean supportExtract(HandlerMapping handlerMapping);

/**
* extract BEST_MATCHING_HANDLER_ATTRIBUTE for current given ServerWebExchange
*
* @param handlerMapping current HandlerMapping, eg. RouterFunctionMapping, RequestMappingHandlerMapping, SimpleUrlHandlerMapping
* @param exchange current request and response
* @return best matching pattern, return null when if not found , eg. /users/{id}
*/
String extract(HandlerMapping handlerMapping, ServerWebExchange exchange);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.spring.webflux.support;

import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;

/**
* best matching pattern extractor for RequestMappingHandlerMapping
*
* @author icodening
* @date 2022.03.29
* @see RequestMappingHandlerMapping
*/
public class RequestMappingHandlerMappingBestMatchingPatternExtractor implements HandlerMappingBestMatchingPatternExtractor {

@Override
public String extract(HandlerMapping handlerMapping, ServerWebExchange exchange) {
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) handlerMapping;
requestMappingHandlerMapping.getHandlerInternal(exchange);
Object attribute = exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
if (attribute instanceof PathPattern) {
return ((PathPattern) attribute).getPatternString();
}
return null;
}

@Override
public boolean supportExtract(HandlerMapping handlerMapping) {
return handlerMapping instanceof RequestMappingHandlerMapping;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.spring.webflux.support;

import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPattern;

import java.util.Collections;
import java.util.List;

/**
* best matching pattern extractor for RouterFunctionMapping
*
* @author icodening
* @date 2022.03.29
* @see RouterFunctionMapping
*/
public class RouterFunctionMappingBestMatchingPatternExtractor implements HandlerMappingBestMatchingPatternExtractor {

private List<RequestPredicate> registeredRequestPredicates = Collections.emptyList();

private volatile boolean initialized = false;

@Override
public boolean supportExtract(HandlerMapping handlerMapping) {
return handlerMapping instanceof RouterFunctionMapping;
}

public void setRegisteredRequestPredicates(List<RequestPredicate> registeredRequestPredicates) {
if (registeredRequestPredicates != null) {
this.registeredRequestPredicates = registeredRequestPredicates;
}
}

@Override
public String extract(HandlerMapping handlerMapping, ServerWebExchange exchange) {
initRequestPredicatesIfNecessary(handlerMapping);
SentinelServerRequest sentinelServerRequest = new SentinelServerRequest(exchange);
for (RequestPredicate requestPredicate : registeredRequestPredicates) {
if (requestPredicate.test(sentinelServerRequest)) {
Object attribute = exchange.getAttribute(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE);
if (attribute instanceof PathPattern) {
exchange.getAttributes().remove(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE);
return ((PathPattern) attribute).getPatternString();
}
}
}
return null;
}

private void initRequestPredicatesIfNecessary(HandlerMapping handlerMapping){
if (!this.initialized) {
RouterFunctionMapping routerFunctionMapping = (RouterFunctionMapping) handlerMapping;
RouterFunction<?> routerFunction = routerFunctionMapping.getRouterFunction();
RouterFunctionRequestPredicateRepository routerFunctionRequestPredicateRepository = new RouterFunctionRequestPredicateRepository();
if (routerFunction != null) {
routerFunction.accept(new RouterFunctionRequestPredicateRegistrarVisitor(routerFunctionRequestPredicateRepository));
}
setRegisteredRequestPredicates(routerFunctionRequestPredicateRepository.getRequestPredicates());
this.initialized = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.spring.webflux.support;

import org.springframework.core.io.Resource;
import org.springframework.web.reactive.function.server.*;
import reactor.core.publisher.Mono;

import java.util.function.Function;

/**
* @author icodening
* @date 2022.03.29
*/
class RouterFunctionRequestPredicateRegistrarVisitor implements RouterFunctions.Visitor {

private final RouterFunctionRequestPredicateRepository routerFunctionRequestPredicateRepository;

RouterFunctionRequestPredicateRegistrarVisitor(RouterFunctionRequestPredicateRepository routerFunctionRequestPredicateRepository) {
this.routerFunctionRequestPredicateRepository = routerFunctionRequestPredicateRepository;
}

@Override
public void startNested(RequestPredicate predicate) {
//ignore
}

@Override
public void endNested(RequestPredicate predicate) {
//ignore
}

@Override
public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction) {
//cache the predicate
this.routerFunctionRequestPredicateRepository.addRequestPredicate(predicate);
}

@Override
public void resources(Function<ServerRequest, Mono<Resource>> lookupFunction) {
//ignore
}

@Override
public void unknown(RouterFunction<?> routerFunction) {
//ignore
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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.spring.webflux.support;

import org.springframework.web.reactive.function.server.RequestPredicate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* RequestPredicate cache for RouterFunction
*
* @author icodening
* @date 2022.03.29
*/
public class RouterFunctionRequestPredicateRepository {

private final List<RequestPredicate> requestPredicates = Collections.synchronizedList(new ArrayList<>());

public RouterFunctionRequestPredicateRepository() {
}

public void addRequestPredicate(RequestPredicate requestPredicate) {
this.requestPredicates.add(requestPredicate);
}

public List<RequestPredicate> getRequestPredicates() {
return requestPredicates;
}
}
Loading