Support customized block page HTTP status in sentinel-web-servlet-adapter (#1112)

* 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.
This commit is contained in:
于玉桔 2019-11-06 10:42:50 +08:00 committed by Eric Zhao
parent cf5b955f12
commit a451d4bbac
4 changed files with 48 additions and 4 deletions

View File

@ -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.CommonTotalFilter;
import com.alibaba.csp.sentinel.config.SentinelConfig;
import com.alibaba.csp.sentinel.util.StringUtil;
/**
* @author zhaoyuguang
* @author leyou
*/
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_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
* {@link CommonTotalFilter} occurs.
@ -41,4 +47,31 @@ public class WebServletConfig {
public static void setBlockPage(String 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));
}
}

View File

@ -27,6 +27,7 @@ import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Util class for web servlet filter.
*
* @author zhaoyuguang
* @author youji.zj
* @author Eric Zhao
*/
@ -65,7 +66,7 @@ public final class FilterUtil {
}
if (StringUtil.isBlank(WebServletConfig.getBlockPage())) {
writeDefaultBlockedPage(response);
writeDefaultBlockedPage(response, WebServletConfig.getBlockPageHttpStatus());
} else {
String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString();
// 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();
out.print(DEFAULT_BLOCK_MSG);
out.flush();

View File

@ -41,6 +41,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
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.*;
/**
* @author zhaoyuguang
* @author Eric Zhao
*/
@RunWith(SpringRunner.class)
@ -111,11 +113,17 @@ public class CommonFilterTest {
private void testCommonBlockAndRedirectBlockPage(String url, ClusterNode cn) throws Exception {
configureRulesFor(url, 0);
// 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))
.andExpect(status().isOk())
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
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.
String redirectUrl = "http://some-location.com";
WebServletConfig.setBlockPage(redirectUrl);
@ -192,7 +200,7 @@ public class CommonFilterTest {
.andExpect(content().string(HELLO_STR));
// This will be blocked.
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));
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())

View File

@ -41,6 +41,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* @author zhaoyuguang
* @author Roger Law
*/
@RunWith(SpringRunner.class)
@ -107,7 +108,7 @@ public class CommonFilterMethodTest {
configureRulesFor(GET + ":" + url, 0);
// The request will be blocked and response is default block message.
this.mvc.perform(get(url).accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(status().isTooManyRequests())
.andExpect(content().string(FilterUtil.DEFAULT_BLOCK_MSG));
assertEquals(1, cnGet.blockQps(), 0.01);