MOO-cows Mailing List Archive


Re: MOO and HTTP

On Wed, 11 Sep 1996, Cheng Soo Yin wrote:

>         I am seeking for someones out there who had implemented the MOO-HTTP
> server or likewise. Please kindly share with me your precious experiences.
> Alternatively, you may like to give me some pointers and these are mostly
> welcome. I will like to discuss with those who are interested in this area.

Ooo boy, did you ask a fun question! :)

There are a lot of people out there who are running MOO-HTTP servers, and
I imagine you'll be getting a lot of replies.  I'll shoot this out for 
what it's worth, but be aware there are probably other solutions to 
making MOO and HTTP work together.

First of all, I recommend you read carefully the LambdaMOO Programmer's
Manual for version 1.8.0p5, the latest release of LambdaMOO.  (It's at*).  Assuming you more
or less know how MOO works, here are three basic ways that I know of to make
a MOO perform like an HTTP server: 

1) The Anonymous Method

Whenever a connection is opened to a MOO, each line of input from that
connection is sent as an argument to a database verb, called
"do_login_command" and defined on the 'listening object' for that
connection.  MOOs usually have only one listening object, which is usally
#0.  Each listening object deals with connections on a specific (and
unique) port.  The key to this method is to make a listening object that
listens on port 80, and has a "do_login_command" verb that can read and
deal with HTTP requests.  To read them, it can simply do something like
the following: 

  set_connection_option(player, "hold-input", 1);
  lines = {};
  while (line = read(player,1))
    lines = {@lines, line};
  set_connection_option(player, "hold-input", 0);
  " with the request ... ";
  return 0;

It's up to you how you want to deal with the request once it's been read 
in.  One good way is to make a GET and POST (and maybe HEAD) verb on the 
same listening object and have "do_login_command" figure out what method 
the request is using, then pass the lines of the request to that verb.  

The tricky thing at this point is deciding what URLs mean.  What URLs are
asking for is clear with conventional HTTP servers: the URL
"" means you are asking the server at for the file "pub/index.html" from the filesystem.  But MOOs
don't have a filesystem, so you have to come up with a protocol for the
URLs indicating just what a browser is requesting from the server.  For
instance, it could be that you'd do something like:

which would be a request for the text in the "bork" property on object
345.  Whatever you do, it should be something flexible and useful.  (Side
note: if you want to have access to the filesystem of the machine the MOO
is running on, I can certainly recommend Jaime Prilusky and Gustavo
Glusman's File Utilities Package, available at

So once you've decided on a protocol, you simply have to write your GET,
POST and HEAD verbs to parse the URL according to this protocol (i.e. 
extract the "345" and "bork" from the URL string, and turn "345" into
#345), get the appropriate data (read #345.bork) and then send it preceded
by the appropriate HTTP headers.  For example: 

  notify(player, "HTTP/1.0 200 Okay");
  notify(player, "Date: " + ctime());
  notify(player, "Server: MyMOO-HTTP");
  notify(player, "");
  notify(player, "<H1>Hello from MyMOO-HTTP!</H1");
  notify(player, #345.bork);

The "boot_player()" line in the code snippet several paragraphs above will
close the connection, and if #345.bork is a beautiful piece of HTML, the
person browsing will get a lovely web page, just like from more
conventional servers. 

2) The Authenticated Method

This method is similar, but allows you to authenticate requests.  For
instance, if #345.bork is readable only to player ABC, you'd want to make
sure player ABC was making the HTTP request.  HTTP specifies that
authentication is sent as the line

  Authentication: Basic XXXXX

where XXXX is the uuencoded version of the string "NAME:PASSWORD".  The
problem is that you've got to find a way to uudecode this string.  The
solution I've used is to write a builtin function uudecode() (the guts of
which are stolen from the Apache server) but I'll bet there's someone out
there who's implemented uudecode in-MOO.  (Anyone?) Either way should
work.  Once the request is authenticated and you know which player is
making the request, your GET/POST/HEAD verbs (which should probably be
wizard-owned) can check to see if the player is allowed to read #345.bork. 
If so, read & send it, if not, send a 401 Unauthorized. 

3) The Interactive Method

The final method I know about (and the one I'm most interested in) is
probably something this list has heard too much about from me ( :) ) but
I'll mention it quickly here.  

As I said above, "do_login_command" is called for each line of input from
a new connection, with that line as its argument.  If "do_login_command"
returns a valid player object number, the connection is logged in as that
player.  It is possible to have "do_login_command", each time it is
called, save its string argument to some convenient property (or even as
Gustavo cleverly suggested into the ".flush" connection option) until it
detects the last line of an HTTP request.  (Usually an empty line
immediately preceded by a line starting with "Content-length: X".)

This final instantiation of "do_login_command" (with the empty line) can
then check back through the saved header lines for the Authentication:
line, uudecode the authentication string, and if the name and password
authenticate correctly, return a valid player object.  The connection is
now "logged in", and will appear to other players in the MOO as a normal
player.  If this is combined with the trick of *not* sending the final
"boot_player()" line in the code snippet from Method (1), the connection
from the MOO server to the HTTP browser will be kept open, and other
players can say/emote/etc. anything to the player who is connected via the
Web browser. 

This becomes particularly exciting if your URL protocol has some way of
specifying commands to be entered as MOO commands, i.e.

Then the GET/POST/HEAD verb (wizard-owned) can use force_input() to make
those commands be entered as if the browser-connected player had typed
them via a normal telnet connection.  Voila: interactive Web Browsing! 
(And state-maintaining HTTP, because you can save any data you want to
about a player in the MOO database.)

I actually have been having a lot of luck with this approach, and would be
happy to tell you about it in more detail if you want, but I suspect this
message is too long already. 

Best of luck! :)


Colin McCormick
Tripod, Inc.

Follow-Ups: References:

Home | Subject Index | Thread Index