Communicate the client's timezone to the server during the authentication
process and keep that information around in case the server ever wants to know. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4356 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
|
||||
package com.threerings.presents.net;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class AuthRequest extends UpstreamMessage
|
||||
{
|
||||
/**
|
||||
@@ -39,6 +41,7 @@ public class AuthRequest extends UpstreamMessage
|
||||
{
|
||||
_creds = creds;
|
||||
_version = version;
|
||||
_zone = TimeZone.getDefault().getID();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,6 +61,14 @@ public class AuthRequest extends UpstreamMessage
|
||||
return _version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timezone in which this client is operating.
|
||||
*/
|
||||
public TimeZone getTimeZone ()
|
||||
{
|
||||
return TimeZone.getTimeZone(_zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this instance.
|
||||
*/
|
||||
@@ -72,4 +83,7 @@ public class AuthRequest extends UpstreamMessage
|
||||
|
||||
/** The version information associated with the client code. */
|
||||
protected String _version;
|
||||
|
||||
/** The timezone in which this client is operating. */
|
||||
protected String _zone;
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ public class ClientManager
|
||||
", conn=" + conn + "].");
|
||||
// create a new client and stick'em in the table
|
||||
client = _factory.createClient(req);
|
||||
client.startSession(this, creds, conn, rsp.authdata);
|
||||
client.startSession(this, req, conn, rsp.authdata);
|
||||
|
||||
// map their client instance
|
||||
_usermap.put(username, client);
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.Throttle;
|
||||
@@ -39,6 +40,7 @@ import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.ProxySubscriber;
|
||||
|
||||
import com.threerings.presents.net.AuthRequest;
|
||||
import com.threerings.presents.net.BootstrapData;
|
||||
import com.threerings.presents.net.BootstrapNotification;
|
||||
import com.threerings.presents.net.Credentials;
|
||||
@@ -105,7 +107,15 @@ public class PresentsClient
|
||||
*/
|
||||
public Credentials getCredentials ()
|
||||
{
|
||||
return _creds;
|
||||
return _areq.getCredentials();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time zone in which this client is operating.
|
||||
*/
|
||||
public TimeZone getTimeZone ()
|
||||
{
|
||||
return _areq.getTimeZone();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,25 +279,6 @@ public class PresentsClient
|
||||
_cmgr.resolveClientObject(username, clr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when {@link #setUsername} has been called and the new client
|
||||
* object is about to be applied to this client. The old client object
|
||||
* will not yet have been destroyed, so any final events can be sent
|
||||
* along prior to the new object being put into effect.
|
||||
*/
|
||||
protected void clientObjectWillChange (
|
||||
ClientObject oldClobj, ClientObject newClobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the new client object has been committed to this
|
||||
* client due to a call to {@link #setUsername}.
|
||||
*/
|
||||
protected void clientObjectDidChange (ClientObject newClobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client object that is associated with this client.
|
||||
*/
|
||||
@@ -297,38 +288,89 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this client instance with the specified username,
|
||||
* connection instance and client object and begins a client session.
|
||||
* Forcibly terminates a client's session. This must be called from
|
||||
* the dobjmgr thread.
|
||||
*/
|
||||
protected void startSession (
|
||||
ClientManager cmgr, Credentials creds, Connection conn, Object authdata)
|
||||
public void endSession ()
|
||||
{
|
||||
_cmgr = cmgr;
|
||||
_creds = creds;
|
||||
_authdata = authdata;
|
||||
setConnection(conn);
|
||||
// queue up a request for our connection to be closed (if we have
|
||||
// a connection, that is)
|
||||
Connection conn = getConnection();
|
||||
if (conn != null) {
|
||||
// go ahead and clear out our connection now to prevent
|
||||
// funniness
|
||||
setConnection(null);
|
||||
// have the connection manager close our connection when it is
|
||||
// next convenient
|
||||
PresentsServer.conmgr.closeConnection(conn);
|
||||
}
|
||||
|
||||
// obtain our starting username
|
||||
assignStartingUsername();
|
||||
// if we don't have a client object, we failed to resolve in the
|
||||
// first place, in which case we have to cope as best we can
|
||||
if (_clobj != null) {
|
||||
// and clean up after ourselves
|
||||
try {
|
||||
sessionDidEnd();
|
||||
} catch (Exception e) {
|
||||
Log.warning("Choked in sessionDidEnd [client=" + this + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
|
||||
// resolve our client object before we get fully underway
|
||||
cmgr.resolveClientObject(_username, this);
|
||||
// release (and destroy) our client object
|
||||
_cmgr.releaseClientObject(_username);
|
||||
}
|
||||
|
||||
// make a note of our session start time
|
||||
_sessionStamp = System.currentTimeMillis();
|
||||
// let the client manager know that we're audi 5000
|
||||
_cmgr.clientSessionDidEnd(this);
|
||||
|
||||
// clear out the client object so that we know the session is over
|
||||
_clobj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is factored out to allow derived classes to use a different
|
||||
* starting username than the one supplied in the user's credentials.
|
||||
* Generally one only wants to munge the starting username if the user
|
||||
* will subsequently choose a "screen name" and it is desirable to
|
||||
* avoid collision between the authentication user namespace and the
|
||||
* screen namespace.
|
||||
* This is called when the server is shut down in the middle of a
|
||||
* client session. In this circumstance, {@link #endSession} will
|
||||
* <em>not</em> be called and so any persistent data that might
|
||||
* normally be flushed at the end of a client's session should likely
|
||||
* be flushed here.
|
||||
*/
|
||||
protected void assignStartingUsername ()
|
||||
public void shutdown ()
|
||||
{
|
||||
_username = _creds.getUsername();
|
||||
// if the client is connected, we need to fake the computation of
|
||||
// their final connect time because we won't be closing their
|
||||
// socket normally
|
||||
if (getConnection() != null) {
|
||||
long now = System.currentTimeMillis();
|
||||
_connectTime += ((now - _networkStamp) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a note that this client is subscribed to this object so that
|
||||
* we can clean up after ourselves if and when the client goes
|
||||
* away. This is called by the client internals and needn't be called
|
||||
* by code outside the client.
|
||||
*/
|
||||
public synchronized void mapSubscrip (DObject object)
|
||||
{
|
||||
_subscrips.put(object.getOid(), object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a note that this client is no longer subscribed to this
|
||||
* object. The subscription map is used to clean up after the client
|
||||
* when it goes away. This is called by the client internals and
|
||||
* needn't be called by code outside the client.
|
||||
*/
|
||||
public synchronized void unmapSubscrip (int oid)
|
||||
{
|
||||
DObject object = _subscrips.remove(oid);
|
||||
if (object != null) {
|
||||
object.removeSubscriber(this);
|
||||
} else {
|
||||
Log.warning("Requested to unmap non-existent subscription " +
|
||||
"[oid=" + oid + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
@@ -356,6 +398,123 @@ public class PresentsClient
|
||||
endSession();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void handleMessage (UpstreamMessage message)
|
||||
{
|
||||
_messagesIn++; // count 'em up!
|
||||
|
||||
// if the client has been getting crazy with the cheeze whiz,
|
||||
// stick a fork in them; the first time through we end our
|
||||
// session, subsequently _throttle is null and we just drop any
|
||||
// messages that come in until we've fully shutdown
|
||||
if (_throttle == null) {
|
||||
// Log.info("Dropping message from force-quit client " +
|
||||
// "[conn=" + _conn +
|
||||
// ", msg=" + message + "].");
|
||||
return;
|
||||
} else if (_throttle.throttleOp(message.received)) {
|
||||
Log.warning("Client sent more than 100 messages in 10 seconds, " +
|
||||
"forcing disconnect " + this + ".");
|
||||
safeEndSession();
|
||||
_throttle = null;
|
||||
}
|
||||
|
||||
// we dispatch to a message dispatcher that is specialized for the
|
||||
// particular class of message that we received
|
||||
MessageDispatcher disp = _disps.get(message.getClass());
|
||||
if (disp == null) {
|
||||
Log.warning("No dispatcher for message [msg=" + message + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise pass the message on to the dispatcher
|
||||
disp.dispatch(this, message);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void objectAvailable (DObject object)
|
||||
{
|
||||
if (postMessage(new ObjectResponse<DObject>(object))) {
|
||||
// make a note of this new subscription
|
||||
mapSubscrip(object);
|
||||
} else {
|
||||
// if we failed to send the object response, unsubscribe
|
||||
object.removeSubscriber(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (int oid, ObjectAccessException cause)
|
||||
{
|
||||
postMessage(new FailureResponse(oid));
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void eventReceived (DEvent event)
|
||||
{
|
||||
if (event instanceof PresentsDObjectMgr.AccessObjectEvent) {
|
||||
Log.warning("Ignoring event that shouldn't be forwarded " +
|
||||
event + ".");
|
||||
Thread.dumpStack();
|
||||
} else {
|
||||
postMessage(new EventNotification(event));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when {@link #setUsername} has been called and the new client
|
||||
* object is about to be applied to this client. The old client object
|
||||
* will not yet have been destroyed, so any final events can be sent
|
||||
* along prior to the new object being put into effect.
|
||||
*/
|
||||
protected void clientObjectWillChange (
|
||||
ClientObject oldClobj, ClientObject newClobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the new client object has been committed to this
|
||||
* client due to a call to {@link #setUsername}.
|
||||
*/
|
||||
protected void clientObjectDidChange (ClientObject newClobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this client instance with the specified username, connection
|
||||
* instance and client object and begins a client session.
|
||||
*/
|
||||
protected void startSession (
|
||||
ClientManager cmgr, AuthRequest req, Connection conn, Object authdata)
|
||||
{
|
||||
_cmgr = cmgr;
|
||||
_areq = req;
|
||||
_authdata = authdata;
|
||||
setConnection(conn);
|
||||
|
||||
// obtain our starting username
|
||||
assignStartingUsername();
|
||||
|
||||
// resolve our client object before we get fully underway
|
||||
cmgr.resolveClientObject(_username, this);
|
||||
|
||||
// make a note of our session start time
|
||||
_sessionStamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is factored out to allow derived classes to use a different
|
||||
* starting username than the one supplied in the user's credentials.
|
||||
* Generally one only wants to munge the starting username if the user
|
||||
* will subsequently choose a "screen name" and it is desirable to
|
||||
* avoid collision between the authentication user namespace and the
|
||||
* screen namespace.
|
||||
*/
|
||||
protected void assignStartingUsername ()
|
||||
{
|
||||
_username = getCredentials().getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the client manager when a new connection arrives that
|
||||
* authenticates as this already established client. This must only be
|
||||
@@ -418,46 +577,6 @@ public class PresentsClient
|
||||
Log.info("Session resumed " + this + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly terminates a client's session. This must be called from
|
||||
* the dobjmgr thread.
|
||||
*/
|
||||
public void endSession ()
|
||||
{
|
||||
// queue up a request for our connection to be closed (if we have
|
||||
// a connection, that is)
|
||||
Connection conn = getConnection();
|
||||
if (conn != null) {
|
||||
// go ahead and clear out our connection now to prevent
|
||||
// funniness
|
||||
setConnection(null);
|
||||
// have the connection manager close our connection when it is
|
||||
// next convenient
|
||||
PresentsServer.conmgr.closeConnection(conn);
|
||||
}
|
||||
|
||||
// if we don't have a client object, we failed to resolve in the
|
||||
// first place, in which case we have to cope as best we can
|
||||
if (_clobj != null) {
|
||||
// and clean up after ourselves
|
||||
try {
|
||||
sessionDidEnd();
|
||||
} catch (Exception e) {
|
||||
Log.warning("Choked in sessionDidEnd [client=" + this + "].");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
|
||||
// release (and destroy) our client object
|
||||
_cmgr.releaseClientObject(_username);
|
||||
}
|
||||
|
||||
// let the client manager know that we're audi 5000
|
||||
_cmgr.clientSessionDidEnd(this);
|
||||
|
||||
// clear out the client object so that we know the session is over
|
||||
_clobj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a runnable on the object manager thread where we can
|
||||
* safely end the session.
|
||||
@@ -479,52 +598,6 @@ public class PresentsClient
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called when the server is shut down in the middle of a
|
||||
* client session. In this circumstance, {@link #endSession} will
|
||||
* <em>not</em> be called and so any persistent data that might
|
||||
* normally be flushed at the end of a client's session should likely
|
||||
* be flushed here.
|
||||
*/
|
||||
public void shutdown ()
|
||||
{
|
||||
// if the client is connected, we need to fake the computation of
|
||||
// their final connect time because we won't be closing their
|
||||
// socket normally
|
||||
if (getConnection() != null) {
|
||||
long now = System.currentTimeMillis();
|
||||
_connectTime += ((now - _networkStamp) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a note that this client is subscribed to this object so that
|
||||
* we can clean up after ourselves if and when the client goes
|
||||
* away. This is called by the client internals and needn't be called
|
||||
* by code outside the client.
|
||||
*/
|
||||
public synchronized void mapSubscrip (DObject object)
|
||||
{
|
||||
_subscrips.put(object.getOid(), object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a note that this client is no longer subscribed to this
|
||||
* object. The subscription map is used to clean up after the client
|
||||
* when it goes away. This is called by the client internals and
|
||||
* needn't be called by code outside the client.
|
||||
*/
|
||||
public synchronized void unmapSubscrip (int oid)
|
||||
{
|
||||
DObject object = _subscrips.remove(oid);
|
||||
if (object != null) {
|
||||
object.removeSubscriber(this);
|
||||
} else {
|
||||
Log.warning("Requested to unmap non-existent subscription " +
|
||||
"[oid=" + oid + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the tracked client subscriptions. Called when the client
|
||||
* goes away and shouldn't be called otherwise.
|
||||
@@ -731,69 +804,6 @@ public class PresentsClient
|
||||
return _conn;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void handleMessage (UpstreamMessage message)
|
||||
{
|
||||
_messagesIn++; // count 'em up!
|
||||
|
||||
// if the client has been getting crazy with the cheeze whiz,
|
||||
// stick a fork in them; the first time through we end our
|
||||
// session, subsequently _throttle is null and we just drop any
|
||||
// messages that come in until we've fully shutdown
|
||||
if (_throttle == null) {
|
||||
// Log.info("Dropping message from force-quit client " +
|
||||
// "[conn=" + _conn +
|
||||
// ", msg=" + message + "].");
|
||||
return;
|
||||
} else if (_throttle.throttleOp(message.received)) {
|
||||
Log.warning("Client sent more than 100 messages in 10 seconds, " +
|
||||
"forcing disconnect " + this + ".");
|
||||
safeEndSession();
|
||||
_throttle = null;
|
||||
}
|
||||
|
||||
// we dispatch to a message dispatcher that is specialized for the
|
||||
// particular class of message that we received
|
||||
MessageDispatcher disp = _disps.get(message.getClass());
|
||||
if (disp == null) {
|
||||
Log.warning("No dispatcher for message [msg=" + message + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise pass the message on to the dispatcher
|
||||
disp.dispatch(this, message);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void objectAvailable (DObject object)
|
||||
{
|
||||
if (postMessage(new ObjectResponse<DObject>(object))) {
|
||||
// make a note of this new subscription
|
||||
mapSubscrip(object);
|
||||
} else {
|
||||
// if we failed to send the object response, unsubscribe
|
||||
object.removeSubscriber(this);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (int oid, ObjectAccessException cause)
|
||||
{
|
||||
postMessage(new FailureResponse(oid));
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void eventReceived (DEvent event)
|
||||
{
|
||||
if (event instanceof PresentsDObjectMgr.AccessObjectEvent) {
|
||||
Log.warning("Ignoring event that shouldn't be forwarded " +
|
||||
event + ".");
|
||||
Thread.dumpStack();
|
||||
} else {
|
||||
postMessage(new EventNotification(event));
|
||||
}
|
||||
}
|
||||
|
||||
/** Callable from non-dobjmgr thread, this queues up a runnable on the
|
||||
* dobjmgr thread to post the supplied message to this client. */
|
||||
protected final void safePostMessage (final DownstreamMessage msg)
|
||||
@@ -954,7 +964,7 @@ public class PresentsClient
|
||||
}
|
||||
|
||||
protected ClientManager _cmgr;
|
||||
protected Credentials _creds;
|
||||
protected AuthRequest _areq;
|
||||
protected Object _authdata;
|
||||
protected Name _username;
|
||||
protected Connection _conn;
|
||||
|
||||
Reference in New Issue
Block a user