Servlet Library

To ease the development of interactive servlets, the web-server collection also provides the following data types and procedures:

10.1  Data Definitions

10.1.1  Environment

An environment is a (listof (cons symbol string)). That is, an environment is a list of improper cons-pairs of a symbol and a string.

A byte-environment is a (listof (cons symbol bytes)). That is, a byte-environment is a list of improper cons-pairs of a symbol and a bytes.

10.1.2  Request

A request is

%(make-request symbol url environment environment string string)
(define-struct request (method uri headers bindings bindings/raw host-ip client-ip post-data/raw))

method
One of
  • 'get

  • 'post

uri
URL, see the net collection in help-desk for details.
headers
An environment containing optional HTTP headers for this request.
bindings
An environment containing optional name-value pairs from either the form submitted or the query part of the URL.
bindings/raw
Either a string or a byte-environment. This is primarily used for file uploads.
host-ip
A string representing what IP address of the server the request came to.
client-ip
A string representing what IP address is associated with the client.
post-data/raw
Either false or a byte-string representing the full POST data.

The bindings contain the information the user supplied when filling out a Web form and hence are usually the most useful part. This makes the following idiom common. For a full example, see Figure ``fig:ex-mul-servlet''.

(extract-bindings/single
  'sym
  (request-bindings
    (send/suspend
      ; ...
      )))

10.1.3  Response

A response is one of the following:

an X-expression representing HTML
Search for XML in help-desk. For example
'(html
  (head
    (title "Fruits")
    (link ((rev "made")
           (href "mailto:mburns@example.com")
           (title "Webmaster"))))
  (body
    (h1 "Fruits!")
    (ul
      (li "Banana")
      (li "Orange")
      (li "Grapefruit")
      (li "Shirley"))))

(list-rest bytes (listof (union string bytes)))
The first byte-string is the MIME type (often #"text/html", but see RFC 2822 for other options). The rest of the strings provide the document's content.

make-response/full
make-response/incremental

make-response/full

   natural-number string natural-number string environment environment(listof (union string bytes)) -> response
   (define (make-response/full code message seconds mime extras body) ···)

code
A natural number indicating the HTTP response code.
message
A string describing the code to a human.
seconds
A natural number indicating the time the resource was created. Use (current-seconds) for dynamically created responses.
mime
A string indicating the response type.
extras
An environment containing extra headers for redirects, authentication, or cookies.
body
(listof (union string bytes))

make-response/incremental

   natural-number string natural-number string environment(((union string bytes) -> void) -> void) -> response
   (define (make-response/incremental code message seconds mime extras gen) ···)

code, message, seconds, mime, extras
All the same as for make-response/full
(gen output) : ((union string bytes) -> void) -> void
The function gen consumes an output function. The output function consumes a string and sends it to the client. For HTTP/1.1 clients, the server uses the chunked encoding, which is reliable. HTTP/1.0 clients, however, can not distinguish between the end of the document and a lost connection. These facts have two implications. First, it is more efficient to send fewer, larger strings. Second, this response should not be used for data that must arrive reliably.

See also make-html-response/incremental in the section titled ``Helpful Servlet Functions''.

10.2  Core Procedures

When writing module servlets, loading servlet.ss via

(require (lib "servlet.ss" "web-server"))

provides the following procedures:

send/suspend

   (string -> response) -> request
   (define (send/suspend page) ···)

The argument, a function that consumes a string, is given a URL that can be used in the document. The argument function must produce a response corresponding to the document's body. Requests to the given URL resume the computation at the point send/suspend was invoked. Thus, the argument function normally produces an HTML form with the action attribute set to the provided URL. The result of send/suspend represents the next request. This procedure is often used for interaction with the user. For an example, see the section titled ``Example Multiplication Servlet''.

send/forward

   (string -> response) -> request
   (define (send/forward page) ···)

Acts like send/suspend, but clears the continuation table first. For example, if the servlet is an on-line quiz where the quiz-taker is not allowed to return to a previous page, use send/forward; this is how it is used in the example the section titled ``Example Math Test Servlet''.

send/finish

   response -> void
   (define (send/finish response) ···)

This provides a convenient way to report an error or otherwise produce a final response. Once called, all URLs generated by send/suspend become invalid. Calling send/finish allows the system to reclaim continuations used by the servlet. Often used as the final result of a servlet. For an example, see the section titled ``Example Multiplication Servlet''.

send/back

   response -> void
   (define (send/back response) ···)

Acts like send/finish, but does not clear the continuation table. Useful for allowing the user to correct erroneous data.

To summarize the differences in the send/ procedures, see Figure ``fig:send''.

Type Refreshes the continuation table Does not refresh the continuation table
response -> void send/finish send/back
(string -> response) -> request send/forward send/suspend
Figure 1:  Differences in the send/ procedures

adjust-timeout!

   natural-number -> void
   (define (adjust-timeout! number) ···)

The server will stop an instance of a servlet after it has been idle for a certain amount of time, as described in the the section titled ``Timeouts''. Calling adjust-timeout! allows the programmer to change the number of seconds before the servlet times out. Larger numbers consume more resources while smaller numbers force the user to restart computations more often.

Unit servlets receive these interaction procedures as imports through the servlet^ signature.

10.3  Helpful Servlet Procedures

extract-binding/single

   symbol environment -> string
   (define (extract-binding/single sym environment) ···)

This extracts a single value associated with sym in the form bindings. If multiple or zero values are associated with the name, it raises an exception. For an example, see the section titled ``Example Multiplication Servlet''.

extract-bindings

   symbol environment -> (listof str)
   (define (extract-bindings sym environment) ···)

Returns the list of values associated with the name sym.

exists-binding?

   symbol environment -> bool
   (define (exists-binding? sym environment) ···)

Returns true if the name sym is bound in the environment. This is useful for, e.g., checkboxes.

extract-user-pass

   environment -> (union #f (cons str str))
   (define (extract-user-pass environment) ···)

Servlets may implement password-based authentication by extracting password information from the HTTP headers. The return value is either a pair consisting of the username and password from the headers or \#f if no password was provided. This only extracts the provided username and password; the servlet must perform any desired checking.

For more information on passwords, see the section titled ``Passwords''.

report-errors-to-browser

   (response -> void) -> void
   (define (report-errors-to-browser where) ···)

Calling this procedure at the beginning of a servlet causes otherwise uncaught exceptions to send an error page displaying the exception to the client. The argument should be based on send/finish if errors are fatal or send/back if the user may correct the failed form submission via the back button. For some applications, revealing the contents of an exception to a client may cause security problems by leaking sensitive information. For such applications, consider setting the current-exception-handler to email the error to the servlet author or store the exception in a log file.

redirect-to

   string [symbol] -> response
   (define (redirect-to url . redirection-status) ···)

Constructs a response that redirects to the given url. The optional argument specifies which kind of redirection to perform:

permanently
Browsers should send future requests directly to this URL.
temporarily
Browsers should send future requests to the original URL.
see-other
The redirection is not replacing the current URL.

See the HTTP 1.1 specification for details on each kind of redirection. The default redirection type is permanently.

make-html-response/incremental

   (((union string bytes) -> void) -> void) -> response/incremental
   (define (make-html-response/incremental chunk-maker) ···)

This fills in default values for make-response/incremental to be appropriate for HTML.

Note that the function passed to this will be called in the connection thread, not the servlet thread. This means that e.g. current-directory will be the server's current directory, not the servlet's.

10.4  Example Multiplication Servlet

As an example of send/suspend, send/finish, and request-bindings, and extract-bindings/single, consider Figure ``fig:ex-mul-servlet''. This servlet first prompts the user for the first number, then for the second number, and finally returns the product of the two numbers. Note that the final page is abstracted into a simple function, as is the common page to request a number.

(require (lib "unitsig.ss")
	 (lib "servlet-sig.ss" "web-server"))

(unit/sig ()
  (import servlet^)

  ;; answer-page : Number -> Xexpr
  (define (answer-page n)
    `(html
       (head
         (link ((rev "made") (href "mburns@example.com") (title "Webmaster"))))
       (body (h1 "Answer")
	     (p ,(string-append "The answer is " (number->string n))))))

  ;; get-number : String -> Number
  (define (get-number which)
    (string->number
      (extract-binding/single
        'num
        (request-bindings
	  (send/suspend
	    (get-number-page which))))))

  ;; get-number-page : String -> String -> Xexpr
  (define (get-number-page which)
    (lambda (k-url)
    `(html
       (head
         (link ((rev "made") (href "mburns@example.com") (title "Webmaster"))))
       (body (h1 "Enter a number")
         (form ((action ,k-url))
           (p ,(string-append "Please enter the " which " number"))
	   (input ((type "text") (name "num") (id "num")))
	   (input ((type "submit"))))))))

  (send/finish (answer-page (* (get-number "first") (get-number "second")))))

Figure 2:  Example servlet using send/suspend, send/finish, request-bindings, and extract-binding/single

For more example servlets, look in the collects/web-server/default-web-root/sevlets/examples directory.

10.5  Example Math Test Servlet

The servlet in Figure ``fig:ex-math-test-servlet'' administers a simple math quiz to the user, keeping track of how many problems were answered correctly and incorrectly. As such, we don't want the user to simply hit the back button each time she enters a wrong answer; thus, we use send/forward to prevent against just that.

(require (lib "unitsig.ss")
         (lib "servlet-sig.ss" "web-server"))

(unit/sig ()
  (import servlet^)

  ;; build-page : String Number Number Xexpr -> Xexpr
  (define (build-page title num-wrong num-right body)
    `(html
       (head (title ,title)
             (link ((rev "made")
                    (href "mailto:netgeek@speakeasy.net")
                    (title "Webmaster"))))
       (body (h1 ,title)
             (p ,(string-append "Wrong: " (number->string num-wrong)))
             (p ,(string-append "Right: " (number->string num-right)))
             ,body)))
  
  ;; get-the-answer : Number Number Number Number -> Number
  (define (get-the-answer num-wrong num-right a b)
    (string->number
      (extract-binding/single
        'answer
        (request-bindings
          (send/forward
            (get-the-answer-page num-wrong num-right a b))))))

  ;; get-the-answer-page : Number Number Number Number -> String -> Xexpr
  (define (get-the-answer-page num-wrong num-right a b)
    (lambda (k-url)
      (build-page
        "What's the answer?"
        num-wrong
        num-right
        `(form ((action ,k-url))
               (p ,(string-append (number->string a)
                                  " * "
                                  (number->string b)))
               (p (input ((type "text")
                          (id "answer")
                          (name "answer"))))
               (p (input ((type "submit"))))))))

  (let loop ((num-wrong 0)
             (num-right 0)
             (a 5)
             (b 7))
    (if (= (* a b) (get-the-answer num-wrong num-right a b))
      (loop num-wrong (+ num-right 1) (+ a 1) (+ b 1))
      (loop (+ num-wrong 1) num-right (+ a 1) (+ b 1)))))

Figure 3:  Simple math test servlet, showing send/forward

10.6  Servlet Development Environment

Choose ``Add TeachPack...'' from DrScheme's ``Language'' menu and select the plt/teachpack/htdp/servlet.ss teachpack. This provides functions for writing servlets including send/suspend and send/finish. All the extra servlet helper functions for extracting information from Web requests and building Web responses also become available through the TeachPack.

For information on plt/teachpack/htdp/servlet2.ss, see Extended Exercises.

The TeachPacks add the following functionality to the DrScheme environment:

Currently, minor edits will need to be made to your existing servlets to use them with the TeachPacks.