- admin can toggle verbose logging of all requests on and off

- admin page shows user hostname
- servlets log request and response if logging is enabled
This commit is contained in:
Andy Janata 2012-01-29 17:13:31 -08:00
parent 5e95299801
commit a26221c2bb
8 changed files with 106 additions and 30 deletions

View File

@ -13,6 +13,19 @@ String remoteAddr = request.getRemoteAddr();
if (!(remoteAddr.equals("0:0:0:0:0:0:0:1") || remoteAddr.equals("127.0.0.1"))) { if (!(remoteAddr.equals("0:0:0:0:0:0:0:1") || remoteAddr.equals("127.0.0.1"))) {
response.sendError(403, "Access is restricted to known hosts"); response.sendError(403, "Access is restricted to known hosts");
} }
ServletContext servletContext = pageContext.getServletContext();
// process verbose toggle
String verboseParam = request.getParameter("verbose");
if (verboseParam != null) {
if (verboseParam.equals("on")) {
servletContext.setAttribute(StartupUtils.VERBOSE_DEBUG, Boolean.TRUE);
} else {
servletContext.setAttribute(StartupUtils.VERBOSE_DEBUG, Boolean.FALSE);
}
}
%> %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@ -33,7 +46,6 @@ th, td {
<body> <body>
<% <%
ServletContext servletContext = pageContext.getServletContext();
Injector injector = (Injector) servletContext.getAttribute(StartupUtils.INJECTOR); Injector injector = (Injector) servletContext.getAttribute(StartupUtils.INJECTOR);
%> %>
@ -83,17 +95,28 @@ Collection<User> users = connectedUsers.getUsers();
<table> <table>
<tr> <tr>
<th>Username</th> <th>Username</th>
<th>Host</th>
</tr> </tr>
<% <%
for (User u : users) { for (User u : users) {
%> %>
<tr> <tr>
<td><% out.print(u.getNickname()); %></td> <td><% out.print(u.getNickname()); %></td>
<td><% out.print(u.getHostName()); %></td>
</tr> </tr>
<% <%
} }
%> %>
</table> </table>
<%
Boolean verboseDebugObj = (Boolean) servletContext.getAttribute(StartupUtils.VERBOSE_DEBUG);
boolean verboseDebug = verboseDebugObj != null ? verboseDebugObj.booleanValue() : false;
%>
<p>
Verbose logging is currently <strong><% out.print(verboseDebug ? "ON" : "OFF"); %></strong>.
<a href="?verbose=on">Turn on.</a> <a href="?verbose=off">Turn off.</a>
</p>
</body> </body>
</html> </html>

View File

@ -14,6 +14,8 @@ import net.socialgamer.cah.Constants.AjaxRequest;
import net.socialgamer.cah.Constants.AjaxResponse; import net.socialgamer.cah.Constants.AjaxResponse;
import net.socialgamer.cah.Constants.ErrorCode; import net.socialgamer.cah.Constants.ErrorCode;
import net.socialgamer.cah.Constants.ReturnableData; import net.socialgamer.cah.Constants.ReturnableData;
import net.socialgamer.cah.Constants.SessionAttribute;
import net.socialgamer.cah.data.User;
import net.socialgamer.cah.handlers.Handler; import net.socialgamer.cah.handlers.Handler;
import net.socialgamer.cah.handlers.Handlers; import net.socialgamer.cah.handlers.Handlers;
@ -36,19 +38,20 @@ public class AjaxServlet extends CahServlet {
final HttpServletResponse response, final HttpSession hSession) throws ServletException, final HttpServletResponse response, final HttpSession hSession) throws ServletException,
IOException { IOException {
final PrintWriter out = response.getWriter(); final PrintWriter out = response.getWriter();
final User user = (User) hSession.getAttribute(SessionAttribute.USER);
int serial = -1; int serial = -1;
if (request.getParameter(AjaxRequest.SERIAL.toString()) != null) { if (request.getParameter(AjaxRequest.SERIAL.toString()) != null) {
try { try {
serial = Integer.parseInt(request.getParameter(AjaxRequest.SERIAL.toString())); serial = Integer.parseInt(request.getParameter(AjaxRequest.SERIAL.toString()));
} catch (final NumberFormatException nfe) { } catch (final NumberFormatException nfe) {
returnError(out, ErrorCode.BAD_REQUEST); returnError(user, out, ErrorCode.BAD_REQUEST);
return; return;
} }
} }
final String op = request.getParameter(AjaxRequest.OP.toString()); final String op = request.getParameter(AjaxRequest.OP.toString());
if (op == null || op.equals("")) { if (op == null || op.equals("")) {
returnError(out, ErrorCode.OP_NOT_SPECIFIED, serial); returnError(user, out, ErrorCode.OP_NOT_SPECIFIED, serial);
return; return;
} }
@ -58,12 +61,12 @@ public class AjaxServlet extends CahServlet {
} catch (final Exception e) { } catch (final Exception e) {
System.err.println("Exception creating handler for " + op); System.err.println("Exception creating handler for " + op);
e.printStackTrace(System.err); e.printStackTrace(System.err);
returnError(out, ErrorCode.BAD_OP, serial); returnError(user, out, ErrorCode.BAD_OP, serial);
return; return;
} }
final Map<ReturnableData, Object> data = handler.handle(new RequestWrapper(request), hSession); final Map<ReturnableData, Object> data = handler.handle(new RequestWrapper(request), hSession);
data.put(AjaxResponse.SERIAL, serial); data.put(AjaxResponse.SERIAL, serial);
returnData(out, data); returnData(user, out, data);
return; return;
} }

View File

@ -2,6 +2,8 @@ package net.socialgamer.cah;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -23,6 +25,7 @@ import org.json.simple.JSONObject;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.sun.istack.internal.Nullable;
/** /**
@ -32,13 +35,7 @@ import com.google.inject.Injector;
public abstract class CahServlet extends HttpServlet { public abstract class CahServlet extends HttpServlet {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** private final boolean debugLog = false;
* @see HttpServlet#HttpServlet()
*/
public CahServlet() {
super();
// TODO Auto-generated constructor stub
}
/** /**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
@ -50,6 +47,21 @@ public abstract class CahServlet extends HttpServlet {
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
final HttpSession hSession = request.getSession(true); final HttpSession hSession = request.getSession(true);
final User user = (User) hSession.getAttribute(SessionAttribute.USER);
if (verboseDebug()) {
// TODO if we have any sort of authentication later, we need to make sure to not log passwords!
// I could use getParameterMap, but that returns an array, and getting pretty strings out of
// array values is a lot of work.
final Map<String, Object> params = new HashMap<String, Object>();
final Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
final String name = paramNames.nextElement();
params.put(name, request.getParameter(name));
}
log(user, "Request: " + JSONValue.toJSONString(params));
}
final String op = request.getParameter(AjaxRequest.OP.toString()); final String op = request.getParameter(AjaxRequest.OP.toString());
final boolean skipSessionUserCheck = op != null final boolean skipSessionUserCheck = op != null
&& (op.equals(AjaxOperation.REGISTER.toString()) && (op.equals(AjaxOperation.REGISTER.toString())
@ -57,24 +69,31 @@ public abstract class CahServlet extends HttpServlet {
if (hSession.isNew()) { if (hSession.isNew()) {
// they should have gotten a session from the index page. // they should have gotten a session from the index page.
// they probably don't have cookies on. // they probably don't have cookies on.
returnError(response.getWriter(), ErrorCode.NO_SESSION); returnError(user, response.getWriter(), ErrorCode.NO_SESSION);
} else if (!skipSessionUserCheck && hSession.getAttribute(SessionAttribute.USER) == null) { } else if (!skipSessionUserCheck && hSession.getAttribute(SessionAttribute.USER) == null) {
returnError(response.getWriter(), ErrorCode.NOT_REGISTERED); returnError(user, response.getWriter(), ErrorCode.NOT_REGISTERED);
} else if (hSession.getAttribute(SessionAttribute.USER) != null } else if (user != null && !user.isValid()) {
&& !(((User) hSession.getAttribute(SessionAttribute.USER)).isValid())) {
// user probably pinged out // user probably pinged out
hSession.invalidate(); hSession.invalidate();
returnError(response.getWriter(), ErrorCode.SESSION_EXPIRED); returnError(user, response.getWriter(), ErrorCode.SESSION_EXPIRED);
} else { } else {
try { try {
handleRequest(request, response, hSession); handleRequest(request, response, hSession);
} catch (final AssertionError ae) { } catch (final AssertionError ae) {
log("Assertion failed", ae);
getServletContext().log(ae.toString()); getServletContext().log(ae.toString());
ae.printStackTrace(); ae.printStackTrace();
} }
} }
} }
private boolean verboseDebug() {
final Boolean verboseDebugObj = (Boolean) getServletContext().getAttribute(
StartupUtils.VERBOSE_DEBUG);
final boolean verboseDebug = verboseDebugObj != null ? verboseDebugObj.booleanValue() : false;
return verboseDebug;
}
/** /**
* Handles a request from a CAH client. A session is guaranteed to exist at this point. * Handles a request from a CAH client. A session is guaranteed to exist at this point.
* *
@ -96,8 +115,9 @@ public abstract class CahServlet extends HttpServlet {
* @param code * @param code
* Error code that the js code knows how to handle. * Error code that the js code knows how to handle.
*/ */
protected void returnError(final PrintWriter writer, final ErrorCode code) { protected void returnError(@Nullable final User user, final PrintWriter writer,
returnError(writer, code, -1); final ErrorCode code) {
returnError(user, writer, code, -1);
} }
/** /**
@ -107,7 +127,9 @@ public abstract class CahServlet extends HttpServlet {
* @param serial * @param serial
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void returnError(final PrintWriter writer, final ErrorCode code, final int serial) { protected void returnError(@Nullable final User user, final PrintWriter writer,
final ErrorCode code,
final int serial) {
final JSONObject ret = new JSONObject(); final JSONObject ret = new JSONObject();
ret.put(AjaxResponse.ERROR, Boolean.TRUE); ret.put(AjaxResponse.ERROR, Boolean.TRUE);
ret.put(AjaxResponse.ERROR_CODE, code.toString()); ret.put(AjaxResponse.ERROR_CODE, code.toString());
@ -122,8 +144,9 @@ public abstract class CahServlet extends HttpServlet {
* @param data * @param data
* Key-value data to return as the response. * Key-value data to return as the response.
*/ */
protected void returnData(final PrintWriter writer, final Map<ReturnableData, Object> data) { protected void returnData(@Nullable final User user, final PrintWriter writer,
returnObject(writer, data); final Map<ReturnableData, Object> data) {
returnObject(user, writer, data);
} }
/** /**
@ -134,18 +157,28 @@ public abstract class CahServlet extends HttpServlet {
* @param data_list * @param data_list
* List of key-value data to return as the response. * List of key-value data to return as the response.
*/ */
protected void returnArray(final PrintWriter writer, protected void returnArray(@Nullable final User user, final PrintWriter writer,
final List<Map<ReturnableData, Object>> data_list) { final List<Map<ReturnableData, Object>> data_list) {
returnObject(writer, data_list); returnObject(user, writer, data_list);
} }
private void returnObject(final PrintWriter writer, final Object object) { private void returnObject(@Nullable final User user, final PrintWriter writer, final Object object) {
final String ret = JSONValue.toJSONString(object); final String ret = JSONValue.toJSONString(object);
writer.println(ret); writer.println(ret);
// System.out.println(">>>> " + ret + " <<<<"); if (verboseDebug()) {
log(user, "Response: " + ret);
}
} }
protected Injector getInjector() { protected Injector getInjector() {
return (Injector) getServletContext().getAttribute(StartupUtils.INJECTOR); return (Injector) getServletContext().getAttribute(StartupUtils.INJECTOR);
} }
protected void log(@Nullable final User user, final String message) {
String userStr = "unknown user";
if (user != null) {
userStr = user.getNickname();
}
log("For " + userStr + ": " + message);
}
} }

View File

@ -82,7 +82,7 @@ public class LongPollServlet extends CahServlet {
for (final QueuedMessage qm : msgs) { for (final QueuedMessage qm : msgs) {
data.add(qm.getData()); data.add(qm.getData());
} }
returnArray(out, data); returnArray(user, out, data);
return; return;
} }
} }
@ -90,6 +90,6 @@ public class LongPollServlet extends CahServlet {
final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>(); final Map<ReturnableData, Object> data = new HashMap<ReturnableData, Object>();
data.put(LongPollResponse.EVENT, LongPollEvent.NOOP.toString()); data.put(LongPollResponse.EVENT, LongPollEvent.NOOP.toString());
data.put(LongPollResponse.TIMESTAMP, System.currentTimeMillis()); data.put(LongPollResponse.TIMESTAMP, System.currentTimeMillis());
returnData(out, data); returnData(user, out, data);
} }
} }

View File

@ -22,4 +22,12 @@ public class RequestWrapper {
public String getParameter(final AjaxRequest parameter) { public String getParameter(final AjaxRequest parameter) {
return request.getParameter(parameter.toString()); return request.getParameter(parameter.toString());
} }
public String getRemoteAddr() {
return request.getRemoteAddr();
}
public String getRemoteHost() {
return request.getRemoteHost();
}
} }

View File

@ -23,6 +23,8 @@ public class StartupUtils extends GuiceServletContextListener {
public static final String DATE_NAME = "started_at"; public static final String DATE_NAME = "started_at";
public static final String VERBOSE_DEBUG = "verbose_debug";
private Date serverStarted; private Date serverStarted;
@Override @Override

View File

@ -17,13 +17,16 @@ public class User {
private Game currentGame; private Game currentGame;
private final String hostName;
/** /**
* Reset when this user object is no longer valid, most likely because it pinged out. * Reset when this user object is no longer valid, most likely because it pinged out.
*/ */
private boolean valid = true; private boolean valid = true;
public User(final String nickname) { public User(final String nickname, final String hostName) {
this.nickname = nickname; this.nickname = nickname;
this.hostName = hostName;
queuedMessages = new PriorityBlockingQueue<QueuedMessage>(); queuedMessages = new PriorityBlockingQueue<QueuedMessage>();
} }
@ -79,6 +82,10 @@ public class User {
return nickname; return nickname;
} }
public String getHostName() {
return hostName;
}
@Override @Override
public String toString() { public String toString() {
return getNickname(); return getNickname();

View File

@ -52,7 +52,7 @@ public class RegisterHandler extends Handler {
} else if (users.hasUser(nick)) { } else if (users.hasUser(nick)) {
return error(ErrorCode.NICK_IN_USE); return error(ErrorCode.NICK_IN_USE);
} else { } else {
final User user = new User(nick); final User user = new User(nick, request.getRemoteHost());
users.newUser(user); users.newUser(user);
session.setAttribute(SessionAttribute.USER, user); session.setAttribute(SessionAttribute.USER, user);