gawkinet: Simple Server
2.10 A Simple Web Server
========================
In the preceding node, we built the core logic for event-driven GUIs.
In this node, we finally extend the core to a real application. No one
would actually write a commercial web server in 'gawk', but it is
instructive to see that it is feasible in principle.
The application is ELIZA, the famous program by Joseph Weizenbaum
that mimics the behavior of a professional psychotherapist when talking
to you. Weizenbaum would certainly object to this description, but this
is part of the legend around ELIZA. Take the site-independent core logic
and append the following code:
function SetUpServer() {
SetUpEliza()
TopHeader = \
"<HTML><title>An HTTP-based System with GAWK</title>\
<HEAD><META HTTP-EQUIV=\"Content-Type\"\
CONTENT=\"text/html; charset=iso-8859-1\"></HEAD>\
<BODY BGCOLOR=\"#ffffff\" TEXT=\"#000000\"\
LINK=\"#0000ff\" VLINK=\"#0000ff\"\
ALINK=\"#0000ff\"> <A NAME=\"top\">"
TopDoc = "\
<h2>Please choose one of the following actions:</h2>\
<UL>\
<LI>\
<A HREF=" MyPrefix "/AboutServer>About this server</A>\
</LI><LI>\
<A HREF=" MyPrefix "/AboutELIZA>About Eliza</A></LI>\
<LI>\
<A HREF=" MyPrefix \
"/StartELIZA>Start talking to Eliza</A></LI></UL>"
TopFooter = "</BODY></HTML>"
}
'SetUpServer()' is similar to the previous example, except for
calling another function, 'SetUpEliza()'. This approach can be used to
implement other kinds of servers. The only changes needed to do so are
hidden in the functions 'SetUpServer()' and 'HandleGET()'. Perhaps it
might be necessary to implement other HTTP methods. The 'igawk' program
that comes with 'gawk' may be useful for this process.
When extending this example to a complete application, the first
thing to do is to implement the function 'SetUpServer()' to initialize
the HTML pages and some variables. These initializations determine the
way your HTML pages look (colors, titles, menu items, etc.).
The function 'HandleGET()' is a nested case selection that decides
which page the user wants to see next. Each nesting level refers to a
menu level of the GUI. Each case implements a certain action of the
menu. On the deepest level of case selection, the handler essentially
knows what the user wants and stores the answer into the variable that
holds the HTML page contents:
function HandleGET() {
# A real HTTP server would treat some parts of the URI as a file name.
# We take parts of the URI as menu choices and go on accordingly.
if(MENU[2] == "AboutServer") {
Document = "This is not a CGI script.\
This is an httpd, an HTML file, and a CGI script all \
in one GAWK script. It needs no separate www-server, \
no installation, and no root privileges.\
<p>To run it, do this:</p><ul>\
<li> start this script with \"gawk -f httpserver.awk\",</li>\
<li> and on the same host let your www browser open location\
\"http://localhost:8080\"</li>\
</ul>\<p>\ Details of HTTP come from:</p><ul>\
<li>Hethmon: Illustrated Guide to HTTP</p>\
<li>RFC 2068</li></ul><p>JK 14.9.1997</p>"
} else if (MENU[2] == "AboutELIZA") {
Document = "This is an implementation of the famous ELIZA\
program by Joseph Weizenbaum. It is written in GAWK and\
uses an HTML GUI."
} else if (MENU[2] == "StartELIZA") {
gsub(/\+/, " ", GETARG["YouSay"])
# Here we also have to substitute coded special characters
Document = "<form method=GET>" \
"<h3>" ElizaSays(GETARG["YouSay"]) "</h3>\
<p><input type=text name=YouSay value=\"\" size=60>\
<br><input type=submit value=\"Tell her about it\"></p></form>"
}
}
Now we are down to the heart of ELIZA, so you can see how it works.
Initially the user does not say anything; then ELIZA resets its money
counter and asks the user to tell what comes to mind open heartedly.
The subsequent answers are converted to uppercase characters and stored
for later comparison. ELIZA presents the bill when being confronted
with a sentence that contains the phrase "shut up." Otherwise, it looks
for keywords in the sentence, conjugates the rest of the sentence,
remembers the keyword for later use, and finally selects an answer from
the set of possible answers:
function ElizaSays(YouSay) {
if (YouSay == "") {
cost = 0
answer = "HI, IM ELIZA, TELL ME YOUR PROBLEM"
} else {
q = toupper(YouSay)
gsub("'", "", q)
if(q == qold) {
answer = "PLEASE DONT REPEAT YOURSELF !"
} else {
if (index(q, "SHUT UP") > 0) {
answer = "WELL, PLEASE PAY YOUR BILL. ITS EXACTLY ... $"\
int(100*rand()+30+cost/100)
} else {
qold = q
w = "-" # no keyword recognized yet
for (i in k) { # search for keywords
if (index(q, i) > 0) {
w = i
break
}
}
if (w == "-") { # no keyword, take old subject
w = wold
subj = subjold
} else { # find subject
subj = substr(q, index(q, w) + length(w)+1)
wold = w
subjold = subj # remember keyword and subject
}
for (i in conj)
gsub(i, conj[i], q) # conjugation
# from all answers to this keyword, select one randomly
answer = r[indices[int(split(k[w], indices) * rand()) + 1]]
# insert subject into answer
gsub("_", subj, answer)
}
}
}
cost += length(answer) # for later payment : 1 cent per character
return answer
}
In the long but simple function 'SetUpEliza()', you can see tables
for conjugation, keywords, and answers.(1) The associative array 'k'
contains indices into the array of answers 'r'. To choose an answer,
ELIZA just picks an index randomly:
function SetUpEliza() {
srand()
wold = "-"
subjold = " "
# table for conjugation
conj[" ARE " ] = " AM "
conj["WERE " ] = "WAS "
conj[" YOU " ] = " I "
conj["YOUR " ] = "MY "
conj[" IVE " ] =\
conj[" I HAVE " ] = " YOU HAVE "
conj[" YOUVE " ] =\
conj[" YOU HAVE "] = " I HAVE "
conj[" IM " ] =\
conj[" I AM " ] = " YOU ARE "
conj[" YOURE " ] =\
conj[" YOU ARE " ] = " I AM "
# table of all answers
r[1] = "DONT YOU BELIEVE THAT I CAN _"
r[2] = "PERHAPS YOU WOULD LIKE TO BE ABLE TO _ ?"
...
# table for looking up answers that
# fit to a certain keyword
k["CAN YOU"] = "1 2 3"
k["CAN I"] = "4 5"
k["YOU ARE"] =\
k["YOURE"] = "6 7 8 9"
...
}
Some interesting remarks and details (including the original source
code of ELIZA) are found on Mark Humphrys' home page. Yahoo! also has
a page with a collection of ELIZA-like programs. Many of them are
written in Java, some of them disclosing the Java source code, and a few
even explain how to modify the Java source code.
---------- Footnotes ----------
(1) The version shown here is abbreviated. The full version comes
with the 'gawk' distribution.