A WSGI/JS gateway would mean that we can instantly plug JS apps into
all the existing WSGI things, meaning that that's one less thing we
have to invent -- I can imagine a "proper" JSGI later, with mod_jsgi
and whatnot, but for now something like this would get us off the
ground.
jsgi.py:
#!/usr/bin/python
# stupid python wsgi gateway to JavaScript
import os
import subprocess
def app(environ, start_response):
stdout, stderr = subprocess.Popen(["rhino","jsgi.js","framework.js"],
stdout=subprocess.PIPE).communicate()
lines = stdout.split("\n")
status = lines[0].strip()
lines.pop(0)
headers = []
while lines and lines[0].strip() != '':
header = lines.pop(0)
headers.append(tuple(header.split(":",1)))
lines.pop(0) # blank line
start_response(status, headers)
return lines
jsgi.js:
var jsgi = {
status: '200 OK',
headers: [],
environ: {},
start_response: function(status, headers) {
jsgi.status = status;
jsgi.headers = headers;
}
};
load(arguments[0]);
out = app(jsgi.environ, jsgi.start_response);
print(status);
for (var i=0; i<jsgi.headers.length; i++) {
print(jsgi.headers[i].join(': '));
}
print('\n');
for (var i=0; i<out.length; i++) {
print(out[i]);
}
framework.js (this would be your framework):
app = function(environ, start_response) {
status = '200 OK';
response_headers = [['Content-type','text/plain']];
start_response(status, response_headers);
return ['Hello world!\n'];
}
Testable with:
python -c "from wsgiref.simple_server import make_server; from jsgi
import app; httpd = make_server('', 8000, app); httpd.serve_forever()"
sil
--
New Year's Day --
everything is in blossom!
I feel about average.
-- Kobayashi Issa
Man, I have that open in a tab and haven't read about it yet :)
> So far it supports servlets containers via Rhino, Simple via Rhino,
> and v8cgi. I'm trying to add as many as I can, but it would be great
> if others contributed too. WSGI/Rack bridges to Jack would be very
> useful.
>
> Side note: Jack has a file called "core.js" where I put things that
> *should* be in a standard library, like File objects and a "require"
> mechanism. Everything else in Jack is intepretter independent (except
> the handlers, of course) It would be great to get rid of that, so I
> love the idea of standardizing this stuff.
How do you hand that stuff to the app you're running? I quite like the
idea of adapting WSGI to pass an API object to the framework.
Now I'm going to read about Jack ;)
sil
OK, have read about Jack now.
I think Jack does two things: it's a wsgi-a-like (the app.invoke()
stuff), and it's a very, very simple web app framework (Jack.Request,
Jack.Response), and I don't think it should do both. It's the
responsibility of the framework to create Request and Response
objects, I think. I'd suggest that Jack should just do the invoke()
stuff (and not Request/Response), but pass two parameters: environ and
API. Some of your core.js stuff should go in API, and API is our
standard, cross-interpreter API object that everyone's framework can
depend on. Make sense?
Then my daft script above can become a WSGI-to-Jack bridge and we get
all the benefits of WSGI, but Jack is the JS side of the equation --
and at some point someone will write mod_jack and then we plug
straight into Apache.
The (currently mostly theoretical) JavaScript frameworks need a way of
getting at various features provided by the interpreter: what we're
sort of referring to as the "base set" of functionality. Imagine, for
the moment, that this base set is the following (this is intentionally
very basic; open returns the contents, not a file object which can be
seek()ed, etc)
1. open(filename): returns the contents of the file filename
2. write(filename, data): writes data to filename
3. include(filename): includes a JavaScript file at this place in the
code (could be thought of as eval(open(filename)), if you had eval :))
4. system(cmd): shells out to run "cmd" and returns its output
Now, those can be offered to the framework in the following ways, I think:
1. just use the native functions provided by your interpreter. This
means that your framework and your app are specific to rhino or v8 or
spidermonkey or whichever
2. Put them in individual functions/objects in the global namespace
(so there'd be File.open, File.write, Include, and System functions in
the global namespace)
3. Put them in a wrapper object in the global namespace (so there'd
be, say, SSJS.File.open, SSJS.File.write, SSJS.Include, SSJS.System
functions, meaning there's only one global object, SSJS)
4. Pass that SSJS object to your framework, so there are no globals at all
I like the latter, number 4. (1), using the native functions, is a
non-starter if you want to be interpreter-agnostic (which I believe we
do). (2) and (3) are suspect because globals are risky; you don't want
to override something provided by the interpreter (or any future JS
interpreter!), and because, well, globals are a bad idea generally.
So, I like the idea of wrapping these things up in one object and
passing that object specifically to the framework.
As mentioned above, as well, the definition of this object can be
standardised and versioned, so your app can check SSJS.version and
complain if it's less than 2.3, or whatever.
Note that this "JSGI" thing would have one wrapper per JS interpreter
that it supports, so it would create the SSJS object specifically for
the interpreter that it's running on.
Jack is doing the right thing with its return values, though, as
noted; it's like Rack, and like WSGI 2.0, so calling yourapp(environ,
SSJS) and yourapp returning [status, [header, header], output] is a
jolly good idea.
That's how I imagine it would work.
Of course it is :) One minor (and it really is minor) extra advantage
with passing the API in as a parameter is that we don't have to come
up with one name that everyone agrees on and must never, ever change
:-)