javascriptgoogle-app-enginegoogle-apigoogle-api-python-clientchannel-api

Channel API -- Invalid Token on production, works on dev server


I'm having difficulty with the Channel API, but only on production. On the dev server this works, but on production I'm getting an Invalid+token error in my javascript, and somehow blank errors in my Python.

Here's my server side code. 'channel_test' is my controller, 'do_send_message' is a function that is deferred, and 'send_message' is a wrapper with a try-except block. Basically it's creating the channel, setting the token as a cookie, and calling deferred tasks until a counter goes up to 100. Each time a task is run, it sends a message over the channel.

def channel_test(self, key):
    if not users.is_current_user_admin(): return 403
    client_id = self.user.email()+key
    token = channel.create_channel(client_id, duration_minutes=10)
    print 'token: %s' % token
    self.response.set_cookie('token', urllib.quote(token))
    deferred.defer(do_send_message, client_id, _queue='retry')

# channel communication
def send_message(client_id, msg):
    """
    msg is a dict
    """
    if client_id:
        print 'client_id: ', client_id
        try:
            channel.send_message(client_id, json.dumps(msg))
            print 'sent message %s' % msg
        except Exception as e:
            print 'something went wrong with msg, %s: %s, %s' % (msg, e, e.__str__())
    else:
        print 'not sending message; no client id'

def do_send_message(client_id, x=10):
    time.sleep(1)

    text = "here's a message! %s" % x
    if x >= 100:
        text = "Done!"
    msg = {"text":text, "percent":x}
    send_message(client_id, msg)

    if x < 100:
        deferred.defer(do_send_message, client_id, x+10, _queue='retry')

Each time 'send_message' is called, it fails, but 'e' doesn't print anything out. Here's the output in the log:

something went wrong with msg, {'text': "here's a message! 10", 'percent': 10}: , 

Furthermore, my client side javascript fails to open the connection. It just get the error 'Invalide+token'. This basically reads the token from the cookie, opens a connection, and each time a message is passed, it writes the message to a notification tray, until the message == 'Done!' and then reloads the page. But, as a said, it fails immediately and onError prints 'Object {description: "Invalid+token.", code: "401"}'.

// globals
var channel, socket, hide;
var $msg = $('#channel-message');
var $bar = $('#notification-tray .progress-bar');


onMessage = function(obj){
    var message = JSON.parse(obj.data);
    if (message.percent !== undefined){
        $bar.css('width', message.percent+'%');
    }
    switchMsg(message.text);
};

onOpen = function(){
    $('#notification-tray').fadeIn();
    $bar.css('width', '10%');
};

onClose = function(){
    $('#notification-tray').fadeOut();
};

onError = function(err){
    $('#notification-tray').fadeOut();
    console.log(err);
};


function closeAndReload(){
    socket.close();
    $.removeCookie('token', {path:'/'});
    $.removeCookie('hide', {path:'/'});
    location.reload();
}

function switchMsg(msg){
    $msg.fadeOut(function(){
        $msg.html(msg);
        $msg.fadeIn(function(){
            if (msg == 'Done!'){
                closeAndReload();
            }
        });
    });
}

function initializeChannel(){
    channel = new goog.appengine.Channel(token);
    socket = channel.open();
    socket.onmessage = onMessage;
    socket.onopen = onOpen;
    socket.onclose = onClose;
    socket.onerror = onError;
}


$(function(){
    token = $.cookie('token');
    if (token !== undefined && token != ""){
        initializeChannel();
    }
});

This works perfectly on my dev server, even though I get this issues in production.

Thanks in advance.


Solution

  • Okay this is frustrating, but I think I've figured out that the token has a certain undocumented maximum length. I set the client_id to

    client_id = self.user.email()+key[0:10]
    

    instead of

    client_id = self.user.email()+key
    

    and it now works. Of course it's annoying that (1) it works on the dev server and not on production and (2) the error is not very informative.