- implement `SentinelPreFilter`, `SentinelPostFilter` and `SentinelErrorFilter` - support fallbackmaster
@@ -18,6 +18,7 @@ | |||||
<module>sentinel-web-servlet</module> | <module>sentinel-web-servlet</module> | ||||
<module>sentinel-dubbo-adapter</module> | <module>sentinel-dubbo-adapter</module> | ||||
<module>sentinel-grpc-adapter</module> | <module>sentinel-grpc-adapter</module> | ||||
<module>sentinel-zuul-adapter</module> | |||||
</modules> | </modules> | ||||
<dependencyManagement> | <dependencyManagement> | ||||
@@ -32,7 +33,11 @@ | |||||
<artifactId>sentinel-extension</artifactId> | <artifactId>sentinel-extension</artifactId> | ||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-web-servlet</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>junit</groupId> | <groupId>junit</groupId> | ||||
<artifactId>junit</artifactId> | <artifactId>junit</artifactId> | ||||
@@ -0,0 +1,148 @@ | |||||
# Sentinel Zuul Adapter | |||||
Zuul does not provide rateLimit function, If use default `SentinelRibbonFilter` route filter. it wrapped by Hystrix Command. so only provide Service level | |||||
circuit protect. | |||||
Sentinel can provide `ServiceId` level and `API Path` level flow control for zuul gateway service. | |||||
*Note*: this project is for zuul 1. | |||||
## How to use | |||||
1. Add maven dependency | |||||
```xml | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-zuul-adapter</artifactId> | |||||
<version>x.y.z</version> | |||||
</dependency> | |||||
``` | |||||
2. Register filters | |||||
```java | |||||
// get registry | |||||
final FilterRegistry r = FilterRegistry.instance(); | |||||
// this is property config. set filter ennable | |||||
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); | |||||
``` | |||||
## How it works | |||||
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 get an `Exception`, trace the exception and exit context. | |||||
the order of Filter can be changed in property: | |||||
Filters create structure like: | |||||
```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) | |||||
``` | |||||
`book` and `coke` are serviceId. | |||||
`---/book/coke` is api path, the real uri is `/coke`. | |||||
## Integration with Sentinel DashBord | |||||
1. Start [Sentinel DashBord](https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0). | |||||
2. Sentinel has full rule config features. see [Dynamic-Rule-Configuration](https://github.com/alibaba/Sentinel/wiki/Dynamic-Rule-Configuration) | |||||
## Fallbacks | |||||
Implements `SentinelFallbackProvider` to define your own Fallback Provider when Sentinel Block Exception throwing. 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. | |||||
Here is an example: | |||||
```java | |||||
// custom provider | |||||
public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider { | |||||
private Logger logger = LoggerFactory.getLogger(DefaultBlockFallbackProvider.class); | |||||
// you can define root as service level | |||||
@Override | |||||
public String getRoute() { | |||||
return "/coke/coke"; | |||||
} | |||||
@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 { | |||||
return new BlockResponse(500, "System Error", route); | |||||
} | |||||
} | |||||
} | |||||
// register fallback | |||||
ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider()); | |||||
``` | |||||
Default block response | |||||
```json | |||||
{ | |||||
"code":429, | |||||
"message":"Sentinel block exception", | |||||
"route":"/" | |||||
} | |||||
``` | |||||
## Origin parser | |||||
自定义解析URL | |||||
```java | |||||
public class DefaultRequestOriginParser implements RequestOriginParser { | |||||
@Override | |||||
public String parseOrigin(HttpServletRequest request) { | |||||
return ""; | |||||
} | |||||
} | |||||
``` |
@@ -0,0 +1,48 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||||
<parent> | |||||
<artifactId>sentinel-adapter</artifactId> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<version>1.4.2-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>sentinel-zuul-adapter</artifactId> | |||||
<packaging>jar</packaging> | |||||
<properties> | |||||
<zuul.version>1.3.1</zuul.version> | |||||
<servlet.api.version>3.1.0</servlet.api.version> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.alibaba.csp</groupId> | |||||
<artifactId>sentinel-core</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.netflix.zuul</groupId> | |||||
<artifactId>zuul-core</artifactId> | |||||
<version>${zuul.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.mockito</groupId> | |||||
<artifactId>mockito-core</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>javax.servlet</groupId> | |||||
<artifactId>javax.servlet-api</artifactId> | |||||
<version>${servlet.api.version}</version> | |||||
<scope>provided</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,62 @@ | |||||
/* | |||||
* 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.constants; | |||||
import com.netflix.zuul.ZuulFilter; | |||||
/** | |||||
* @author tiger | |||||
*/ | |||||
public class ZuulConstant { | |||||
/** | |||||
* Zuul {@link com.netflix.zuul.context.RequestContext} key for use in load balancer. | |||||
*/ | |||||
public static final String SERVICE_ID_KEY = "serviceId"; | |||||
/** | |||||
* {@link ZuulFilter#filterType()} error type. | |||||
*/ | |||||
public static final String ERROR_TYPE = "error"; | |||||
/** | |||||
* {@link ZuulFilter#filterType()} post type. | |||||
*/ | |||||
public static final String POST_TYPE = "post"; | |||||
/** | |||||
* {@link ZuulFilter#filterType()} pre type. | |||||
*/ | |||||
public static final String PRE_TYPE = "pre"; | |||||
/** | |||||
* {@link ZuulFilter#filterType()} route type. | |||||
*/ | |||||
public static final String ROUTE_TYPE = "route"; | |||||
/** | |||||
* Filter Order for SEND_RESPONSE_FILTER_ORDER | |||||
*/ | |||||
public static final int SEND_RESPONSE_FILTER_ORDER = 1000; | |||||
/** | |||||
* Zuul use Sentinel as default context when serviceId is empty. | |||||
*/ | |||||
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context"; | |||||
private ZuulConstant(){} | |||||
} |
@@ -0,0 +1,67 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException} | |||||
* | |||||
* @author tiger | |||||
*/ | |||||
public class BlockResponse { | |||||
private int code; | |||||
private String message; | |||||
private String route; | |||||
public BlockResponse(int code, String message, String route) { | |||||
this.code = code; | |||||
this.message = message; | |||||
this.route = route; | |||||
} | |||||
public int getCode() { | |||||
return code; | |||||
} | |||||
public void setCode(int code) { | |||||
this.code = code; | |||||
} | |||||
public String getMessage() { | |||||
return message; | |||||
} | |||||
public void setMessage(String message) { | |||||
this.message = message; | |||||
} | |||||
public String getRoute() { | |||||
return route; | |||||
} | |||||
public void setRoute(String route) { | |||||
this.route = route; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "{" + | |||||
"\"code\":" + code + | |||||
", \"message\":" + "\"" + message + "\"" + | |||||
", \"route\":" + "\"" + route + "\"" + | |||||
'}'; | |||||
} | |||||
} |
@@ -0,0 +1,43 @@ | |||||
/* | |||||
* 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; | |||||
import com.alibaba.csp.sentinel.log.RecordLog; | |||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||||
/** | |||||
* Default Fallback provider for sentinel {@link BlockException}, {@literal *} meant for all routes. | |||||
* | |||||
* @author tiger | |||||
*/ | |||||
public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider { | |||||
@Override | |||||
public String getRoute() { | |||||
return "*"; | |||||
} | |||||
@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 { | |||||
return new BlockResponse(500, "System Error", route); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package com.alibaba.csp.sentinel.adapter.zuul.fallback; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
/** | |||||
* @author tiger | |||||
*/ | |||||
public class DefaultRequestOriginParser implements RequestOriginParser { | |||||
@Override | |||||
public String parseOrigin(HttpServletRequest request) { | |||||
return ""; | |||||
} | |||||
} |
@@ -0,0 +1,27 @@ | |||||
/* | |||||
* 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; | |||||
} | |||||
} |
@@ -0,0 +1,34 @@ | |||||
/* | |||||
* 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; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
/** | |||||
* The origin parser parses request origin (e.g. IP, user, appName) from HTTP request. | |||||
* | |||||
* @author tiger | |||||
*/ | |||||
public interface RequestOriginParser { | |||||
/** | |||||
* Parse the origin from given HTTP request. | |||||
* | |||||
* @param request HTTP request | |||||
* @return parsed origin | |||||
*/ | |||||
String parseOrigin(HttpServletRequest request); | |||||
} |
@@ -0,0 +1,31 @@ | |||||
/* | |||||
* 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); | |||||
} |
@@ -0,0 +1,57 @@ | |||||
/* | |||||
* 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; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
/** | |||||
* This provide fall back class manager. | |||||
* | |||||
* @author tiger | |||||
*/ | |||||
public class ZuulBlockFallbackManager { | |||||
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<String, ZuulBlockFallbackProvider>(); | |||||
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider(); | |||||
/** | |||||
* Register special provider for different route. | |||||
*/ | |||||
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) { | |||||
String route = provider.getRoute(); | |||||
if ("*".equals(route) || route == null) { | |||||
defaultFallbackProvider = provider; | |||||
} else { | |||||
fallbackProviderCache.put(route, provider); | |||||
} | |||||
} | |||||
public static ZuulBlockFallbackProvider getFallbackProvider(String route) { | |||||
ZuulBlockFallbackProvider provider = fallbackProviderCache.get(route); | |||||
if (provider == null) { | |||||
provider = defaultFallbackProvider; | |||||
} | |||||
return provider; | |||||
} | |||||
public synchronized static void clear(){ | |||||
fallbackProviderCache.clear(); | |||||
} | |||||
} |
@@ -0,0 +1,40 @@ | |||||
/* | |||||
* 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; | |||||
/** | |||||
* This interface is compatible for different spring cloud version. | |||||
* | |||||
* @author tiger | |||||
*/ | |||||
public interface ZuulBlockFallbackProvider { | |||||
/** | |||||
* The route this fallback will be used for. | |||||
* @return The route the fallback will be used for. | |||||
*/ | |||||
String getRoute(); | |||||
/** | |||||
* Provides a fallback response based on the cause of the failed execution. | |||||
* | |||||
* @param route The route the fallback is for | |||||
* @param cause cause of the main method failure, may be <code>null</code> | |||||
* @return the fallback response | |||||
*/ | |||||
BlockResponse fallbackResponse(String route, Throwable cause); | |||||
} |
@@ -0,0 +1,46 @@ | |||||
/* | |||||
* 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(); | |||||
} | |||||
} |
@@ -0,0 +1,81 @@ | |||||
/* | |||||
* 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; | |||||
} | |||||
} |
@@ -0,0 +1,60 @@ | |||||
/* | |||||
* 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; | |||||
} | |||||
} |
@@ -0,0 +1,112 @@ | |||||
/* | |||||
* 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); | |||||
} | |||||
} |
@@ -0,0 +1,80 @@ | |||||
/* | |||||
* 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; | |||||
} | |||||
} |
@@ -0,0 +1,157 @@ | |||||
/* | |||||
* 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,62 @@ | |||||
/* | |||||
* 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; | |||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | |||||
import org.junit.Assert; | |||||
import org.junit.Test; | |||||
/** | |||||
* @author tiger | |||||
*/ | |||||
public class ZuulBlockFallbackManagerTest { | |||||
private String ROUTE = "/test"; | |||||
private String DEFAULT_ROUTE = "*"; | |||||
class MyNullResponseFallBackProvider implements ZuulBlockFallbackProvider { | |||||
@Override | |||||
public String getRoute() { | |||||
return ROUTE; | |||||
} | |||||
@Override | |||||
public BlockResponse fallbackResponse(String route, Throwable cause) { | |||||
return null; | |||||
} | |||||
} | |||||
@Test | |||||
public void testRegisterProvider() throws Exception { | |||||
MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider(); | |||||
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider); | |||||
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE); | |||||
Assert.assertNull(myNullResponseFallBackProvider.fallbackResponse(ROUTE, new FlowException("flow ex"))); | |||||
} | |||||
@Test | |||||
public void clear() { | |||||
MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider(); | |||||
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider); | |||||
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE); | |||||
ZuulBlockFallbackManager.clear(); | |||||
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(),DEFAULT_ROUTE); | |||||
} | |||||
} |
@@ -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.zuul.fallback; | |||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException; | |||||
import org.junit.Assert; | |||||
import org.junit.Test; | |||||
/** | |||||
* @author tiger | |||||
*/ | |||||
public class ZuulBlockFallbackProviderTest { | |||||
private String ALL_ROUTE = "*"; | |||||
@Test | |||||
public void testGetNullRoute() throws Exception { | |||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(null); | |||||
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE); | |||||
} | |||||
@Test | |||||
public void testGetDefaultRoute() throws Exception { | |||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE); | |||||
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE); | |||||
} | |||||
@Test | |||||
public void testGetNotInCacheRoute() throws Exception { | |||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider("/not/in"); | |||||
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE); | |||||
} | |||||
@Test | |||||
public void testFlowControlFallbackResponse() throws Exception { | |||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE); | |||||
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new FlowException("flow exception")); | |||||
Assert.assertEquals(clientHttpResponse.getCode(), 429); | |||||
} | |||||
@Test | |||||
public void testRuntimeExceptionFallbackResponse() throws Exception { | |||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE); | |||||
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new RuntimeException()); | |||||
Assert.assertEquals(clientHttpResponse.getCode(), 500); | |||||
} | |||||
} |
@@ -0,0 +1,56 @@ | |||||
/* | |||||
* 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.netflix.zuul.context.RequestContext; | |||||
import org.junit.Assert; | |||||
import org.junit.Test; | |||||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE; | |||||
/** | |||||
* @author tiger | |||||
*/ | |||||
public class SentinelErrorFilterTest { | |||||
@Test | |||||
public void testFilterType() throws Exception { | |||||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); | |||||
Assert.assertEquals(sentinelErrorFilter.filterType(), ERROR_TYPE); | |||||
} | |||||
@Test | |||||
public void testShouldFilter() { | |||||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||||
Assert.assertFalse(properties.isEnabled()); | |||||
properties.setEnabled(true); | |||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); | |||||
RequestContext ctx = RequestContext.getCurrentContext(); | |||||
ctx.setThrowable(new RuntimeException()); | |||||
Assert.assertTrue(sentinelErrorFilter.shouldFilter()); | |||||
} | |||||
@Test | |||||
public void testRun() throws Exception { | |||||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); | |||||
Object result = sentinelErrorFilter.run(); | |||||
Assert.assertNull(result); | |||||
} | |||||
} |
@@ -0,0 +1,45 @@ | |||||
/* | |||||
* 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 org.junit.Assert; | |||||
import org.junit.Test; | |||||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE; | |||||
/** | |||||
* @author tiger | |||||
*/ | |||||
public class SentinelPostFilterTest { | |||||
@Test | |||||
public void testFilterType() throws Exception { | |||||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties); | |||||
Assert.assertEquals(sentinelPostFilter.filterType(), POST_TYPE); | |||||
} | |||||
@Test | |||||
public void testRun() throws Exception { | |||||
SentinelZuulProperties properties = new SentinelZuulProperties(); | |||||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties); | |||||
Object result = sentinelPostFilter.run(); | |||||
Assert.assertNull(result); | |||||
} | |||||
} |
@@ -0,0 +1,106 @@ | |||||
/* | |||||
* 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)); | |||||
} | |||||
} |