Look, I appreciate you're trying to help-out, but it seems you are answering the questions you know the answers to, instead of the questions I ask. It's OK to say that you don't know the answer. You are not alone in this user-group, perhaps someone else does.
We all got that. it's an external process, but it's implemented already, it > "just works", has a simple yet powerful routing algo and its secure. > With SSE you have to do it yourself. > > I know that there is a "somewhat-working" solution for web-sockets, using Tornado. I know it would be better to use it, instead of trying to make SSE work in web2py by myself. In the long-term I'll probably do something like that. But as you said, not in all scenarios, a web-socket is requited - sometimes an SSE does what I need. And as it is HTTP-based, I thought it should have been easy to implement in web2py. This is exactly the example shown on the videos about > websocket_messaging.py . the user receives updates through the ws, and he > sends to the default web2py installation with a simple ajax post its > message. web2py then queues that message to tornado, that informs all > connected users of the new message on the ws channel. > > Again, that is not an answer to my questions. My questions where referring to how web2py can implement SSE, not how Tornado can implement web-sockets and have web2py push stuff into it. > > On the SSE side, you'd have some controller that basically does: > > def events(): > initialization_of_sse > while True: > yield send_a_message > > you have to think to security, routing, etc by yourself. > > Basically in that while True loop you'd likely want to inspect your > "storage" (redis, ram, dict, database, whatever) if there's a new message > for the user. > You can't "exit" from there and resume it....all the logic needs to happen > inside that yield(ing) loop. > That is answering the question : "How does web2py keep a long-lasting connection". That is NOT answering the question: "How can a different controller-action activate this" I found a way to extract the web2py-SSE example, here are the relevant parts (I *bold*'ed the important stuff): *Controller:* # -*- coding: utf-8 -*- import time from gluon.contenttype import contenttype ### required - do no delete def user(): return dict(form=auth()) def download(): return response.download(request,db) def call(): return service() ### end requires def index(): return dict() def error(): return dict() def sse(): return dict() def buildMsg(eid , msg): mmsg = "id: %s\n" %eid mmsg += "data: {\n" mmsg += "data: \"msg\": \"%s\", \n" %msg mmsg += "data: \"id\": %s\n" %eid mmsg += "data: }\n\n" return mmsg *def sent_server_event():* response.headers['Content-Type'] = 'text/event-stream' response.headers['Cache-Control'] = 'no-cache' * def sendMsg():* startedAt = time.time(); #http://www.epochconverter.com/ * while True:* messaggio = buildMsg(startedAt , time.time()) * yield messaggio* * time.sleep(5)* * if ((time.time() - startedAt) > 10):break* * return sendMsg()* def event_sender(): response.headers['Content-Type'] = 'text/event-stream' response.headers['Cache-Control'] = 'no-cache' mtime = time.time() return 'data:' + str(mtime) *View (script-part):* * * if (!window.DOMTokenList) { Element.prototype.containsClass = function(name) { return new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)").test(this.className); }; Element.prototype.addClass = function(name) { if (!this.containsClass(name)) { var c = this.className; this.className = c ? [c, name].join(' ') : name; } }; Element.prototype.removeClass = function(name) { if (this.containsClass(name)) { var c = this.className; this.className = c.replace( new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)", "g"), ""); } }; } // sse.php sends messages with text/event-stream mimetype. *var source = new EventSource('{{=URL("sent_server_event")}}');* function Logger(id) { this.el = document.getElementById(id); } Logger.prototype.log = function(msg, opt_class) { var fragment = document.createDocumentFragment(); var p = document.createElement('p'); p.className = opt_class || 'info'; p.textContent = msg; fragment.appendChild(p); this.el.appendChild(fragment); }; Logger.prototype.clear = function() { this.el.textContent = ''; }; var logger = new Logger('log'); *function closeConnection() {* * source.close();* logger.log('> Connection was closed'); updateConnectionStatus('Disconnected', false); } function updateConnectionStatus(msg, connected) { var el = document.querySelector('#connection'); if (connected) { if (el.classList) { el.classList.add('connected'); el.classList.remove('disconnected'); } else { el.addClass('connected'); el.removeClass('disconnected'); } } else { if (el.classList) { el.classList.remove('connected'); el.classList.add('disconnected'); } else { el.removeClass('connected'); el.addClass('disconnected'); } } el.innerHTML = msg + '<div></div>'; } *source.addEventListener('message', function(event) {* //console.log(event.data) * var data = JSON.parse(event.data);* var d = new Date(data.msg * 1e3); var timeStr = [d.getHours(), d.getMinutes(), d.getSeconds()].join(':'); coolclock.render(d.getHours(), d.getMinutes(), d.getSeconds()); logger.log('lastEventID: ' + event.lastEventId + ', server time: ' + timeStr, 'msg'); }, false); *source.addEventListener('open', function(event) {* logger.log('> Connection was opened'); updateConnectionStatus('Connected', true); }, false); *source.addEventListener('error', function(event) {* if (event.eventPhase == 2) { //EventSource.CLOSED logger.log('> Connection was closed'); updateConnectionStatus('Disconnected', false); } }, false); var coolclock = CoolClock.findAndCreateClocks(); Now, I can see that it's ported from php, and that there are some unused stuff in the controller - probably as this is a ruff proof-of-concept only... Now, what this example is doing, basically, is establishing an SSE connection with a web2py controller, that yields a time-stamp, twice, than exits out of the loop. Meaning, it generates 2 responses for each single-connection, while sleeping 5 seconds in between, then the loop is broken, so web2py stops sending more responses. This closes the connection, and 3 seconds later (as is defined in the SSE spec), the connection re-establishes itself, and so on. There is also an option to close the connection manually, from the client side. That's all fine and dandy... But it answers NONE of the questions I asked... There is *no inter-controller/action communication* in here, there is no way to *POST* something *from the client to the server*, that will *call a different action* in web2py, which will *then invoke another yield* of the SSE action, thus* intentionally-spawning another response over the existing connection....* And what if there are multiple connections to multiple clients? the only way to differentiate between them would be via their sessions. Now, the way I understand this, it's a *fundamental "executional" * limitation of web2py - it has no concurrency, so each invocation of web2py's wsgi-handler, is in fact a single-process-single-thread type of scenario, so that there could never exist multiple sessions that are handled at the same time... Unless another process/thread is being spawned by the web-server itself. In that case, there would have to be some sort of inter-process/inter-thread communication going on, in order for one session in one thread, to invoke an action in a separate session on a different thread. The only way around this, would obviously have to be using web2py over something like gEvent. But the question would then still remain: Whether an in-proc/in-thread/cross-sub-routine communication, or an inter-proc/inter-thread communication, there would STILL exist a need to rout across "sessions". In a sense, the controller-action's execution-run-time would have to be bound to the session that invoked it. Am I understanding this correctly? If so, it's not a small matter - is a mismatch of fundamental execution-architecture. There is a critical component missing. If it INDEED does not exist in web2py, I would like it to be said up-front CLEARLY. This way, a discussion about future possibilities can be started, perhaps for web3py. I would then also not have to waste time and effort digging through un-documented territories, and half-ass'ed "proof-of-concept" that, evidently, prove that the concept does not work, and go around it to show some completely useless use-case... This is all very disappointing... -- --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.