private async Task messageFrom(WebSocketConnection wsc, string msg)
        {
            ManagedConnection mc = null;

            lock (conns)
            {
                mc = conns.Find(x => x.WSC.Id == wsc.Id);
            }
            if (mc == null)
            {
                await wsc.CloseIfNotClosedAsync("Where did this websocket connection come from?");

                return;
            }
            mc.LastActiveUtc = DateTime.UtcNow;
            // Diagnostic: see what happens when message handling code throws
            if (msg == "BOO")
            {
                throw new Exception("up");
            }
            // Client announcing their session key as the first message
            if (msg.StartsWith("SESSIONKEY "))
            {
                if (mc.SessionKey != null)
                {
                    await wsc.CloseIfNotClosedAsync("Client already sent their session key");

                    return;
                }
                var sessionKey = msg.Substring(11);
                var startStr   = docJuggler.StartSession(sessionKey);
                if (startStr == null)
                {
                    await wsc.CloseIfNotClosedAsync("We're not expecting a session with this key");

                    return;
                }
                mc.SessionKey = sessionKey;
                await wsc.SendAsync("HELLO " + startStr, CancellationToken.None);

                return;
            }
            // Anything else: client must be past sessionkey check
            if (mc.SessionKey == null)
            {
                await wsc.CloseIfNotClosedAsync("Don't talk until you've announced your session key");

                return;
            }
            // Just a keepalive ping: see if session is still open?
            if (msg == "PING")
            {
                if (!docJuggler.IsSessionOpen(mc.SessionKey))
                {
                    await wsc.CloseIfNotClosedAsync("This is not an open session");
                }
                return;
            }
            // Client announced a change
            if (msg.StartsWith("CHANGE "))
            {
                int ix1 = msg.IndexOf(' ', 7);
                int ix2 = msg.IndexOf(' ', ix1 + 1);
                if (ix2 == -1)
                {
                    ix2 = msg.Length;
                }
                int    revId     = int.Parse(msg.Substring(7, ix1 - 7));
                string selStr    = msg.Substring(ix1 + 1, ix2 - ix1 - 1);
                string changeStr = null;
                if (ix2 != msg.Length)
                {
                    changeStr = msg.Substring(ix2 + 1);
                }
                if (!docJuggler.ChangeReceived(mc.SessionKey, revId, selStr, changeStr))
                {
                    await wsc.CloseIfNotClosedAsync("We don't like this change; your session might have expired, the doc may be gone, or the change may be invalid.");
                }
                return;
            }
            // Anything else: No.
            await wsc.CloseIfNotClosedAsync("You shouldn't have said that");
        }