Generating Dynamic Content with TWS

Overview

Generating dynamic content (that is, HTML pages that are created on-the-fly) is very easy with TWS. Here is what you have to do:

  1. Look at the MIME notebook tab on the built-in TWS configuration web page to determine what filename suffixes are used for dynamic content files. In the default configuration, a content file with the suffix .tws is processed as dynamic content with a default MIME type of text/html. You can change this suffix to something different using the built-in configuration web pages or you can add additional dynamic content suffixes with different default MIME types. Or you can just use the default.

  2. Create a content file having the appropriate suffix using your favorite editor.

  3. Embed TCL code in the new content file by placing the code within <%...%> markup. When the file is requested, the TWS server will execute the embedded TCL code and replace the original <%...%> markup with the string returned by the enclosed the TCL code. Similarly, TWS replaces <$...> markup with the value of the TCL variabled named within the markup. Text of the content file that is not contained within <%...%> or <$...> is unchanged.

  4. TCL variables may be embedded in dynamic content files within <$...> markup. The markup is replaced with the value of the variable. If the variable does not exist, the markup is replaced by an empty string.

That is the gist of what needs to be done. Of course, there are some details which will be described later. But if you are adventuresome and/or impatient, you can begin writing your own dynamic content for TWS with no further help.

For some examples of dynamic content for TWS, try looking at the files that implement the built-in TWS configuration web pages. The configuration web pages are part of a ZIP archive that are appended to the end of the TWS executable. Use a ZIP de-archiving program like unzip or pkunzip to extract the following:

config/common.tcl
config/index.tws
config/pw/dispatcher.tws
config/pw/dedit.tws
config/pw/mime.tws
config/pw/medit.tws
config/pw/auth.tws
config/pw/aedit.tws
config/pw/user.tws
config/pw/uedit.tws
config/pw/shutdown.tws

Another resource is the documentation for the new TCL commands that TWS implements and which are designed for use in dynamic content. Descriptions of the new TCL commands are available at http://www.hwaci.com/sw/tws/commands.html. Most of these new TCL commands are used in the built-in configuration web pages so you can see examples of their use there.

But if you not so impatient and you want to really understand the details of how TWS works, you can keep reading this document.

A Very Simple Example

Let's begin with a very simple example: a web page that returns the current time of day. Create a file named "ex1.tws" (assuming you are using the default dynamic content suffix) and put in the following code:

The current time and date:
<% clock format [clock seconds] %>

Now put the ex1.tws file where TWS can find it and type the appropriate URL into your web browser. You should get back a screen that looks something like this:

The current time and date: Mon Feb 19 08:42:08 EST 2001

What happened here is that TWS replaced the "<% clock format [clock seconds] %>" markup in the original content file with the result of the clock command: the current time and date. Try this simple experiment to make sure it works for you and that your TWS server setup and working properly.

The hsubst Command

TWS executes embedded TCL code using the the hsubst TCL command. Hsubst is one of several custom TCL commands that TWS implements. Here is how it works:

TWS first reads the original content file into a TCL string variable. Then it invokes the hsubst on that string. The hsubst command scans the string for instances of the following markup:

The <%...%> markup is replace by the string returned by the enclosed TCL code. The <$...> markup is replaced by the value of the named TCL variable. If no such TCL variable exists, the <$...> markup is simply removed.

The embedded TCL code is executed from top to bottom using the same interpreter and in the same variable namespace and context. So TCL code at the beginning of the content file can create variables that are used by code embedded later in the file.

Context Variables: D and Q

Before running hsubst to process the embedded TCL code, the TWS server first puts information about the HTTP request in two TCL array variables: D and Q. The D variable holds information gathered directly from the HTTP request header and the Q variable contains query parameters and cookies.

To see how this works, create a new dynamic content file named "ex2.tws" as follows:

<h1>Values for D:</h1>

<blockquote>
<% ::tws::parray D %>
</blockquote>

<h1>Values for Q:</h1>

<blockquote>
<% ::tws::parray Q %>
</blockquote>

Put the above file someplace where your TWS server can find it, then enter the appropriate URL into your web browser to get TWS to server the file. The result should be something like the following:

Values for D:

D(ACCEPT) = */*
D(ACCEPT-CHARSET) = iso-8859-1,*,utf-8
D(ACCEPT-ENCODING) = gzip
D(ACCEPT-LANGUAGE) = en
D(content) =
D(cookie) =
D(count) = 1
D(default-mime-type) = text/html
D(docroot) = /usr/home/drh/tws/bld/htdocs
D(document) = /usr/home/drh/tws/bld/htdocs/ex2.tws
D(handler) = fdynamic
D(HOST) = localhost
D(host) =
D(method) = GET
D(namespace) =
D(path) = /htdocs/ex2
D(prefix) = /htdocs
D(protocol) = HTTP/1.0
D(pwd) = /usr/home/drh/tws/bld/htdocs
D(query) =
D(realm) =
D(reply-content) = <h1>Values for D:</h1> <blockquote> <% ::tws::parray D
D(suffix) = /ex2
D(USER-AGENT) = BrowseX-1.2.3 Linux 2.2.18 (1.2.3 Linux 2.2.18)

Values for Q:

The ::tws::parray command is a special TWS variant of the standard TCL parray command for printing the contents of a TCL array. The standard parray command prints the contents of the array on standard output. The ::tws::parray variant returns a string that shows the contents of the array formatted in HTML. Otherwise, parray and ::tws::parray are the same.

So the "<% ::tws::parray D %>" markup gets replaced with a string of HTML that describes the content of the D array variable and "<% ::tws::parray Q %>" gets replaced with HTML that describes the contents of the Q array.

Look closely at the D array. Every attribute in the header of the original HTTP request is converted to capital letters and stored in the D array. Every element of the D array whose name is in all capitals comes directly from the HTTP request header and every line of the HTTP request header results in an entry in the D array. In the example above, you can see, for example, various ACCEPT codings that tell us what kinds of replies the browser will accept, we find a HOST attribute that tell the name of the host were the request originated, and there is a USER-AGENT attribute that tells us the the BrowseX web browser was used.

The elements of D with lower-case labels were inserted by TWS itself. The lower-case elements include information like the type of the request ("GET" in this example), and the name of the requested document at various stages of decoding. TWS also inserted decoded versions of cookies and query parameters.

Presumably you obtained the prior output by entering a URL like this:

http://localhost/ex2

In other words, you entered a URL without query parameters. The Q variable is designed to hold decoded query parameters, and since you had none, the Q array is empty. Now let's enter the same URL but add some query parameters to the end and see what changes. Try using URL like this:

http://localhost/ex2?a=1&op=add&b=25

Entering this URL results in the following output:

Values for D:

D(ACCEPT) = */*
D(ACCEPT-CHARSET) = iso-8859-1,*,utf-8
D(ACCEPT-ENCODING) = gzip
D(ACCEPT-LANGUAGE) = en
D(content) = a=1&op=add&b=25
D(cookie) =
D(count) = 1
D(default-mime-type) = text/html
D(docroot) = /usr/home/drh/tws/bld/htdocs
D(document) = /usr/home/drh/tws/bld/htdocs/ex2.tws
D(handler) = fdynamic
D(HOST) = localhost
D(host) =
D(method) = GET
D(namespace) =
D(path) = /htdocs/ex2
D(prefix) = /htdocs
D(protocol) = HTTP/1.0
D(pwd) = /usr/home/drh/tws/bld/htdocs
D(query) = a 1 op add b 25
D(realm) =
D(reply-content) = <h1>Values for D:</h1> <blockquote> <% ::tws::parray D
D(suffix) = /ex2
D(USER-AGENT) = BrowseX-1.2.3 Linux 2.2.18 (1.2.3 Linux 2.2.18)

Values for Q:

Q(a) = 1
Q(b) = 25
Q(op) = add

The differences are that the D(content) and D(query) variables and the Q array now all have values. As you can see the D(content) variable holds the exact text of the query string. The URL above used the GET method, but D(content) would also hold the query string if the POST method were used. The D(query) variable holds the query string decoded into a TCL list. Alternating elements of D(query) are parameters and values. The Q variable is just the value of D(query) decoded even further. There is one element in the Q array for each query parameter and the value of that element is the value of the query parameter. Actually, the Q array is set up to hold both query parameters and cookies both. We did not see any cookies in the example above, but if there had been some, they would have appeared as a list in D(cookie) and as additional values in the Q array.

The D and Q arrays contain everything there is to know about the incoming HTTP request in a preparsed and convenient-to-use format. Basically, D and Q take the place of environment variables in CGI, except that D and Q are much easier to use.

Using The Built-In Database Engine

TWS includes the SQLite SQL database library. The SQLite library allows TCL code to access GDBM database files using SQL syntax.

For additional information on using SQLite, see the SQLite Documentation.

To Be Continued...


Contents This page was last modified on 2001/02/18 18:47:32 GMT