I am using App Engine Modules in my python project. (https://developers.google.com/appengine/docs/python/modules/#Python_Background_threads)
I am also using channels in m project: https://developers.google.com/appengine/docs/python/channel/
I want to direct the connected/disconnected post messages ('/_ah/channel/connected/', '/_ah/channel/disconnected/') to my api module. Right now I can't get them to show up in any module (default or api)
app.yaml
api_version: 1
application: integrate
version: 1-0-0
runtime: python27
threadsafe: true
builtins:
- deferred: on
libraries:
- name: pycrypto
version: "2.6"
handlers:
- url: /favicon\.ico
static_files: static/favicon.ico
upload: static/favicon\.ico
- url: /admin/.+
script: src.default.main.app
login: admin
- url: /.*
script: src.default.main.app
api.yaml
api_version: 1
application: integrate
module: api
version: 1-0-0
runtime: python27
threadsafe: true
inbound_services:
- channel_presence
builtins:
- deferred: on
libraries:
- name: pycrypto
version: "2.6"
handlers:
- url: /admin/.+
script: src.api.main.app
login: admin
- url: /.*
script: src.api.main.app
dispatch.yaml
application: integrate
dispatch:
- url: "*/_ah/channel/*"
module: api
Note: Just to be clear this all works in dev mode locally.
api.main.app
app = webapp2.WSGIApplication(debug=True)
_routes = [
:
ChannelDisconnectedHandler.mapping(),
ChannelConnectHandler.mapping()
]
for r in self._routes:
app.router.add(r)
ChannelDisconnectHandler
CHANNEL_DISCONNECTED_URL_PATTERN = '/_ah/channel/disconnected/'
class ChannelDisconnectedHandler(RequestHandler):
@classmethod
def mapping(cls):
return CHANNEL_DISCONNECTED_URL_PATTERN, cls
def post(self):
"""
Channel Presence handler. Will be called when a client disconnects.
"""
channel_id = self.request.get('from')
logging.info("Channel Disconnect. Id: %s" % channel_id)
ChannelConnectHandler
CHANNEL_CONNECT_URL_PATTERN = '/_ah/channel/connected/'
class ChannelConnectHandler(RequestHandler):
@classmethod
def mapping(cls):
return CHANNEL_CONNECT_URL_PATTERN, cls
def post(self):
"""
Channel Presence handler. Will be called when a client connects.
"""
channel_id = self.request.get('from')
logging.info("Channel Connect. Id: %s" % channel_id)
So my client (written in javascript) posts to my api module and opens a channel.
var open_channel = function(tokenResponse) {
console.log("Open Channel. token Response: " + tokenResponse)
token = tokenResponse.token;
var channel = new goog.appengine.Channel(token);
if (socket != null) {
socket.close();
}
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
};
onOpened = function() {
console.info("Channel API Connection is open.");
};
onError = function(e) {
console.info("CHANNEL Error. Code: " + e.code + ", Description: " + e.description);
};
onClose = function() {
console.info("Close Channel");
};
onMessage = function(msg) {
console.info("Message Received: " + msg + ", Data: " + msg.data);
};
This callback function is reached with a valid token. I create the socket successfully and complete this function as expected. On my local system the onOpened function is then called and I receive the messages from the server. In production onOpened is never called and I never receive any messages. The /_ah/channel/connected/ is also never called.
Is the Channel service not supported with modules? Any thoughts as to what I am missing?
According to Google Enterprise Support (modified slightly from their raw answer):
channel_presence
inbound service must be enabled inapp.yaml
.Enabling this inbound service in module’s yaml file (e.g.,
api.yaml
in this question) won’t enable this service.URL paths starting with
*/_ah
are not dispatchable paths and cannot be routed bydispatch.yaml
. Therefore,channel_presence
URL paths handlers have to be described inapp.yaml
.You have to declare a hadler routing for connect and disconects URLs.
Handler routing in
main.py
:I've bumped into issues with using the Channel API in modules as well and I tried to work around them using a similar trick as Emil mentions by redirecting the requests to the modules.
It was a slightly more complicated setup though because I actually had 3 modules, where 2 of them used the Channel API and one was the 'frontend'. Something like this:
I wanted to be able to listen to "notifications" from the two separate services in the frontend.
And the way I managed to work around that (in dev) was to add redirects to the frontend which read the tokens which I had prefixed on each service and redirect to each service.
"Great, It works!" I thought but then when I tried to deploy to app engine I realized there's more to it there as the talkgadget endpoints used internally by the Channel API seemed to expect a certain source app and thus did not allow cross domain communication.
So I ended up using multiple projects instead of modules and by putting an HTML iframe "postMessage bridge" to get around the cross domain issues. And gladly it works really well and as a side effect I get twice as many "free" channels to use.
I found an issue related to this here which may be interesting for you to track: https://code.google.com/p/googleappengine/issues/detail?id=10293