/*
* Copyright 2015 the original author or authors.
* @https://github.com/scouter-project/scouter
*
* 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 scouter.xtra.http;
import scouter.agent.Configure;
import scouter.agent.counter.meter.MeterUsers;
import scouter.agent.netio.data.DataProxy;
import scouter.agent.proxy.IHttpTrace;
import scouter.agent.summary.EndUserAjaxData;
import scouter.agent.summary.EndUserErrorData;
import scouter.agent.summary.EndUserNavigationData;
import scouter.agent.summary.EndUserSummary;
import scouter.agent.trace.IProfileCollector;
import scouter.agent.trace.TraceContext;
import scouter.agent.trace.TraceContextManager;
import scouter.agent.trace.TraceMain;
import scouter.agent.trace.TransferMap;
import scouter.lang.conf.ConfObserver;
import scouter.lang.pack.XLogTypes;
import scouter.lang.step.MessageStep;
import scouter.util.CastUtil;
import scouter.util.CompareUtil;
import scouter.util.HashUtil;
import scouter.util.Hexa32;
import scouter.util.StringUtil;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import static scouter.agent.AgentCommonConstant.ASYNC_SERVLET_DISPATCHED_PREFIX;
import static scouter.agent.AgentCommonConstant.REQUEST_ATTRIBUTE_CALLER_TRANSFER_MAP;
import static scouter.agent.AgentCommonConstant.REQUEST_ATTRIBUTE_SELF_DISPATCHED;
public class HttpTrace implements IHttpTrace {
boolean remote_by_header;
boolean __ip_dummy_test;
String http_remote_ip_header_key;
public static String[] ipRandom = {"27.114.0.121", "58.3.128.121",
"101.53.64.121", "125.7.128.121", "202.68.224.121", "62.241.64.121", "86.63.224.121", "78.110.176.121",
"84.18.128.121", "95.142.176.121", "61.47.128.121", "110.76.32.121", "116.251.64.121", "123.150.0.121",
"125.254.128.121", "5.134.32.0", "5.134.32.121", "52.119.0.121", "154.0.128.121", "190.46.0.121"};
public HttpTrace() {
Configure conf = Configure.getInstance();
this.http_remote_ip_header_key = conf.trace_http_client_ip_header_key;
this.remote_by_header = !StringUtil.isEmpty(this.http_remote_ip_header_key);
this.__ip_dummy_test = conf.__ip_dummy_test;
ConfObserver.add(HttpTrace.class.getName(), new Runnable() {
public void run() {
String x = Configure.getInstance().trace_http_client_ip_header_key;
if (CompareUtil.equals(x, http_remote_ip_header_key) == false) {
remote_by_header = StringUtil.isEmpty(x) == false;
http_remote_ip_header_key = x;
}
}
});
}
public String getParameter(Object req, String key) {
HttpServletRequest request = (HttpServletRequest) req;
String ctype = request.getContentType();
if (ctype != null && ctype.startsWith("application/x-www-form-urlencoded"))
return null;
return request.getParameter(key);
}
public String getHeader(Object req, String key) {
HttpServletRequest request = (HttpServletRequest) req;
return request.getHeader(key);
}
public void start(TraceContext ctx, Object req, Object res) {
Configure conf = Configure.getInstance();
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
ctx.serviceName = getRequestURI(request);
ctx.serviceHash = HashUtil.hash(ctx.serviceName);
if (ctx.serviceHash == conf.getEndUserPerfEndpointHash()) {
ctx.isStaticContents = true;
processEndUserData(request);
return;
}
ctx.isStaticContents = TraceMain.isStaticContents(ctx.serviceName);
ctx.http_method = request.getMethod();
ctx.http_query = request.getQueryString();
ctx.http_content_type = request.getContentType();
ctx.remoteIp = getRemoteAddr(request);
TransferMap.ID transferId = (TransferMap.ID)request.getAttribute(REQUEST_ATTRIBUTE_CALLER_TRANSFER_MAP);
request.setAttribute(REQUEST_ATTRIBUTE_CALLER_TRANSFER_MAP, null);
// System.out.println("[scouter][http-start]transferId:thread: " + transferId + " ::: " + Thread.currentThread().getName());
// System.out.println("[scouter][http-start]url: " + ctx.serviceName);
if(transferId != null) {
if(transferId.gxid !=0) ctx.gxid = transferId.gxid;
if(transferId.callee !=0) ctx.txid = transferId.callee;
if(transferId.caller !=0) ctx.caller = transferId.caller;
ctx.xType = transferId.xType;
if(ctx.xType == XLogTypes.ASYNCSERVLET_DISPATCHED_SERVICE) {
TraceContext callerCtx = TraceContextManager.getDeferredContext(ctx.caller);
StringBuilder sb = new StringBuilder(ctx.serviceName.length()*3);
sb.append(ASYNC_SERVLET_DISPATCHED_PREFIX);
if (Boolean.TRUE.equals(request.getAttribute(REQUEST_ATTRIBUTE_SELF_DISPATCHED))) {
request.setAttribute(REQUEST_ATTRIBUTE_SELF_DISPATCHED, false);
sb.append("[self]");
if (callerCtx != null) sb.append(callerCtx.serviceName);
ctx.serviceName = sb.toString();
} else {
if (callerCtx != null) sb.append(callerCtx.serviceName).append(":/");
ctx.serviceName = sb.append(ctx.serviceName).toString();
}
}
}
try {
switch (conf.trace_user_mode) {
case 3:
ctx.userid = UseridUtil.getUseridFromHeader(request, response, conf.trace_user_session_key);
if (ctx.userid == 0 && ctx.remoteIp != null) {
ctx.userid = HashUtil.hash(ctx.remoteIp);
}
break;
case 2:
ctx.userid = UseridUtil.getUserid(request, response);
break;
case 1:
ctx.userid = UseridUtil.getUseridCustom(request, response, conf.trace_user_session_key);
if (ctx.userid == 0 && ctx.remoteIp != null) {
ctx.userid = HashUtil.hash(ctx.remoteIp);
}
break;
default:
if (ctx.remoteIp != null) {
ctx.userid = HashUtil.hash(ctx.remoteIp);
}
break;
}
MeterUsers.add(ctx.userid);
} catch (Throwable e) {
// ignore
}
String referer = request.getHeader("Referer");
if (referer != null) {
ctx.referer = DataProxy.sendReferer(referer);
}
String userAgent = request.getHeader("User-Agent");
if (userAgent != null) {
ctx.userAgent = DataProxy.sendUserAgent(userAgent);
ctx.userAgentString = userAgent;
}
dump(ctx.profile, request, ctx);
if (conf.trace_interservice_enabled && transferId == null) {
try {
String gxid = request.getHeader(conf._trace_interservice_gxid_header_key);
if (gxid != null) {
ctx.gxid = Hexa32.toLong32(gxid);
}
String txid = request.getHeader(conf._trace_interservice_callee_header_key);
if (txid != null) {
ctx.txid = Hexa32.toLong32(txid);
ctx.is_child_tx = true;
}
String caller = request.getHeader(conf._trace_interservice_caller_header_key);
if (caller != null) {
ctx.caller = Hexa32.toLong32(caller);
ctx.is_child_tx = true;
}
} catch (Throwable t) {
}
}
if (conf.trace_response_gxid_enabled && !ctx.isStaticContents) {
try {
if (ctx.gxid == 0)
ctx.gxid = ctx.txid;
String resGxId = Hexa32.toString32(ctx.gxid) + ":" + ctx.startTime;
response.setHeader(conf._trace_interservice_gxid_header_key, resGxId);
Cookie c = new Cookie(conf._trace_interservice_gxid_header_key, resGxId);
response.addCookie(c);
} catch (Throwable t) {
}
}
if (conf.trace_webserver_enabled) {
try {
ctx.web_name = request.getHeader(conf.trace_webserver_name_header_key);
String web_time = request.getHeader(conf.trace_webserver_time_header_key);
if (web_time != null) {
int x = web_time.indexOf("t=");
if (x >= 0) {
web_time = web_time.substring(x + 2);
x = web_time.indexOf(' ');
if (x > 0) {
web_time = web_time.substring(0, x);
}
ctx.web_time = (int) (System.currentTimeMillis() - (Long.parseLong(web_time) / 1000));
}
}
} catch (Throwable t) {
}
}
}
private void processEndUserData(HttpServletRequest request) {
EndUserNavigationData nav;
EndUserErrorData err;
EndUserAjaxData ajax;
if ("err".equals(request.getParameter("p"))) {
EndUserErrorData data = new EndUserErrorData();
data.count = 1;
data.stacktrace = DataProxy.sendError(StringUtil.nullToEmpty(request.getParameter("stacktrace")));
data.userAgent = DataProxy.sendUserAgent(StringUtil.nullToEmpty(request.getParameter("userAgent")));
data.host = DataProxy.sendServiceName(StringUtil.nullToEmpty(request.getParameter("host")));
data.uri = DataProxy.sendServiceName(StringUtil.nullToEmpty(request.getParameter("uri")));
data.message = DataProxy.sendError(StringUtil.nullToEmpty(request.getParameter("message")));
data.name = DataProxy.sendError(StringUtil.nullToEmpty(request.getParameter("name")));
data.file = DataProxy.sendServiceName(StringUtil.nullToEmpty(request.getParameter("file")));
data.lineNumber = CastUtil.cint(request.getParameter("lineNumber"));
data.columnNumber = CastUtil.cint(request.getParameter("columnNumber"));
//Logger.println("@ input error data -> print");
//Logger.println(data);
EndUserSummary.getInstance().process(data);
} else if ("nav".equals(request.getParameter("p"))) {
} else if ("ax".equals(request.getParameter("p"))) {
}
//EndUserSummary.getInstance().process(p);
}
private String getRequestURI(HttpServletRequest request) {
String uri = request.getRequestURI();
if (uri == null)
return "no-url";
int x = uri.indexOf(';');
if (x > 0)
return uri.substring(0, x);
else
return uri;
}
private String getRemoteAddr(HttpServletRequest request) {
try {
//For Testing
if (__ip_dummy_test) {
return getRandomIp();
}
if (remote_by_header) {
String remoteIp = request.getHeader(http_remote_ip_header_key);
int commaPos = remoteIp.indexOf(',');
if (remoteIp != null && commaPos > -1) {
remoteIp = remoteIp.substring(0, commaPos);
}
return remoteIp;
} else {
return request.getRemoteAddr();
}
} catch (Throwable t) {
remote_by_header = false;
return "0.0.0.0";
}
}
private String getRandomIp() {
int len = ipRandom.length;
int randomNum = (int) (Math.random() * (len-1));
return ipRandom[randomNum];
}
public void end(TraceContext ctx, Object req, Object res) {
// HttpServletRequest request = (HttpServletRequest)req;
// HttpServletResponse response = (HttpServletResponse)res;
}
private static void dump(IProfileCollector p, HttpServletRequest request, TraceContext ctx) {
Configure conf = Configure.getInstance();
if (conf.profile_http_querystring_enabled) {
String msg = request.getMethod() + " ?" + StringUtil.trimToEmpty(request.getQueryString());
MessageStep step = new MessageStep(msg);
step.start_time = (int) (System.currentTimeMillis() - ctx.startTime);
p.add(step);
}
if (conf.profile_http_header_enabled) {
if (conf.profile_http_header_url_prefix == null || ctx.serviceName.indexOf(conf.profile_http_header_url_prefix) >= 0) {
Enumeration en = request.getHeaderNames();
if (en != null) {
int start_time = (int) (System.currentTimeMillis() - ctx.startTime);
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
if (conf._profile_http_header_keys != null
&& conf._profile_http_header_keys.size() > 0
&& !conf._profile_http_header_keys.contains(key.toUpperCase())) {
continue;
}
String value = new StringBuilder().append("header: ").append(key).append("=")
.append(StringUtil.limiting(request.getHeader(key), 1024)).toString();
MessageStep step = new MessageStep(value);
step.start_time = start_time;
p.add(step);
}
}
}
}
if (conf.profile_http_parameter_enabled) {
if (conf.profile_http_parameter_url_prefix == null || ctx.serviceName.indexOf(conf.profile_http_parameter_url_prefix) >= 0) {
String ctype = request.getContentType();
if (ctype != null && ctype.indexOf("multipart") >= 0)
return;
Enumeration en = request.getParameterNames();
if (en != null) {
int start_time = (int) (System.currentTimeMillis() - ctx.startTime);
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
String value = new StringBuilder().append("parameter: ").append(key).append("=")
.append(StringUtil.limiting(request.getParameter(key), 1024)).toString();
MessageStep step = new MessageStep(value);
step.start_time = start_time;
// step.start_cpu = (int) (SysJMX.getCurrentThreadCPU()
// - ctx.startCpu);
p.add(step);
}
}
}
}
}
public void rejectText(Object res, String text) {
HttpServletResponse response = (HttpServletResponse) res;
try {
PrintWriter pw = response.getWriter();
pw.println(text);
} catch (IOException e) {
}
}
public void rejectUrl(Object res, String url) {
HttpServletResponse response = (HttpServletResponse) res;
try {
response.sendRedirect(url);
} catch (IOException e) {
}
}
public void addAsyncContextListener(Object ac) {
return;
}
public TraceContext getTraceContextFromAsyncContext(Object oAsyncContext) {
return null;
}
public void setDispatchTransferMap(Object oAsyncContext, long gxid, long caller, long callee, byte xType) {
return;
}
public void setSelfDispatch(Object oAsyncContext, boolean self) {
return;
}
public boolean isSelfDispatch(Object oAsyncContext) {
return false;
}
}