* Return canonical status 429 in the default block handler of sentinel-web-servlet-adapter. * Add a `csp.sentinel.web.servlet.block.page.http.status` property to support customized block status configuration.master
@@ -18,8 +18,10 @@ package com.alibaba.csp.sentinel.adapter.servlet.config; | |||||
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; | import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter; | ||||
import com.alibaba.csp.sentinel.adapter.servlet.CommonTotalFilter; | import com.alibaba.csp.sentinel.adapter.servlet.CommonTotalFilter; | ||||
import com.alibaba.csp.sentinel.config.SentinelConfig; | import com.alibaba.csp.sentinel.config.SentinelConfig; | ||||
import com.alibaba.csp.sentinel.util.StringUtil; | |||||
/** | /** | ||||
* @author zhaoyuguang | |||||
* @author leyou | * @author leyou | ||||
*/ | */ | ||||
public class WebServletConfig { | public class WebServletConfig { | ||||
@@ -28,6 +30,10 @@ public class WebServletConfig { | |||||
public static final String BLOCK_PAGE = "csp.sentinel.web.servlet.block.page"; | public static final String BLOCK_PAGE = "csp.sentinel.web.servlet.block.page"; | ||||
public static final String BLOCK_PAGE_HTTP_STATUS = "csp.sentinel.web.servlet.block.page.http.status"; | |||||
private static final int HTTP_STATUS_TOO_MANY_REQUESTS = 429; | |||||
/** | /** | ||||
* Get redirecting page when Sentinel blocking for {@link CommonFilter} or | * Get redirecting page when Sentinel blocking for {@link CommonFilter} or | ||||
* {@link CommonTotalFilter} occurs. | * {@link CommonTotalFilter} occurs. | ||||
@@ -41,4 +47,31 @@ public class WebServletConfig { | |||||
public static void setBlockPage(String blockPage) { | public static void setBlockPage(String blockPage) { | ||||
SentinelConfig.setConfig(BLOCK_PAGE, blockPage); | SentinelConfig.setConfig(BLOCK_PAGE, blockPage); | ||||
} | } | ||||
/** | |||||
* Return status 429 in the default block page, | |||||
* you can use -Dcsp.sentinel.web.servlet.block.page.http.status=200 or other http status, | |||||
* to set http status which you want of the default block page. | |||||
* When csp.sentinel.web.servlet.block.page.http.status is empty or not number, | |||||
* the block page http status will be automatically set to 429(Too Many Requests). | |||||
* | |||||
* @return block page http status | |||||
*/ | |||||
public static int getBlockPageHttpStatus() { | |||||
String value = SentinelConfig.getConfig(BLOCK_PAGE_HTTP_STATUS); | |||||
if (StringUtil.isEmpty(value)) { | |||||
setBlockPageHttpStatus(HTTP_STATUS_TOO_MANY_REQUESTS); | |||||
return HTTP_STATUS_TOO_MANY_REQUESTS; | |||||
} | |||||
try { | |||||
return Integer.parseInt(SentinelConfig.getConfig(BLOCK_PAGE_HTTP_STATUS)); | |||||
} catch (NumberFormatException e) { | |||||
setBlockPageHttpStatus(HTTP_STATUS_TOO_MANY_REQUESTS); | |||||
} | |||||
return HTTP_STATUS_TOO_MANY_REQUESTS; | |||||
} | |||||
public static void setBlockPageHttpStatus(int httpStatus) { | |||||
SentinelConfig.setConfig(BLOCK_PAGE_HTTP_STATUS, String.valueOf(httpStatus)); | |||||
} | |||||
} | } |
@@ -27,6 +27,7 @@ import com.alibaba.csp.sentinel.util.StringUtil; | |||||
/** | /** | ||||
* Util class for web servlet filter. | * Util class for web servlet filter. | ||||
* | * | ||||
* @author zhaoyuguang | |||||
* @author youji.zj | * @author youji.zj | ||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@@ -65,7 +66,7 @@ public final class FilterUtil { | |||||
} | } | ||||
if (StringUtil.isBlank(WebServletConfig.getBlockPage())) { | if (StringUtil.isBlank(WebServletConfig.getBlockPage())) { | ||||
writeDefaultBlockedPage(response); | |||||
writeDefaultBlockedPage(response, WebServletConfig.getBlockPageHttpStatus()); | |||||
} else { | } else { | ||||
String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString(); | String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString(); | ||||
// Redirect to the customized block page. | // Redirect to the customized block page. | ||||
@@ -73,7 +74,8 @@ public final class FilterUtil { | |||||
} | } | ||||
} | } | ||||
private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException { | |||||
private static void writeDefaultBlockedPage(HttpServletResponse response, int httpStatus) throws IOException { | |||||
response.setStatus(httpStatus); | |||||
PrintWriter out = response.getWriter(); | PrintWriter out = response.getWriter(); | ||||
out.print(DEFAULT_BLOCK_MSG); | out.print(DEFAULT_BLOCK_MSG); | ||||
out.flush(); | out.flush(); | ||||
@@ -41,6 +41,7 @@ import org.junit.runner.RunWith; | |||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||||
import org.springframework.boot.test.context.SpringBootTest; | import org.springframework.boot.test.context.SpringBootTest; | ||||
import org.springframework.http.HttpStatus; | |||||
import org.springframework.http.MediaType; | import org.springframework.http.MediaType; | ||||
import org.springframework.test.context.junit4.SpringRunner; | import org.springframework.test.context.junit4.SpringRunner; | ||||
import org.springframework.test.web.servlet.MockMvc; | import org.springframework.test.web.servlet.MockMvc; | ||||
@@ -50,6 +51,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder | |||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | ||||
/** | /** | ||||
* @author zhaoyuguang | |||||
* @author Eric Zhao | * @author Eric Zhao | ||||
*/ | */ | ||||
@RunWith(SpringRunner.class) | @RunWith(SpringRunner.class) | ||||
@@ -111,11 +113,17 @@ public class CommonFilterTest { | |||||
private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception { | private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception { | ||||
configureRulesFor(url, 0); | configureRulesFor(url, 0); | ||||
// The request will be blocked and response is default block message. | // The request will be blocked and response is default block message. | ||||
WebServletConfig.setBlockPageHttpStatus(HttpStatus.OK.value()); | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | ||||
.andExpect(status().isOk()) | .andExpect(status().isOk()) | ||||
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | ||||
assertEquals(1, cn.blockQps(), 0.01); | assertEquals(1, cn.blockQps(), 0.01); | ||||
WebServletConfig.setBlockPageHttpStatus(HttpStatus.TOO_MANY_REQUESTS.value()); | |||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | |||||
.andExpect(status().isTooManyRequests()) | |||||
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | |||||
// Test for redirect. | // Test for redirect. | ||||
String redirectUrl = "http://some-location.com"; | String redirectUrl = "http://some-location.com"; | ||||
WebServletConfig.setBlockPage(redirectUrl); | WebServletConfig.setBlockPage(redirectUrl); | ||||
@@ -192,7 +200,7 @@ public class CommonFilterTest { | |||||
.andExpect(content().string(HELLO_STR)); | .andExpect(content().string(HELLO_STR)); | ||||
// This will be blocked. | // This will be blocked. | ||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN).header(headerName, limitOrigin)) | this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN).header(headerName, limitOrigin)) | ||||
.andExpect(status().isOk()) | |||||
.andExpect(status().isTooManyRequests()) | |||||
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | ||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | ||||
.andExpect(status().isOk()) | .andExpect(status().isOk()) | ||||
@@ -41,6 +41,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder | |||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; | ||||
/** | /** | ||||
* @author zhaoyuguang | |||||
* @author Roger Law | * @author Roger Law | ||||
*/ | */ | ||||
@RunWith(SpringRunner.class) | @RunWith(SpringRunner.class) | ||||
@@ -107,7 +108,7 @@ public class CommonFilterMethodTest { | |||||
configureRulesFor(GET + ":" + url, 0); | configureRulesFor(GET + ":" + url, 0); | ||||
// The request will be blocked and response is default block message. | // The request will be blocked and response is default block message. | ||||
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN)) | ||||
.andExpect(status().isOk()) | |||||
.andExpect(status().isTooManyRequests()) | |||||
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | .andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG)); | ||||
assertEquals(1, cnGet.blockQps(), 0.01); | assertEquals(1, cnGet.blockQps(), 0.01); | ||||