package com.yirendai.infra.cicada.capture;
import com.yirendai.infra.cicada.entity.trace.Endpoint;
import com.yirendai.infra.cicada.entity.trace.Span;
import com.yirendai.infra.cicada.utils.IpUtils;
import com.yirendai.infra.cicada.utils.SpringContextUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebFilter(filterName = "cicadaHttpFilter", urlPatterns = {"*"},
initParams = {@WebInitParam(name = "uriConditions", value = "*", description = "匹配的uri"),
@WebInitParam(name = "uriNotMatchConditions", description = "不配置的uri",
value = "**.html,**.htm,**.js,**.gif,**.css,**.rtt,**.dsp,**.jpg,**.png,**.mp4,"
+ "**.flv,**.doc,**.docx,**.xls,**.xlsx,**.ppt,**.pptx,**.pdf,**.ico")})
@SuppressWarnings("PMD.CyclomaticComplexity")
public class HttpFilter implements Filter {
private static final Logger LOG = LoggerFactory.getLogger(HttpFilter.class);
// 需要拦截的正则表达式
private final List<String> uriConditionRegexList = new ArrayList<String>();
// 不需要拦截的正则表达式
private final List<String> uriNotMatchConditionsRegexList = new ArrayList<String>();
private final Tracer tracer = Tracer.getInstance();
private static final String DEFAULT_APP_NAME = "http";
public void destroy() {
// do nothing
}
@SuppressWarnings("PMD.NPathComplexity")
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
LOG.error("SessionUserFilter only supports HTTP requests");
throw new ServletException("SessionUserFilter only supports HTTP requests");
}
final long start = System.currentTimeMillis();
final HttpServletRequest req = (HttpServletRequest) request;
final HttpServletResponse res = (HttpServletResponse) response;
final String uri = req.getRequestURI();
if (req.getCharacterEncoding() == null || req.getCharacterEncoding().equalsIgnoreCase("ISO-8859-1")) {
req.setCharacterEncoding("utf-8");
}
if (res.getCharacterEncoding() == null || res.getCharacterEncoding().equalsIgnoreCase("ISO-8859-1")) {
res.setCharacterEncoding("utf-8");
}
// 验证URL
if (needFilter(uri)) {
final String localIp = IpUtils.getRealIpWithStaticCache();
final int localPort = req.getLocalPort();
final Endpoint endpoint = new Endpoint(localIp, localPort);
final String url = req.getRequestURI();
final String httpMethod = req.getMethod();
final String appName = SpringContextUtil.getAppName(DEFAULT_APP_NAME);
final Span parentSpan = tracer.getParentSpan();
Span span;
if (parentSpan == null) {
span = tracer.newSpan(appName, url, httpMethod);
} else {
span = tracer.newSpan(appName, url, httpMethod, parentSpan);
}
tracer.serverReceiveRecord(span, endpoint, start);
try {
chain.doFilter(request, response);
} catch (IOException ex) {
span.addException(url, httpMethod, ex, endpoint);
throw ex;
} catch (ServletException ex) {
span.addException(url, httpMethod, ex, endpoint);
throw ex;
} finally {
final long end = System.currentTimeMillis();
tracer.serverSendRecord(span, endpoint, end);
}
} else {
// URL拦截未通过,直接执行doFilter
chain.doFilter(request, response);
}
}
public void init(final FilterConfig config) throws ServletException {
final String uriConditions = config.getInitParameter("uriConditions");
final String uriNotMatchConditions = config.getInitParameter("uriNotMatchConditions");
fillList(uriConditionRegexList, uriConditions);
fillList(uriNotMatchConditionsRegexList, uriNotMatchConditions);
}
private void fillList(final List<String> targetList, final String configPara) {
if (StringUtils.isBlank(configPara)) {
return;
}
final String[] conditionSplits = configPara.split("[,|;]");
for (String condition : conditionSplits) {
condition = condition.trim();
if (StringUtils.isBlank(condition)) {
continue;
}
targetList.add(toRegexString(condition));
}
}
@SuppressWarnings({"PMD.AvoidReassigningParameters", "PMD.AvoidPrefixingMethodParameters"})
public String toRegexString(String inStr) {
String ret = inStr;
if (StringUtils.isBlank(inStr)) {
ret = inStr;
} else {
if ("*".equals(inStr)) {
inStr = "**";
}
if (inStr.length() > 1 && inStr.charAt(0) == '*') {
ret = inStr.replaceAll("\\*", "\\[\\\\S\\| \\]\\*") + "$";
} else {
ret = inStr.replaceAll("\\*\\*", "\\[\\\\S\\| \\]\\ACCECC").replaceAll("\\*", "\\[\\\\w\\| |\\%\\|\\\\.\\]\\*")
.replaceAll("ACCECC", "\\*") + "$";
}
}
return ret;
}
public boolean canMatch(final String uri, final List<String> conditions) {
boolean ret = false;
if (CollectionUtils.isNotEmpty(conditions)) {
for (int i = 0; i < conditions.size(); ++i) {
final String condition = conditions.get(i);
if (uri.matches(condition)) {
ret = true;
}
}
}
return ret;
}
public boolean needFilter(final String uri) {
boolean ret = false;
if (StringUtils.isNotBlank(uri)) {
if (canMatch(uri, this.uriNotMatchConditionsRegexList)) {
ret = false;
} else if (canMatch(uri, this.uriConditionRegexList)) {
ret = true;
}
}
return ret;
}
}