Obviously stupid JS WSGI gateway

300 views
Skip to first unread message

Stuart Langridge

unread,
Jan 29, 2009, 4:48:16 PM1/29/09
to serverjs
OK, this is obviously stupid, but: a trivial WSGI/JS gateway, which
I've called JSGI because I don't know what acronyms are. :)

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

Stuart Langridge

unread,
Jan 29, 2009, 4:58:15 PM1/29/09
to serverjs
Actually, given the "standard functions" idea from other threads, one
handy thing might be that your JS framework app() takes three
parameters: environ, start_response, and API, where API is an object
containing our standard API (API.File.open() would open files, etc,
etc). Would be a neat way to bundle stuff up; your jsgi gateway file
takes care of making sure that it passes an appropriate API object for
the JS interpreter it's running on (my noddy example uses rhino, so
it'd define API.File.open() in terms of rhino's readFile(), etc), and
if you had API.version in there and we standardised the API object's
api (heh) then you could say "this application requires JSGI version 2
to work".

Tom Robinson

unread,
Jan 29, 2009, 5:07:23 PM1/29/09
to serverjs
The Jack project is essentially JSGI: http://jackjs.org It's closer to
Ruby's Rack, but the same idea.

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.

-Tom

Stuart Langridge

unread,
Jan 29, 2009, 5:10:14 PM1/29/09
to serv...@googlegroups.com
> The Jack project is essentially JSGI: http://jackjs.org It's closer to
> Ruby's Rack, but the same idea.

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

Stuart Langridge

unread,
Jan 29, 2009, 5:22:51 PM1/29/09
to serv...@googlegroups.com
>> 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.

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.

Tom Robinson

unread,
Jan 29, 2009, 5:43:56 PM1/29/09
to serverjs
That's basically how it works, it just calls "invoke" with a single
argument, "env" (which is defined on Function objects, so functions
are apps too). It does *not* automatically create the Request and
Response objects, those are optional, and it's up to you to create
them if you want them.

You may have been looking at "Roundabout" which is a simple Sinatra-
like framework that does manage the Request/Response objects.
Roundabout isn't really part of Jack, and should probably be moved out
eventually.

I don't quite understand the reason for passing in API is.

-Tom

Stuart Langridge

unread,
Jan 29, 2009, 7:04:31 PM1/29/09
to serv...@googlegroups.com
> I don't quite understand the reason for passing in API is.

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.

Tom Robinson

unread,
Jan 29, 2009, 7:18:03 PM1/29/09
to serverjs
I think passing around your standard library as an argument takes the
"no global variables" mantra too far. Do you add this API parameter to
every single function in your program? What's the benefit? Eliminating
global variables isn't a benefit itself.

Peter Svensson

unread,
Jan 30, 2009, 12:31:22 AM1/30/09
to serv...@googlegroups.com
I definitely second 'No Globals'.
Also, Jack seems like a very good starting point for the JSGI spec.

Cheers,
PS

http://unclescript.blogspot.com

Ross Boucher

unread,
Jan 30, 2009, 12:53:51 AM1/30/09
to serverjs
I can't really think of any good reason for passing API around as a
parameter. Trying to do this without a single global sounds like a
nightmare to me. Personally, I'm fine with introducing globals where
appropriate "File, import", but I can see the argument for just doing
something like API.File, API.import. (API is a terrible name though).

Dion Almaer

unread,
Jan 30, 2009, 2:00:59 AM1/30/09
to serv...@googlegroups.com
How about js.* instead of API.*

js.File / js.io.File ;)

D

Peter Michaux

unread,
Jan 30, 2009, 2:26:15 AM1/30/09
to serverjs
On Jan 29, 9:53 pm, Ross Boucher <rbouc...@gmail.com> wrote:
> I can't really think of any good reason for passing API around as a
> parameter.

I agree. "File" can be a global object just like string. "File"
doesn't need to be available if the script doesn't need it...

> Personally, I'm fine with introducing globals where
> appropriate "File, import",

The most critical one of all is "load" or some other way to have more
code introduced to the current execution. After "load" is available
then modules of code can be distributed as zipped up packages which
introduce all sorts of other functionality. "File" can be loaded with
"load" when needed.

> but I can see the argument for just doing
> something like API.File, API.import. (API is a terrible name though).

If "API" is a global object, then that isn't "passing it around".

Peter

Stuart Langridge

unread,
Jan 30, 2009, 5:35:50 AM1/30/09
to serv...@googlegroups.com
> I can't really think of any good reason for passing API around as a
> parameter. Trying to do this without a single global sounds like a
> nightmare to me. Personally, I'm fine with introducing globals where
> appropriate "File, import", but I can see the argument for just doing
> something like API.File, API.import. (API is a terrible name though).

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
:-)

Tom Robinson

unread,
Jan 30, 2009, 6:28:21 AM1/30/09
to serverjs
If I understand correctly you want apps to look like this:

function myApp(env, api) {

// ... some stuff ...

someFunction(api, blah);

// ... some more stuff ...

return [200, headers, body];
}

function someFunction(api, someArgument) {

var f = new api.File("hello.txt");

// ... more stuff ...
}

So nearly every function in your application needs to have this api
argument in order to be able to call anything in the standard library?

Ionut Gabriel Stan

unread,
Jan 30, 2009, 9:01:13 AM1/30/09
to serv...@googlegroups.com
On 1/30/2009 09:00, Dion Almaer wrote:
> How about js.* instead of API.*
>
> js.File / js.io.File ;)

I'd call it JSGI

>
> D
>
> On Thu, Jan 29, 2009 at 9:53 PM, Ross Boucher <rbou...@gmail.com
> <mailto:rbou...@gmail.com>> wrote:
>
>
> I can't really think of any good reason for passing API around as a
> parameter. Trying to do this without a single global sounds like a
> nightmare to me. Personally, I'm fine with introducing globals where
> appropriate "File, import", but I can see the argument for just doing
> something like API.File, API.import. (API is a terrible name though).
>
> On Jan 29, 9:31 pm, Peter Svensson <psvens...@gmail.com

mvalente

unread,
Jan 30, 2009, 11:43:03 AM1/30/09
to serverjs


On Jan 30, 5:31 am, Peter Svensson <psvens...@gmail.com> wrote:
> I definitely second 'No Globals'.
> Also, Jack seems like a very good starting point for the JSGI spec.
>

Agreed. But I think that, although you should avoid globals, there
is some stuff that *is* global. Just like Array or Object, File needs
to be global. Next up someone will bring up monads...

-- MV
Reply all
Reply to author
Forward
0 new messages