racket/collects/help/servlets/scheme/misc/cgi.ss
Eli Barzilay 3532652539 misc improvements
svn: r7191
2007-08-27 04:07:47 +00:00

152 lines
6.5 KiB
Scheme

(module cgi mzscheme
(require "../../private/headelts.ss")
(require (lib "servlet.ss" "web-server"))
(provide interface-version timeout start)
(define interface-version 'v1)
(define timeout +inf.0)
(define (start initial-request)
(with-errors-to-browser
send/finish
(lambda ()
`(html
(head ,hd-css
,@hd-links
(title "How to write CGI scripts"))
(body
(h1 "How to write CGI scripts")
(a ([name "cgi"] (value "CGI scripts")))
"Type " (tt "CGI") " in the " (b "Search for") " "
"field in Help Desk and click on the "
(b (tt "Search")) " button to get information "
"on CGI-related functions."
(p)
"A CGI script is merely a program with funny inputs and "
"outputs. Input comes either from an environment variable "
"or through the standard input port, in a special format. "
"Output consists of a MIME header followed by the content. "
"Everything in-between is pure program."
(p)
"MzScheme comes with a CGI library that is designed to "
"make it easy to write such scripts. In the mini-tutorial "
"below, we'll walk you through the "
"construction of such a script. If you have questions or "
"comments, send email to "
(a ((href "mailto:sk@plt-scheme.org")) "sk@plt-scheme.org") "."
(p)
(hr)
(p)
"Let's write a simple \"finger server\" in MzScheme. "
"The front-end will be a Web form that accepts a username. "
"The form should supply a username in the field `name'. "
"The CGI script fingers that user."
(p)
"First, make sure you have MzScheme installed on the host "
"where your Web server is located."
(p)
"A CGI script must be an executable. Each OS has different "
"ways of launching an application. Under Unix, it's "
"probably easiest to make them simple shell scripts. "
"Therefore, place the following magic incantation at the "
"top of your script:"
(p)
(pre " #!/bin/sh" (br)
" string=? ; exec /usr/local/bin/mzscheme -r $0 \"$@\"")
(p)
"Make sure the path to MzScheme is specified correctly."
(p)
"Now we're in Scheme-land. First, let's load the Scheme "
"CGI library and define where `finger' resides."
(p)
(pre
" (require (lib \"cgi.ss\" \"net\"))" (br)
" (define finger-program \"/usr/bin/finger\")")
(p)
"Next we must get the names bound by the form, and "
"extract the username field."
(p)
(pre
" (let ((bindings (get-bindings)))" (br)
" (let ((name (extract-binding/single 'name bindings)))")
(p)
"We use extract-binding/single to make sure only one name "
"field was bound. (You can bind the same field multiple "
"times using check-boxes. This is just one kind of "
"error-checking; a robust CGI script will do more."
(p)
"Next we invoke the finger program using `process*'. "
"If no username was specified, we just run finger on the host."
(p)
(pre
" (let ((results (if (string=? name \"\"))" (br)
" (process* finger-program)" (br)
" (process* finger-program name))))")
(p)
"The `process*' function returns a list of several values. "
"The first of these is the output port. Let's pull this "
"out and name it."
(p)
(pre
" (let ((proc->self (car results)))")
(p)
"Now we extract the output of running finger into a "
"list of strings."
(p)
(pre
" (let ((strings (let loop " (br)
" (let ((l (read-line proc->self)))" (br)
" (if (eof-object? l)" (br)
" null" (br)
" (cons l (loop))))))))")
(p)
"All that's left is to print this out to the user. "
"We use the `generate-html-output' procedure to do that, "
"which takes care of generating the appropriate MIME header "
"(as required of CGI scripts). "
"Note that the <pre> tag of HTML doesn't prevent its "
"contents from being processed. To avoid this "
"(i.e., to generate truly verbatim output), "
"we use `string->html', which knows about HTML quoting "
"conventions."
(p)
(pre
" (generate-html-output \"Finger Gateway Output\"" (br)
" (append " (br)
" '(\"<pre>\")" (br)
" (map string->html strings)" (br)
" '(\"</pre>\"))))))))")
(p)
"That's all! This program will work irrespective of "
"whether the form uses a GET or POST method to send its "
"data over, which gives designers additional flexibility "
"(GET provides a weak form of persistence, while "
"POST is more robust and better suited to large volumes of "
"data)."
(p)
"Here's the entire program, once again:"
(p)
(pre
" #!/bin/sh" (br)
" string=? ; exec /usr/local/bin/mzscheme -r $0 \"$@\"" (br)
"" (br)
" (require (lib \"cgi.ss\" \"net\"))" (br)
" (define finger-program \"/usr/bin/finger\")" (br)
"" (br)
" (let ((bindings (get-bindings)))" (br)
" (let ((name (extract-binding/single 'name bindings)))" (br)
" (let ((results (if (string=? name "")" (br)
" (process* finger-program)" (br)
" (process* finger-program name))))" (br)
" (let ((proc->self (car results)))" (br)
" (let ((strings (let loop " (br)
" (let ((l (read-line proc->self)))" (br)
" (if (eof-object? l)" (br)
" null" (br)
" (cons l (loop)))))))" (br)
" (generate-html-output \"Finger Gateway Output\"" (br)
" (append" (br)
" '(\"<pre>\")" (br)
" (map string->html strings)" (br)
" '(\"</pre>\"))))))))")))))))