Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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,51 @@
/*
* 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.beans.factory.SmartInitializingSingleton;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.support.RouterFunctionMapping;

/**
* RequestPredicate registrar for RouterFunction, existing RouterFunction can be record
*
* @author icodening
* @date 2022.03.29
*/
public class RequestPredicateRegistrar implements SmartInitializingSingleton {

private final RouterFunctionRequestPredicateRepository routerFunctionRequestPredicateRepository = new RouterFunctionRequestPredicateRepository();

private final RouterFunctionMapping routerFunctionMapping;

public RequestPredicateRegistrar(RouterFunctionMapping routerFunctionMapping) {
AssertUtil.notNull(routerFunctionMapping, "routerFunctionMapping cannot be null");
this.routerFunctionMapping = routerFunctionMapping;
}

@Override
public void afterSingletonsInstantiated() {
RouterFunction<?> routerFunction = this.routerFunctionMapping.getRouterFunction();
if (routerFunction != null) {
routerFunction.accept(new RouterFunctionRequestPredicateRegistrarVisitor(routerFunctionRequestPredicateRepository));
}
}

public RouterFunctionRequestPredicateRepository getRouterFunctionRequestPredicateRepository() {
return routerFunctionRequestPredicateRepository;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.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.List;

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

private final RouterFunctionRequestPredicateRepository routerFunctionRequestPredicateRepository;

public RouterFunctionBestMatchingPatternExtractor(RouterFunctionRequestPredicateRepository routerFunctionRequestPredicateRepository) {
this.routerFunctionRequestPredicateRepository = routerFunctionRequestPredicateRepository;
}

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

@Override
public String extract(HandlerMapping handlerMapping, ServerWebExchange exchange) {
SentinelServerRequest sentinelServerRequest = new SentinelServerRequest(exchange);
List<RequestPredicate> requestPredicates = routerFunctionRequestPredicateRepository.getRequestPredicates();
for (RequestPredicate requestPredicate : requestPredicates) {
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;
}
}
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
}
}
Loading