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.


Reply via email to