- Now the adapter is based-on sentinel-api-gateway-adapter-common and supports both proxyId and customized APIs Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -1,6 +1,7 @@ | |||
# Sentinel Zuul Adapter | |||
Sentinel Zuul Adapter provides **ServiceId level** and **API Path level** flow control for Zuul gateway service. | |||
Sentinel Zuul Adapter provides **route level** and **customized API level** | |||
flow control for Zuul API Gateway. | |||
> *Note*: this adapter only support Zuul 1.x. | |||
@@ -18,54 +19,68 @@ Sentinel Zuul Adapter provides **ServiceId level** and **API Path level** flow c | |||
2. Register filters | |||
For Spring Cloud Zuul users, we only need to inject the three filters in Spring configuration class like this: | |||
```java | |||
@Configuration | |||
public class ZuulConfig { | |||
@Bean | |||
public ZuulFilter sentinelZuulPreFilter() { | |||
// We can provider the filter order here. | |||
return new SentinelZuulPreFilter(10000); | |||
} | |||
@Bean | |||
public ZuulFilter sentinelZuulPostFilter() { | |||
return new SentinelZuulPostFilter(1000); | |||
} | |||
@Bean | |||
public ZuulFilter sentinelZuulErrorFilter() { | |||
return new SentinelZuulErrorFilter(-1); | |||
} | |||
} | |||
``` | |||
For original Zuul users: | |||
```java | |||
// get registry | |||
// Get filter registry | |||
final FilterRegistry r = FilterRegistry.instance(); | |||
// this is property config. set filter enable | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
properties.setEnabled(true); | |||
// set url cleaner, here use default | |||
DefaultUrlCleaner defaultUrlCleaner = new DefaultUrlCleaner(); | |||
// set origin parser. here use default | |||
DefaultRequestOriginParser defaultRequestOriginParser = new DefaultRequestOriginParser(); | |||
// register filters. you must register all three filters. | |||
SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, defaultUrlCleaner, defaultRequestOriginParser); | |||
r.put("sentinelPreFilter", sentinelPreFilter); | |||
SentinelPostFilter postFilter = new SentinelPostFilter(properties); | |||
r.put("sentinelPostFilter", postFilter); | |||
SentinelErrorFilter errorFilter = new SentinelErrorFilter(properties); | |||
r.put("sentinelErrorFilter", errorFilter); | |||
// We need to register all three filters. | |||
SentinelZuulPreFilter sentinelPreFilter = new SentinelZuulPreFilter(); | |||
r.put("sentinelZuulPreFilter", sentinelPreFilter); | |||
SentinelZuulPostFilter postFilter = new SentinelZuulPostFilter(); | |||
r.put("sentinelZuulPostFilter", postFilter); | |||
SentinelZuulErrorFilter errorFilter = new SentinelZuulErrorFilter(); | |||
r.put("sentinelZuulErrorFilter", errorFilter); | |||
``` | |||
## How it works | |||
As Zuul run as per thread per connection block model, we add filters around `route Filter` to trace sentinel statistics. | |||
As Zuul run as per thread per connection block model, we add filters around route filter to trace Sentinel statistics. | |||
- `SentinelPreFilter`: Get an entry of resource, the first order is **ServiceId** (the key in RequestContext is `serviceId`, this can set in own custom filter), then **API Path**. | |||
- `SentinelPostFilter`: When success response, exit entry. | |||
- `SentinelPreFilter`: When an `Exception` caught, trace the exception and exit context. | |||
- `SentinelZuulPreFilter`: This pre-filter will regard all proxy ID (`proxy` in `RequestContext`) and all customized API as resources. When a `BlockException` caught, the filter will try to find a fallback to execute. | |||
- `SentinelZuulPostFilter`: When the response has no exception caught, the post filter will complete the entries. | |||
- `SentinelZuulPreFilter`: When an exception is caught, the filter will trace the exception and complete the entries. | |||
<img width="792" src="https://user-images.githubusercontent.com/9305625/47277113-6b5da780-d5ef-11e8-8a0a-93a6b09b0887.png"> | |||
The order of filters can be changed in property. | |||
The order of filters can be changed via the constructor. | |||
The invocation chain resembles this: | |||
```bash | |||
EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
-EntranceNode: coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
--coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
---/coke/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
-EntranceNode: sentinel_default_context(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
-EntranceNode: book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
--book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
---/book/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
-EntranceNode: sentinel_gateway_context$$route$$another-route-b(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:8 1mb:1 1mt:9) | |||
--another-route-b(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:4 1mb:1 1mt:5) | |||
--another_customized_api(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:4 1mb:0 1mt:4) | |||
-EntranceNode: sentinel_gateway_context$$route$$my-route-1(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:6 1mb:0 1mt:6) | |||
--my-route-1(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:2 1mb:0 1mt:2) | |||
--some_customized_api(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:2 1mb:0 1mt:2) | |||
``` | |||
- `book` and `coke` are serviceId. | |||
- `/book/coke` is api path, the real API path is `/coke`. | |||
## Integration with Sentinel Dashboard | |||
1. Start [Sentinel Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard). | |||
@@ -76,7 +91,7 @@ EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) | |||
You can implement `SentinelFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown. | |||
The default fallback provider is `DefaultBlockFallbackProvider`. | |||
By default fallback route is `ServiveId + URI PATH`, example `/book/coke`, first `book` is serviceId, `/coke` is URI PATH, so that both can be needed. | |||
By default fallback route is proxy ID (or customized API name). | |||
Here is an example: | |||
@@ -90,7 +105,7 @@ public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider { | |||
// you can define root as service level | |||
@Override | |||
public String getRoute() { | |||
return "/coke/coke"; | |||
return "my-route"; | |||
} | |||
@Override | |||
@@ -21,6 +21,11 @@ | |||
<groupId>com.alibaba.csp</groupId> | |||
<artifactId>sentinel-core</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.alibaba.csp</groupId> | |||
<artifactId>sentinel-api-gateway-adapter-common</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.servlet</groupId> | |||
<artifactId>javax.servlet-api</artifactId> | |||
@@ -39,6 +44,20 @@ | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<!-- we need to use AntPathMatcher in spring-core --> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-core</artifactId> | |||
<version>4.3.20.RELEASE</version> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.cloud</groupId> | |||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId> | |||
<version>1.4.6.RELEASE</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
@@ -0,0 +1,47 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; | |||
import com.netflix.zuul.context.RequestContext; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public class RequestContextItemParser implements RequestItemParser<RequestContext> { | |||
@Override | |||
public String getPath(RequestContext requestContext) { | |||
return requestContext.getRequest().getServletPath(); | |||
} | |||
@Override | |||
public String getRemoteAddress(RequestContext requestContext) { | |||
return requestContext.getRequest().getRemoteAddr(); | |||
} | |||
@Override | |||
public String getHeader(RequestContext requestContext, String headerKey) { | |||
return requestContext.getRequest().getHeader(headerKey); | |||
} | |||
@Override | |||
public String getUrlParam(RequestContext requestContext, String paramName) { | |||
return requestContext.getRequest().getParameter(paramName); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.api; | |||
import java.util.Set; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public class ZuulApiDefinitionChangeObserver implements ApiDefinitionChangeObserver { | |||
@Override | |||
public void onChange(Set<ApiDefinition> apiDefinitions) { | |||
ZuulGatewayApiMatcherManager.loadApiDefinitions(apiDefinitions); | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.api; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.concurrent.ConcurrentHashMap; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher.RequestContextApiMatcher; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public final class ZuulGatewayApiMatcherManager { | |||
private static final Map<String, RequestContextApiMatcher> API_MATCHER_MAP = new ConcurrentHashMap<>(); | |||
public static Map<String, RequestContextApiMatcher> getApiMatcherMap() { | |||
return Collections.unmodifiableMap(API_MATCHER_MAP); | |||
} | |||
public static RequestContextApiMatcher getMatcher(final String apiName) { | |||
if (apiName == null) { | |||
return null; | |||
} | |||
return API_MATCHER_MAP.get(apiName); | |||
} | |||
public static Set<ApiDefinition> getApiDefinitionSet() { | |||
Set<ApiDefinition> set = new HashSet<>(); | |||
for (RequestContextApiMatcher matcher : API_MATCHER_MAP.values()) { | |||
set.add(matcher.getApiDefinition()); | |||
} | |||
return set; | |||
} | |||
static synchronized void loadApiDefinitions(/*@Valid*/ Set<ApiDefinition> definitions) { | |||
if (definitions == null || definitions.isEmpty()) { | |||
API_MATCHER_MAP.clear(); | |||
return; | |||
} | |||
for (ApiDefinition definition : definitions) { | |||
addApiDefinition(definition); | |||
} | |||
} | |||
static void addApiDefinition(ApiDefinition definition) { | |||
API_MATCHER_MAP.put(definition.getApiName(), new RequestContextApiMatcher(definition)); | |||
} | |||
private ZuulGatewayApiMatcherManager() {} | |||
} |
@@ -0,0 +1,72 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.api.matcher; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher.AbstractApiMatcher; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route.ZuulRouteMatchers; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||
import com.netflix.zuul.context.RequestContext; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public class RequestContextApiMatcher extends AbstractApiMatcher<RequestContext> { | |||
public RequestContextApiMatcher(ApiDefinition apiDefinition) { | |||
super(apiDefinition); | |||
} | |||
@Override | |||
protected void initializeMatchers() { | |||
if (apiDefinition.getPredicateItems() != null) { | |||
for (ApiPredicateItem item : apiDefinition.getPredicateItems()) { | |||
Predicate<RequestContext> predicate = fromApiPredicate(item); | |||
if (predicate != null) { | |||
matchers.add(predicate); | |||
} | |||
} | |||
} | |||
} | |||
private Predicate<RequestContext> fromApiPredicate(/*@NonNull*/ ApiPredicateItem item) { | |||
if (item instanceof ApiPathPredicateItem) { | |||
return fromApiPathPredicate((ApiPathPredicateItem)item); | |||
} | |||
return null; | |||
} | |||
private Predicate<RequestContext> fromApiPathPredicate(/*@Valid*/ ApiPathPredicateItem item) { | |||
String pattern = item.getPattern(); | |||
if (StringUtil.isBlank(pattern)) { | |||
return null; | |||
} | |||
switch (item.getMatchStrategy()) { | |||
case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX: | |||
return ZuulRouteMatchers.regexPath(pattern); | |||
case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX: | |||
return ZuulRouteMatchers.antPath(pattern); | |||
default: | |||
return ZuulRouteMatchers.exactPath(pattern); | |||
} | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.api.route; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||
import com.netflix.zuul.context.RequestContext; | |||
import org.springframework.util.AntPathMatcher; | |||
import org.springframework.util.PathMatcher; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public class PrefixRoutePathMatcher implements Predicate<RequestContext> { | |||
private final String pattern; | |||
private final PathMatcher pathMatcher; | |||
private final boolean canMatch; | |||
public PrefixRoutePathMatcher(String pattern) { | |||
AssertUtil.assertNotBlank(pattern, "pattern cannot be blank"); | |||
this.pattern = pattern; | |||
this.pathMatcher = new AntPathMatcher(); | |||
this.canMatch = pathMatcher.isPattern(pattern); | |||
} | |||
@Override | |||
public boolean test(RequestContext context) { | |||
String path = context.getRequest().getServletPath(); | |||
if (canMatch) { | |||
return pathMatcher.match(pattern, path); | |||
} | |||
return false; | |||
} | |||
public String getPattern() { | |||
return pattern; | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.api.route; | |||
import java.util.regex.Pattern; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||
import com.netflix.zuul.context.RequestContext; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public class RegexRoutePathMatcher implements Predicate<RequestContext> { | |||
private final String pattern; | |||
private final Pattern regex; | |||
public RegexRoutePathMatcher(String pattern) { | |||
AssertUtil.assertNotBlank(pattern, "pattern cannot be blank"); | |||
this.pattern = pattern; | |||
this.regex = Pattern.compile(pattern); | |||
} | |||
@Override | |||
public boolean test(RequestContext context) { | |||
String path = context.getRequest().getServletPath(); | |||
return regex.matcher(path).matches(); | |||
} | |||
public String getPattern() { | |||
return pattern; | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.api.route; | |||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||
import com.netflix.zuul.context.RequestContext; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public final class ZuulRouteMatchers { | |||
public static Predicate<RequestContext> all() { | |||
return new Predicate<RequestContext>() { | |||
@Override | |||
public boolean test(RequestContext requestContext) { | |||
return true; | |||
} | |||
}; | |||
} | |||
public static Predicate<RequestContext> antPath(String pathPattern) { | |||
return new PrefixRoutePathMatcher(pathPattern); | |||
} | |||
public static Predicate<RequestContext> exactPath(final String path) { | |||
return new Predicate<RequestContext>() { | |||
@Override | |||
public boolean test(RequestContext exchange) { | |||
return exchange.getRequest().getServletPath().equals(path); | |||
} | |||
}; | |||
} | |||
public static Predicate<RequestContext> regexPath(String pathPattern) { | |||
return new RegexRoutePathMatcher(pathPattern); | |||
} | |||
private ZuulRouteMatchers() {} | |||
} |
@@ -1,4 +1,4 @@ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback; | |||
import javax.servlet.http.HttpServletRequest; | |||
@@ -6,6 +6,7 @@ import javax.servlet.http.HttpServletRequest; | |||
* @author tiger | |||
*/ | |||
public class DefaultRequestOriginParser implements RequestOriginParser { | |||
@Override | |||
public String parseOrigin(HttpServletRequest request) { | |||
return ""; |
@@ -13,7 +13,7 @@ | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback; | |||
import javax.servlet.http.HttpServletRequest; | |||
@@ -0,0 +1,38 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.callback; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
public final class ZuulGatewayCallbackManager { | |||
private static volatile RequestOriginParser originParser = new DefaultRequestOriginParser(); | |||
public static RequestOriginParser getOriginParser() { | |||
return originParser; | |||
} | |||
public static void setOriginParser(RequestOriginParser originParser) { | |||
AssertUtil.notNull(originParser, "originParser cannot be null"); | |||
ZuulGatewayCallbackManager.originParser = originParser; | |||
} | |||
private ZuulGatewayCallbackManager() {} | |||
} |
@@ -14,7 +14,7 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.constants; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.constants; | |||
import com.netflix.zuul.ZuulFilter; | |||
@@ -27,6 +27,10 @@ public class ZuulConstant { | |||
* Zuul {@link com.netflix.zuul.context.RequestContext} key for use in load balancer. | |||
*/ | |||
public static final String SERVICE_ID_KEY = "serviceId"; | |||
/** | |||
* Zuul {@link com.netflix.zuul.context.RequestContext} key for proxying (route ID). | |||
*/ | |||
public static final String PROXY_ID_KEY = "proxy"; | |||
/** | |||
* {@link ZuulFilter#filterType()} error type. | |||
@@ -58,5 +62,12 @@ public class ZuulConstant { | |||
*/ | |||
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context"; | |||
/** | |||
* Zuul context key for keeping Sentinel entries. | |||
* | |||
* @since 1.6.0 | |||
*/ | |||
public static final String ZUUL_CTX_SENTINEL_ENTRIES_KEY = "_sentinel_entries"; | |||
private ZuulConstant(){} | |||
} |
@@ -14,7 +14,7 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; | |||
/** | |||
* Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException} | |||
@@ -22,7 +22,12 @@ package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
* @author tiger | |||
*/ | |||
public class BlockResponse { | |||
/** | |||
* HTTP status code. | |||
*/ | |||
private int code; | |||
private String message; | |||
private String route; | |||
@@ -14,9 +14,8 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
/** | |||
@@ -33,7 +32,6 @@ public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider { | |||
@Override | |||
public BlockResponse fallbackResponse(String route, Throwable cause) { | |||
RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route)); | |||
if (cause instanceof BlockException) { | |||
return new BlockResponse(429, "Sentinel block exception", route); | |||
} else { |
@@ -14,11 +14,13 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
/** | |||
* This provide fall back class manager. | |||
* | |||
@@ -26,7 +28,7 @@ import java.util.Map; | |||
*/ | |||
public class ZuulBlockFallbackManager { | |||
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<String, ZuulBlockFallbackProvider>(); | |||
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<>(); | |||
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider(); | |||
@@ -34,6 +36,7 @@ public class ZuulBlockFallbackManager { | |||
* Register special provider for different route. | |||
*/ | |||
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) { | |||
AssertUtil.notNull(provider, "fallback provider cannot be null"); | |||
String route = provider.getRoute(); | |||
if ("*".equals(route) || route == null) { | |||
defaultFallbackProvider = provider; |
@@ -14,7 +14,7 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; | |||
/** | |||
* This interface is compatible for different spring cloud version. |
@@ -0,0 +1,66 @@ | |||
/* | |||
* Copyright 1999-2019 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.zuul.filters; | |||
import java.util.Deque; | |||
import com.alibaba.csp.sentinel.AsyncEntry; | |||
import com.alibaba.csp.sentinel.Tracer; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; | |||
import com.alibaba.csp.sentinel.context.ContextUtil; | |||
import com.netflix.zuul.context.RequestContext; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
*/ | |||
final class SentinelEntryUtils { | |||
@SuppressWarnings("unchecked") | |||
static void tryExitFromCurrentContext() { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) { | |||
Deque<AsyncEntry> asyncEntries = (Deque<AsyncEntry>) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); | |||
AsyncEntry entry; | |||
while (!asyncEntries.isEmpty()) { | |||
entry = asyncEntries.pop(); | |||
entry.exit(); | |||
} | |||
ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); | |||
} | |||
ContextUtil.exit(); | |||
} | |||
@SuppressWarnings("unchecked") | |||
static void tryTraceExceptionThenExitFromCurrentContext(Throwable t) { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) { | |||
Deque<AsyncEntry> asyncEntries = (Deque<AsyncEntry>) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); | |||
AsyncEntry entry; | |||
while (!asyncEntries.isEmpty()) { | |||
entry = asyncEntries.pop(); | |||
Tracer.traceEntry(t, entry); | |||
entry.exit(); | |||
} | |||
ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); | |||
} | |||
ContextUtil.exit(); | |||
} | |||
private SentinelEntryUtils() {} | |||
} |
@@ -0,0 +1,75 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
import com.netflix.zuul.ZuulFilter; | |||
import com.netflix.zuul.context.RequestContext; | |||
import com.netflix.zuul.exception.ZuulException; | |||
/** | |||
* This filter track routing exception and exit entry; | |||
* | |||
* @author tiger | |||
* @author Eric Zhao | |||
*/ | |||
public class SentinelZuulErrorFilter extends ZuulFilter { | |||
private final int order; | |||
public SentinelZuulErrorFilter() { | |||
this(-1); | |||
} | |||
public SentinelZuulErrorFilter(int order) { | |||
this.order = order; | |||
} | |||
@Override | |||
public String filterType() { | |||
return ZuulConstant.ERROR_TYPE; | |||
} | |||
@Override | |||
public boolean shouldFilter() { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
return ctx.getThrowable() != null; | |||
} | |||
@Override | |||
public int filterOrder() { | |||
return order; | |||
} | |||
@Override | |||
public Object run() throws ZuulException { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
Throwable throwable = ctx.getThrowable(); | |||
if (throwable != null) { | |||
if (!BlockException.isBlockException(throwable)) { | |||
// Trace exception for each entry and exit entries in order. | |||
// The entries can be retrieved from the request context. | |||
SentinelEntryUtils.tryTraceExceptionThenExitFromCurrentContext(throwable); | |||
RecordLog.info("[SentinelZuulErrorFilter] Trace error cause", throwable.getCause()); | |||
} | |||
} | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; | |||
import com.netflix.zuul.ZuulFilter; | |||
import com.netflix.zuul.exception.ZuulException; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SEND_RESPONSE_FILTER_ORDER; | |||
/** | |||
* This filter will mark complete and exit {@link com.alibaba.csp.sentinel.Entry}. | |||
* | |||
* @author tiger | |||
* @author Eric Zhao | |||
*/ | |||
public class SentinelZuulPostFilter extends ZuulFilter { | |||
private final int order; | |||
public SentinelZuulPostFilter() { | |||
this(SEND_RESPONSE_FILTER_ORDER); | |||
} | |||
public SentinelZuulPostFilter(int order) { | |||
this.order = order; | |||
} | |||
@Override | |||
public String filterType() { | |||
return ZuulConstant.POST_TYPE; | |||
} | |||
@Override | |||
public int filterOrder() { | |||
return order; | |||
} | |||
@Override | |||
public boolean shouldFilter() { | |||
return true; | |||
} | |||
@Override | |||
public Object run() throws ZuulException { | |||
// Exit the entries in order. | |||
// The entries can be retrieved from the request context. | |||
SentinelEntryUtils.tryExitFromCurrentContext(); | |||
return null; | |||
} | |||
} |
@@ -0,0 +1,159 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; | |||
import java.util.ArrayDeque; | |||
import java.util.Deque; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import com.alibaba.csp.sentinel.AsyncEntry; | |||
import com.alibaba.csp.sentinel.EntryType; | |||
import com.alibaba.csp.sentinel.SphU; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser; | |||
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; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher.RequestContextApiMatcher; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.callback.ZuulGatewayCallbackManager; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackManager; | |||
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.StringUtil; | |||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||
import com.netflix.zuul.ZuulFilter; | |||
import com.netflix.zuul.context.RequestContext; | |||
import com.netflix.zuul.exception.ZuulException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; | |||
/** | |||
* This pre-filter will regard all {@code proxyId} and all customized API as resources. | |||
* When a BlockException caught, the filter will try to find a fallback to execute. | |||
* | |||
* @author tiger | |||
* @author Eric Zhao | |||
*/ | |||
public class SentinelZuulPreFilter extends ZuulFilter { | |||
private final int order; | |||
private final GatewayParamParser<RequestContext> paramParser = new GatewayParamParser<>( | |||
new RequestContextItemParser()); | |||
public SentinelZuulPreFilter() { | |||
this(10000); | |||
} | |||
public SentinelZuulPreFilter(int order) { | |||
this.order = order; | |||
} | |||
@Override | |||
public String filterType() { | |||
return ZuulConstant.PRE_TYPE; | |||
} | |||
/** | |||
* This run before route filter so we can get more accurate RT time. | |||
*/ | |||
@Override | |||
public int filterOrder() { | |||
return order; | |||
} | |||
@Override | |||
public boolean shouldFilter() { | |||
return true; | |||
} | |||
private void doSentinelEntry(String resourceName, final int resType, RequestContext requestContext, | |||
Deque<AsyncEntry> asyncEntries) throws BlockException { | |||
Object[] params = paramParser.parseParameterFor(resourceName, requestContext, | |||
new Predicate<GatewayFlowRule>() { | |||
@Override | |||
public boolean test(GatewayFlowRule r) { | |||
return r.getResourceMode() == resType; | |||
} | |||
}); | |||
AsyncEntry entry = SphU.asyncEntry(resourceName, EntryType.IN, 1, params); | |||
asyncEntries.push(entry); | |||
} | |||
@Override | |||
public Object run() throws ZuulException { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
String origin = parseOrigin(ctx.getRequest()); | |||
String routeId = (String)ctx.get(ZuulConstant.PROXY_ID_KEY); | |||
Deque<AsyncEntry> asyncEntries = new ArrayDeque<>(); | |||
String fallBackRoute = routeId; | |||
try { | |||
if (StringUtil.isNotBlank(routeId)) { | |||
ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin); | |||
doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, ctx, asyncEntries); | |||
} | |||
Set<String> matchingApis = pickMatchingApiDefinitions(ctx); | |||
if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) { | |||
ContextUtil.enter(ZuulConstant.ZUUL_DEFAULT_CONTEXT, origin); | |||
} | |||
for (String apiName : matchingApis) { | |||
fallBackRoute = apiName; | |||
doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, asyncEntries); | |||
} | |||
} catch (BlockException ex) { | |||
ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider( | |||
fallBackRoute); | |||
BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, ex); | |||
// Prevent routing from running | |||
ctx.setRouteHost(null); | |||
ctx.set(ZuulConstant.SERVICE_ID_KEY, null); | |||
// Set fallback response. | |||
ctx.setResponseBody(blockResponse.toString()); | |||
ctx.setResponseStatusCode(blockResponse.getCode()); | |||
} finally { | |||
// We don't exit the entry here. We need to exit the entries in post filter to record Rt correctly. | |||
// So here the entries will be carried in the request context. | |||
if (!asyncEntries.isEmpty()) { | |||
ctx.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, asyncEntries); | |||
} | |||
} | |||
return null; | |||
} | |||
private String parseOrigin(HttpServletRequest request) { | |||
return ZuulGatewayCallbackManager.getOriginParser().parseOrigin(request); | |||
} | |||
private Set<String> pickMatchingApiDefinitions(RequestContext requestContext) { | |||
Set<String> apis = new HashSet<>(); | |||
for (RequestContextApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) { | |||
if (matcher.test(requestContext)) { | |||
apis.add(matcher.getApiName()); | |||
} | |||
} | |||
return apis; | |||
} | |||
} |
@@ -1,27 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
/*** | |||
* @author tiger | |||
*/ | |||
public class DefaultUrlCleaner implements UrlCleaner { | |||
@Override | |||
public String clean(String originUrl) { | |||
return originUrl; | |||
} | |||
} |
@@ -1,31 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
/*** | |||
* @author tiger | |||
*/ | |||
public interface UrlCleaner { | |||
/*** | |||
* <p>Process the url. Some path variables should be handled and unified.</p> | |||
* <p>e.g. collect_item_relation--10200012121-.html will be converted to collect_item_relation.html</p> | |||
* | |||
* @param originUrl original url | |||
* @return processed url | |||
*/ | |||
String clean(String originUrl); | |||
} |
@@ -1,46 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
import com.netflix.zuul.ZuulFilter; | |||
/** | |||
* Abstract class for sentinel filters. | |||
* | |||
* @author tiger | |||
*/ | |||
public abstract class AbstractSentinelFilter extends ZuulFilter { | |||
private final SentinelZuulProperties sentinelZuulProperties; | |||
public SentinelZuulProperties getSentinelZuulProperties() { | |||
return sentinelZuulProperties; | |||
} | |||
public AbstractSentinelFilter(SentinelZuulProperties sentinelZuulProperties) { | |||
AssertUtil.notNull(sentinelZuulProperties,"SentinelZuulProperties can not be null"); | |||
this.sentinelZuulProperties = sentinelZuulProperties; | |||
} | |||
@Override | |||
public boolean shouldFilter() { | |||
return sentinelZuulProperties.isEnabled(); | |||
} | |||
} |
@@ -1,81 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
import com.alibaba.csp.sentinel.Tracer; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import com.alibaba.csp.sentinel.context.ContextUtil; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
import com.netflix.zuul.context.RequestContext; | |||
import com.netflix.zuul.exception.ZuulException; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE; | |||
/** | |||
* This filter track routing exception and exit entry; | |||
* | |||
* @author tiger | |||
*/ | |||
public class SentinelErrorFilter extends AbstractSentinelFilter { | |||
public SentinelErrorFilter(SentinelZuulProperties sentinelZuulProperties) { | |||
super(sentinelZuulProperties); | |||
} | |||
@Override | |||
public String filterType() { | |||
return ERROR_TYPE; | |||
} | |||
@Override | |||
public boolean shouldFilter() { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
return getSentinelZuulProperties().isEnabled() && ctx.getThrowable() != null; | |||
} | |||
@Override | |||
public int filterOrder() { | |||
return getSentinelZuulProperties().getOrder().getError(); | |||
} | |||
/** | |||
* Trace not {@link BlockException} ex. | |||
* While loop will exit all entries, | |||
* Case serviceId and URL entry twice in {@link SentinelPreFilter}. | |||
* The ContextUtil.getContext().getCurEntry() will exit from inner to outer. | |||
*/ | |||
@Override | |||
public Object run() throws ZuulException { | |||
try { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
Throwable throwable = ctx.getThrowable(); | |||
if (throwable != null) { | |||
if (!BlockException.isBlockException(throwable)) { | |||
Tracer.trace(throwable.getCause()); | |||
RecordLog.info("[Sentinel Error Filter] Trace cause", throwable.getCause()); | |||
} | |||
} | |||
} finally { | |||
while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) { | |||
ContextUtil.getContext().getCurEntry().exit(); | |||
} | |||
ContextUtil.exit(); | |||
} | |||
return null; | |||
} | |||
} |
@@ -1,60 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import com.alibaba.csp.sentinel.context.ContextUtil; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import com.netflix.zuul.exception.ZuulException; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE; | |||
/** | |||
* This filter do success routing RT statistic and exit {@link com.alibaba.csp.sentinel.Entry} | |||
* | |||
* @author tiger | |||
*/ | |||
public class SentinelPostFilter extends AbstractSentinelFilter { | |||
public SentinelPostFilter(SentinelZuulProperties sentinelZuulProperties) { | |||
super(sentinelZuulProperties); | |||
} | |||
@Override | |||
public String filterType() { | |||
return POST_TYPE; | |||
} | |||
@Override | |||
public int filterOrder() { | |||
return getSentinelZuulProperties().getOrder().getPost(); | |||
} | |||
/** | |||
* While loop will exit all entries, | |||
* Case serviceId and URL entry twice in {@link SentinelPreFilter}. | |||
* The ContextUtil.getContext().getCurEntry() will exit from inner to outer. | |||
*/ | |||
@Override | |||
public Object run() throws ZuulException { | |||
while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) { | |||
ContextUtil.getContext().getCurEntry().exit(); | |||
} | |||
ContextUtil.exit(); | |||
return null; | |||
} | |||
} |
@@ -1,112 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
import com.alibaba.csp.sentinel.EntryType; | |||
import com.alibaba.csp.sentinel.SphU; | |||
import com.alibaba.csp.sentinel.adapter.zuul.fallback.*; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import com.alibaba.csp.sentinel.adapter.zuul.util.FilterUtil; | |||
import com.alibaba.csp.sentinel.context.ContextUtil; | |||
import com.alibaba.csp.sentinel.log.RecordLog; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
import com.netflix.zuul.context.RequestContext; | |||
import com.netflix.zuul.exception.ZuulException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.PRE_TYPE; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SERVICE_ID_KEY; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ZUUL_DEFAULT_CONTEXT; | |||
/** | |||
* This pre filter get an entry of resource,the first order is ServiceId, then API Path. | |||
* When get a BlockException run fallback logic. | |||
* | |||
* @author tiger | |||
*/ | |||
public class SentinelPreFilter extends AbstractSentinelFilter { | |||
private final UrlCleaner urlCleaner; | |||
private final RequestOriginParser requestOriginParser; | |||
public SentinelPreFilter(SentinelZuulProperties sentinelZuulProperties, | |||
UrlCleaner urlCleaner, | |||
RequestOriginParser requestOriginParser) { | |||
super(sentinelZuulProperties); | |||
AssertUtil.notNull(urlCleaner, "UrlCleaner can not be null"); | |||
AssertUtil.notNull(requestOriginParser, "RequestOriginParser can not be null"); | |||
this.urlCleaner = urlCleaner; | |||
this.requestOriginParser = requestOriginParser; | |||
} | |||
@Override | |||
public String filterType() { | |||
return PRE_TYPE; | |||
} | |||
/** | |||
* This run before route filter so we can get more accurate RT time. | |||
*/ | |||
@Override | |||
public int filterOrder() { | |||
return getSentinelZuulProperties().getOrder().getPre(); | |||
} | |||
@Override | |||
public Object run() throws ZuulException { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
String origin = parseOrigin(ctx.getRequest()); | |||
String serviceTarget = (String) ctx.get(SERVICE_ID_KEY); | |||
// When serviceId blocked first get the service level fallback provider. | |||
String fallBackRoute = serviceTarget; | |||
try { | |||
if (StringUtil.isNotEmpty(serviceTarget)) { | |||
RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter ServiceId: %s", origin, serviceTarget)); | |||
ContextUtil.enter(serviceTarget, origin); | |||
SphU.entry(serviceTarget, EntryType.IN); | |||
} else { | |||
RecordLog.info("[Sentinel Pre Filter] ServiceId is empty"); | |||
ContextUtil.enter(ZUUL_DEFAULT_CONTEXT, origin); | |||
} | |||
String uriTarget = FilterUtil.filterTarget(ctx.getRequest()); | |||
// Clean and unify the URL. | |||
// For REST APIs, you have to clean the URL (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or | |||
// the amount of context and resources will exceed the threshold. | |||
uriTarget = urlCleaner.clean(uriTarget); | |||
fallBackRoute = uriTarget; | |||
RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter Uri Path: %s", origin, uriTarget)); | |||
SphU.entry(uriTarget, EntryType.IN); | |||
} catch (BlockException ex) { | |||
RecordLog.warn(String.format("[Sentinel Pre Filter] Block Exception when Origin: %s enter fall back route: %s", origin, fallBackRoute), ex); | |||
ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(fallBackRoute); | |||
BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, ex); | |||
// prevent routing from running | |||
ctx.setRouteHost(null); | |||
ctx.set(SERVICE_ID_KEY, null); | |||
ctx.setResponseBody(blockResponse.toString()); | |||
} | |||
return null; | |||
} | |||
private String parseOrigin(HttpServletRequest request) { | |||
return requestOriginParser.parseOrigin(request); | |||
} | |||
} |
@@ -1,80 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.properties; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SEND_RESPONSE_FILTER_ORDER; | |||
/** | |||
* Sentinel Spring Cloud Zuul AutoConfiguration property. | |||
* | |||
* @author tiger | |||
*/ | |||
public class SentinelZuulProperties { | |||
private boolean enabled = false; | |||
private Order order = new Order(); | |||
public static class Order { | |||
private int post = SEND_RESPONSE_FILTER_ORDER - 10; | |||
private int pre = 10000; | |||
private int error = -1; | |||
public int getPost() { | |||
return post; | |||
} | |||
public void setPost(int post) { | |||
this.post = post; | |||
} | |||
public int getPre() { | |||
return pre; | |||
} | |||
public void setPre(int pre) { | |||
this.pre = pre; | |||
} | |||
public int getError() { | |||
return error; | |||
} | |||
public void setError(int error) { | |||
this.error = error; | |||
} | |||
} | |||
public boolean isEnabled() { | |||
return enabled; | |||
} | |||
public void setEnabled(boolean enabled) { | |||
this.enabled = enabled; | |||
} | |||
public Order getOrder() { | |||
return order; | |||
} | |||
public void setOrder(Order order) { | |||
this.order = order; | |||
} | |||
} |
@@ -1,157 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.util; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
import javax.servlet.http.HttpServletRequest; | |||
/** | |||
* Util class for web servlet filter. | |||
* This is same as servlet adapter util class | |||
* | |||
* @author tiger | |||
*/ | |||
public final class FilterUtil { | |||
public static String filterTarget(HttpServletRequest request) { | |||
String pathInfo = getResourcePath(request); | |||
if (!pathInfo.startsWith("/")) { | |||
pathInfo = "/" + pathInfo; | |||
} | |||
if ("/".equals(pathInfo)) { | |||
return pathInfo; | |||
} | |||
// Note: pathInfo should be converted to camelCase style. | |||
int lastSlashIndex = pathInfo.lastIndexOf("/"); | |||
if (lastSlashIndex >= 0) { | |||
pathInfo = pathInfo.substring(0, lastSlashIndex) + "/" | |||
+ StringUtil.trim(pathInfo.substring(lastSlashIndex + 1)); | |||
} else { | |||
pathInfo = "/" + StringUtil.trim(pathInfo); | |||
} | |||
return pathInfo; | |||
} | |||
private static String getResourcePath(HttpServletRequest request) { | |||
String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false); | |||
String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0); | |||
return servletPath + pathInfo; | |||
} | |||
private static String normalizeAbsolutePath(String path, boolean removeTrailingSlash) throws IllegalStateException { | |||
return normalizePath(path, true, false, removeTrailingSlash); | |||
} | |||
private static String normalizePath(String path, boolean forceAbsolute, boolean forceRelative, | |||
boolean removeTrailingSlash) throws IllegalStateException { | |||
char[] pathChars = StringUtil.trimToEmpty(path).toCharArray(); | |||
int length = pathChars.length; | |||
// Check path and slash. | |||
boolean startsWithSlash = false; | |||
boolean endsWithSlash = false; | |||
if (length > 0) { | |||
char firstChar = pathChars[0]; | |||
char lastChar = pathChars[length - 1]; | |||
startsWithSlash = firstChar == '/' || firstChar == '\\'; | |||
endsWithSlash = lastChar == '/' || lastChar == '\\'; | |||
} | |||
StringBuilder buf = new StringBuilder(length); | |||
boolean isAbsolutePath = forceAbsolute || !forceRelative && startsWithSlash; | |||
int index = startsWithSlash ? 0 : -1; | |||
int level = 0; | |||
if (isAbsolutePath) { | |||
buf.append("/"); | |||
} | |||
while (index < length) { | |||
index = indexOfSlash(pathChars, index + 1, false); | |||
if (index == length) { | |||
break; | |||
} | |||
int nextSlashIndex = indexOfSlash(pathChars, index, true); | |||
String element = new String(pathChars, index, nextSlashIndex - index); | |||
index = nextSlashIndex; | |||
// Ignore "." | |||
if (".".equals(element)) { | |||
continue; | |||
} | |||
// Backtrack ".." | |||
if ("..".equals(element)) { | |||
if (level == 0) { | |||
if (isAbsolutePath) { | |||
throw new IllegalStateException(path); | |||
} else { | |||
buf.append("../"); | |||
} | |||
} else { | |||
buf.setLength(pathChars[--level]); | |||
} | |||
continue; | |||
} | |||
pathChars[level++] = (char)buf.length(); | |||
buf.append(element).append('/'); | |||
} | |||
// remove the last "/" | |||
if (buf.length() > 0) { | |||
if (!endsWithSlash || removeTrailingSlash) { | |||
buf.setLength(buf.length() - 1); | |||
} | |||
} | |||
return buf.toString(); | |||
} | |||
private static int indexOfSlash(char[] chars, int beginIndex, boolean slash) { | |||
int i = beginIndex; | |||
for (; i < chars.length; i++) { | |||
char ch = chars[i]; | |||
if (slash) { | |||
if (ch == '/' || ch == '\\') { | |||
break; // if a slash | |||
} | |||
} else { | |||
if (ch != '/' && ch != '\\') { | |||
break; // if not a slash | |||
} | |||
} | |||
} | |||
return i; | |||
} | |||
private FilterUtil() {} | |||
} |
@@ -0,0 +1 @@ | |||
com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulApiDefinitionChangeObserver |
@@ -14,13 +14,13 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
/** | |||
* @author tiger | |||
*/ | |||
@@ -56,7 +56,7 @@ public class ZuulBlockFallbackManagerTest { | |||
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider); | |||
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE); | |||
ZuulBlockFallbackManager.clear(); | |||
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(),DEFAULT_ROUTE); | |||
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(), DEFAULT_ROUTE); | |||
} | |||
} |
@@ -14,9 +14,10 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
@@ -48,7 +49,8 @@ public class ZuulBlockFallbackProviderTest { | |||
@Test | |||
public void testFlowControlFallbackResponse() throws Exception { | |||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE); | |||
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new FlowException("flow exception")); | |||
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, | |||
new FlowException("flow exception")); | |||
Assert.assertEquals(clientHttpResponse.getCode(), 429); | |||
} | |||
@@ -14,42 +14,37 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import com.netflix.zuul.context.RequestContext; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.ERROR_TYPE; | |||
/** | |||
* @author tiger | |||
*/ | |||
public class SentinelErrorFilterTest { | |||
public class SentinelZuulErrorFilterTest { | |||
@Test | |||
public void testFilterType() throws Exception { | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); | |||
Assert.assertEquals(sentinelErrorFilter.filterType(), ERROR_TYPE); | |||
SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter(); | |||
Assert.assertEquals(sentinelZuulErrorFilter.filterType(), ERROR_TYPE); | |||
} | |||
@Test | |||
public void testShouldFilter() { | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
Assert.assertFalse(properties.isEnabled()); | |||
properties.setEnabled(true); | |||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); | |||
SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter(); | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
ctx.setThrowable(new RuntimeException()); | |||
Assert.assertTrue(sentinelErrorFilter.shouldFilter()); | |||
Assert.assertTrue(sentinelZuulErrorFilter.shouldFilter()); | |||
} | |||
@Test | |||
public void testRun() throws Exception { | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); | |||
Object result = sentinelErrorFilter.run(); | |||
SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter(); | |||
Object result = sentinelZuulErrorFilter.run(); | |||
Assert.assertNull(result); | |||
} | |||
@@ -14,31 +14,28 @@ | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.POST_TYPE; | |||
/** | |||
* @author tiger | |||
*/ | |||
public class SentinelPostFilterTest { | |||
public class SentinelZuulPostFilterTest { | |||
@Test | |||
public void testFilterType() throws Exception { | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties); | |||
Assert.assertEquals(sentinelPostFilter.filterType(), POST_TYPE); | |||
SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter(); | |||
Assert.assertEquals(sentinelZuulPostFilter.filterType(), POST_TYPE); | |||
} | |||
@Test | |||
public void testRun() throws Exception { | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties); | |||
Object result = sentinelPostFilter.run(); | |||
SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter(); | |||
Object result = sentinelZuulPostFilter.run(); | |||
Assert.assertNull(result); | |||
} | |||
@@ -0,0 +1,61 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; | |||
import com.netflix.zuul.context.RequestContext; | |||
import org.junit.Assert; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import javax.servlet.http.HttpServletRequest; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.PRE_TYPE; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SERVICE_ID_KEY; | |||
import static org.mockito.Mockito.when; | |||
/** | |||
* @author tiger | |||
*/ | |||
public class SentinelZuulPreFilterTest { | |||
private String SERVICE_ID = "servicea"; | |||
private String URI = "/servicea/test"; | |||
@Mock | |||
private HttpServletRequest httpServletRequest; | |||
@Before | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
when(httpServletRequest.getContextPath()).thenReturn(""); | |||
when(httpServletRequest.getPathInfo()).thenReturn(URI); | |||
RequestContext requestContext = new RequestContext(); | |||
requestContext.set(SERVICE_ID_KEY, SERVICE_ID); | |||
requestContext.setRequest(httpServletRequest); | |||
RequestContext.testSetCurrentContext(requestContext); | |||
} | |||
@Test | |||
public void testFilterType() throws Exception { | |||
SentinelZuulPreFilter sentinelZuulPreFilter = new SentinelZuulPreFilter(); | |||
Assert.assertEquals(sentinelZuulPreFilter.filterType(), PRE_TYPE); | |||
} | |||
} |
@@ -1,106 +0,0 @@ | |||
/* | |||
* Copyright 1999-2018 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 | |||
* | |||
* 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 com.alibaba.csp.sentinel.adapter.zuul.filters; | |||
import com.alibaba.csp.sentinel.adapter.zuul.fallback.DefaultRequestOriginParser; | |||
import com.alibaba.csp.sentinel.adapter.zuul.fallback.RequestOriginParser; | |||
import com.alibaba.csp.sentinel.adapter.zuul.fallback.UrlCleaner; | |||
import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | |||
import com.netflix.zuul.context.RequestContext; | |||
import org.junit.Assert; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import org.mockito.invocation.InvocationOnMock; | |||
import org.mockito.stubbing.Answer; | |||
import javax.servlet.http.HttpServletRequest; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.PRE_TYPE; | |||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SERVICE_ID_KEY; | |||
import static org.mockito.BDDMockito.given; | |||
import static org.mockito.Mockito.when; | |||
/** | |||
* @author tiger | |||
*/ | |||
public class SentinelPreFilterTest { | |||
private String SERVICE_ID = "servicea"; | |||
private String URI = "/servicea/test"; | |||
@Mock | |||
private HttpServletRequest httpServletRequest; | |||
@Mock | |||
private UrlCleaner urlCleaner; | |||
private final RequestOriginParser requestOriginParser = new DefaultRequestOriginParser(); | |||
@Before | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
when(httpServletRequest.getContextPath()).thenReturn(""); | |||
when(httpServletRequest.getPathInfo()).thenReturn(URI); | |||
RequestContext requestContext = new RequestContext(); | |||
requestContext.set(SERVICE_ID_KEY, SERVICE_ID); | |||
requestContext.setRequest(httpServletRequest); | |||
RequestContext.testSetCurrentContext(requestContext); | |||
} | |||
@Test | |||
public void testFilterType() throws Exception { | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser); | |||
Assert.assertEquals(sentinelPreFilter.filterType(), PRE_TYPE); | |||
} | |||
@Test | |||
public void testRun() throws Exception { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser); | |||
given(urlCleaner.clean(URI)).willReturn(URI); | |||
sentinelPreFilter.run(); | |||
Assert.assertNull(ctx.getRouteHost()); | |||
Assert.assertEquals(ctx.get(SERVICE_ID_KEY), SERVICE_ID); | |||
} | |||
@Test | |||
public void testServiceFallBackRun() throws Exception { | |||
RequestContext ctx = RequestContext.getCurrentContext(); | |||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||
properties.setEnabled(true); | |||
SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser); | |||
given(urlCleaner.clean(URI)).willAnswer( | |||
new Answer<Object>() { | |||
@Override | |||
public Object answer(InvocationOnMock invocation) throws Throwable { | |||
throw new FlowException("flow ex"); | |||
} | |||
} | |||
); | |||
sentinelPreFilter.run(); | |||
Assert.assertNull(ctx.getRouteHost()); | |||
Assert.assertNull(ctx.get(SERVICE_ID_KEY)); | |||
} | |||
} |