From oleg Tue Mar 25 13:17:13 CST 1997
Newsgroups: comp.lang.scheme,comp.programming,comp.infosystems.www.authoring.cgi,comp.infosystems.www.misc
Subject: the Web IS a _computer_, HTML its language (and a pushy Scheme CGI setting it off)
Summary: a tail-f CGI that starts a Server-Net-Browser hyper-computer
Followup-To:
Distribution:
From: oleg-at-pobox.com
Keywords: Finite Automaton, FSM, HTTP, Scheme, CGI, Computation, WWW, Push
Cc:
Status: RO
I apologize for the corny title; yet I mean it _literally_. This
article is to describe a computer (a multi-threaded Finite State
machine) with the Net as its "hardware", an http server and a browser
as its TWO engines, http messages carrying html content as the
computer's data, and url as a program counter. This whole conglomerate
can really perform a sequence of steps on its own, querying and
changing a global state: it does _compute_. BTW, the CGI script that
sets the whole process off can also serve a _real_ purpose, say,
monitoring operating system's logs.
A while back there has been a discussion in comp.programming if HTML
is a Programming [language]. The interaction between a browser and a
http server -- which is typically a browser sending a request, the
server replying, and they both shut up -- can be called computation by
stretching a point. Just as one can press a "PI" button on a
calculator and say that it has "computed" 3.141592. Many people
however won't be so generous to call trivial fetching
calculation. Still, there are calculators that can (after being
properly programmed and set off) perform _automatically_ a _sequence_
of steps that query and modify a global environment and arrive at some
(visual) result. So can the whole HTTP network, as we are about to
show. Because it's HTML code that carries this global state around and
tells a browser to switch to another state, HTML can be rightfully
called a programming language.
It is not for nothing the fad word 'push' is mentioned in the title of
this article: server push is what the CGI script actually does to do
the real work. And yes, this CGI script is written entirely in
Scheme. Hopefully this gives one answer to a question (recently posted
on comp.lang.scheme) if Scheme can have any real use.
To start the web computation, "Open URL"
http://your.host/cgi-bin/tail-f
One may specify parameters along the way (especially if the URL is
going to be embedded in a link):
http://your.host/cgi-bin/tail-f?period=600&file=/w/web/ns-home/httpd-80/logs/access
As the script's name implies, the script watches a given 'file',
sending its last 'nlines' lines to the client. The script doesn't quit
after the first peek into the file, but rather keeps watching the
file, checking up on it every 'period' seconds. The watching stops
when a user agent (a browser, that is) closes the connection: say, the
user has pressed the 'stop' button. Of course watching terminates if
the file disappears. All in all, this is similar to a familiar UNIX
"tail -f" command, it merely works across the net.
Opening the tail-f URL starts off the server-web-browser finite
automaton. When it stops, you'll see the result of the computation: a
frame set with a HTML form on the top, and the last lines of the
'file' (if the file name was specified with the URL). The form (the
top frame) presents the parameters of the script -- file name, watch
period, nlines -- and their specified or implied values. One can
enter/modify them right on the form, and then click on the 'watch'
button. The bottom frame shows the tail of the file (or the reason why
it can't be done: say, the file does not exist or can't be read).
Beside 'file', 'nlines', 'period' parameters, the script uses one
more, "internal" parameter 'send', which carries the state of the
automaton and passes it between its two engines, the server and the
browser. All in all, the automaton has four states:
Initial state (#0): 'send' parameter is absent
The script only outputs the framework (frames)
Its first frame (or the body of )
will call this script again with send=form
The second frame will call this script with send=watch
(thus the computation is forked in two parallel "threads")!
Making a body: send=form
makes a form with the current tail-f parameters and
sends it to the client
form's ACTION is the present script with send=watch
Initiating watching the file: send=watch
Locating the file, and and letting the user know
that we starting the watch
Really watching the file: send=watching
Once initiated, the automaton goes through all these states without
any user intervention. It's the last, final state that does the useful
work: checks upon the file, and sends its last few lines to the user,
using a "server push".
BTW, in scanning/watching a file, we deliberately don't employ direct
positioning of the file. For one thing, fseek() etc. functions are not
standard in Scheme. For another, treating a file as a purely
sequential stream enables us to handle 'file's which are not regular
files per se: named (FIFO) pipes, raw devices, serial ports, etc. For
example, you can create and activate a FIFO pipe:
bash$ mknod /tmp/tf1 p
bash$ while true; do date > /tmp/tf1; sleep 10; done
and then open URL
http://your.host/cgi-bin/tail-f?file=/tmp/tf1&nlines=2 or simply,
http://your.host/cgi-bin/tail-f and enter "/tmp/tf1" on the
form. After that, whenever you glance at screen, you'll see two most
recent timestamps in the bottom frame, updated and "scrolled"
automatically.
This script's functioning has been tested with Netscape 3.0 on a Mac
and UNIX (HP-UX 10.00, Solaris 2.3), Netscape 2.01 on Win95 and (with
limited functionality) IE 3.0 on Win95. The CGI script itself is
interpreted by Gambit, a Scheme interpreter/compiler. The script can
be also compiled: that is, translated into C by GambitC compiler
(gambc24g) and then compiled into a standalone executable. Alas, I
don't have access to a publicly accessible HP box, or any other
computer with Gambit or Scheme interpreter/compiler already installed.
The text of the script is available from:
http://pobox.com/~oleg/ftp/Scheme/tail-f.scm
See http://pobox.com/~oleg/ftp/Scheme/myenv.scm
for definitions of ++, --! etc. macros and 'cout', 'cerr' procedures I
use in my Scheme code. Please see
http://pobox.com/~oleg/ftp/Scheme/
for descriptions.
The script implements all CGI functionality, tail-f functionality, and
the "server push". It also defines CGI:url-unquote to parse a
QUERY_STRING into an assoc list, and declares and implements a
circular buffer object. The latter two parts can be used
independently.
The following snippet from the tail-f.scm code shows how CGI
programming in Scheme actually looks like. BTW, this fragment tells
how the script makes the browser call itself again, in another
state. The snippet is a little bit long: I apologize.
(let*
((query-string (OS:getenv "QUERY_STRING"))
(query-parms (append (CGI:url-unquote (or query-string ""))
; default values for the optional parameters
'(("period" "5") ("nlines" "10"))))
(self-url (string-append "http:"
(or
(OS:getenv "SCRIPT_NAME")
(CGI:bail-out "SCRIPT_NAME env parameter expected"))))
)
; Prepare to watch the file...
(define (TF:prepare-watch)
(if (or (not file-to-watch) (string=? file-to-watch ""))
(CGI:bail-out "no file name specified"))
(if (not (OS:file-exists? file-to-watch))
(CGI:bail-out "The file " file-to-watch " does not exist"
"or is not permitted to read"))
(let ((self-url-full
(string-append self-url "?" (or query-string "")
(if query-string "&" ""))))
(cout "Content-type: text/html" nl nl
""
"

Starting watch of " file-to-watch
"

After the file is completely scanned, this display would "
"automatically change "
"and you will see the last " nlines " lines of the file

" nl
"If the change does not seem to be coming for a long time, "
"your browser may not support "
"a <META> tag. In this case, please "
"follow this link"
"" flush-output)))
To run the script, you need Gambit (2.4g), or any other Scheme
interpreter; I can also provide a compiled (after translation from
Scheme to C) executable for HP-UX platform.
A word of warning: the script really tells you the tail of any file it
can read. Thus every file which is world-readable on your system
becomes, well, WORLD-readable. Your sysadm may not like this idea. It
is rather easy however to add some form of file access control: for
example, by modifying the script to allow checking up on files only
>from /tmp directory. Better yet, one can use functionality already
provided by a HTTP server: put the tail-f script into a directory
access to which is controlled by .htaccess.
I will really appreciate any comments, questions and suggestions.