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 [email protected].
For more options, visit https://groups.google.com/groups/opt_out.