When I wrote the small app "SSE_clock" I was searching a replacement for a 
"long polling javascript code" that I was using in order to push db's table 
update notifications to clients. I abandoned the project by lack of 
browser's support.
Anyway, the application is a simple translation from php to python. 
Original demo target is to show that SSEs reconnect automatically and that 
it possible send multiple events on a single connection. Here attached 
you'll find original code in php to compare with python version.
However SSE has other features not discussed in the clock example. 
Below some links that I collected during my research:

http://blog.samshull.com/2010/10/ajax-push-in-ios-safari-and-chrome-with.html
http://my.opera.com/WebApplications/blog/show.dml/438711
http://dev.w3.org/html5/eventsource/
http://www.html5rocks.com/en/tutorials/eventsource/basics/
https://github.com/rwldrn/jquery.eventsource
http://www.igvita.com/2011/08/26/server-sent-event-notifications-with-html5/
http://dsheiko.com/weblog/html5-and-server-sent-events
http://blog.abourget.net/2010/6/16/html5-eventsource-in-pylons-read-comet-ajax-polling/
http://en.wikipedia.org/wiki/Server-sent_events
https://msmvps.com/blogs/theproblemsolver/archive/2011/11/07/html-5-server-sent-events.aspx
http://peter.sh/examples/?/javascript/event-source.html
http://weblog.bocoup.com/chrome-6-server-sent-events-with-new-eventsource
https://developer.mozilla.org/en/Server-sent_events/Using_server-sent_events
http://stevenhollidge.blogspot.com/2011/10/html5-server-side-events.html
http://www.dotnetage.com/publishing/home/2011/09/23/6932/html5-server-sent-events.html
http://moshhard.wordpress.com/2011/10/11/html5-server-sent-event-combine-with-web-notification/
http://www.codeproject.com/Questions/405117/Sending-Html5-SSE-to-specific-clients


Il giorno lunedì 8 aprile 2013 17:48:31 UTC+2, Arnon Marcus ha scritto:
>
> 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.


Attachment: sse.7z
Description: application/7z-compressed

Reply via email to