gawkinet: Simple Server

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