Apache Rivet

The Rivet Team

The Apache Software Foundation

Table of Contents

Introduction to Apache Rivet
Apache Rivet Installation
Rivet Apache Directives
Rivet Tcl Commands and Variables
var — get the value of a form variable.
upload — handle a file uploaded by a client.
load_response — load form variables into an array.
load_headers — get client request's headers.
load_cookies — get any cookie variables sent by the client.
load_env — get the request's environment variables.
env — Loads a single "environmental variable" into a Tcl variable.
include — includes a file into the output stream without modification.
parse — parses a Rivet template file.
headers — set and parse HTTP headers.
makeurl — construct url's based on hostname, port.
cookie — get and set cookies.
clock_to_rfc850_gmt — create a rfc850 time from [clock seconds].
html — construct html tagged text.
incr0 — increment a variable or set it to 1 if nonexistant.
parray — Tcl's parray with html formatting.
abort_page — Stops outputing data to web page, similar in purpose to PHP's die command.
no_body — Prevents Rivet from sending any content.
escape_string — convert a string into escaped characters.
escape_sgml_chars — escape special SGML characters in a string.
escape_shell_command — escape shell metacharacters in a string.
unescape_string — unescape escaped characters in a string.
apache_log_error — log messages to the Apache error log
apache_table — access and manipulate Apache tables in the request structure.
Examples and Usage
Rivet Tcl Packages
DIO - Database Interface Objects
DIO — Database Interface Objects
DIODisplay - Database Interface Objects Display Class
DIODisplay — Database Interface Objects Display Class
Session Package
Introduction
Requirements
Preparing To Use It
Example Usage
Using Sessions From Your Code
Session Configuration Options
Session Methods
Getting Additional Randomness From The Entropy File
Form: An HTML Form Fields Generation Utility
Introduction
form — a Tcl command object for creating HTML forms
Calendar Package
Introduction
Calendar — Utility class the builds and prints a calendar table
XmlCalendar — Prints XML formatted calendar tables
HtmlCalendar — Concrete class derived from XmlCalendar
Resources - How to Get Help
Mailing Lists
Newsgroup
Web Sites
Bug Tracking System
IRC
Editing Rivet Template Files
Rivet Internals
Initialization
RivetChan
The global Command
Page Parsing, Execution and Caching
Debugging Rivet and Apache
Upgrading from mod_dtcl or NeoWebScript
mod_dtcl
NeoWebScript

List of Examples

1. Hello World
2. Generate a Table
3. Variable Access
4. File Upload
5. File Download
6. XML Messages and Ajax

Document revision: $Revision: 959821 $, last modified 2010-07-20 21:49:04+02:00$ by $Author: mxmanghi $.


Introduction to Apache Rivet

Apache Rivet is a system for creating dynamic web content via a programming language integrated with Apache Web Server. It is designed to be fast, powerful and extensible, consume few system resources, be easy to learn, and to provide the user with a platform that can also be used for other programming tasks outside the web (GUI's, system administration tasks, text processing, database manipulation, XML, and so on). In order to meet these goals, we have chosen the Tcl programming language to combine with the Apache Web Server.

In this manual, we aim to help get you started, and then writing productive code as quickly as possible, as well as giving you ideas on how to best take advantage of Rivet's architecture to create different styles of web site.

This documentation is a work in progress, and, like everything else about Apache Rivet, it is Free Software. If you see something that needs improving, and have ideas or suggestions, don't hesitate to let us know. If you want to contribute directly, better yet!


Apache Rivet Installation

  1. Check Dependencies

    To install Rivet, you will need Tcl 8.4 or greater. Rivet can be compiled to work with either Apache 1.3.xx or Apache 2.2.xx server. It is known to run on Linux, FreeBSD, OpenBSD, Solaris and HPUX. Windows NT is also possible - please see the directions in the distribution.

  2. Get Rivet

    Download the sources at http://tcl.apache.org/rivet/download.html. Currently the only way to obtain Rivet. In the future, we hope to have a FreeBSD port, Debian package, RPM's, and windows binaries.

  3. Install Tcl

    If you don't have Tcl already, you need it! If you already have it, you should just be able to use your system Tcl as long as it is recent. You can tell Rivet build scripts where Tcl is via the --with-tcl option to configure (see below).

  4. Get and Install Apache Sources

    Rivet needs some Apache include (.h) files in order to build. The easiest way to get them is to download the source code of the Apache web server, although some systems (Debian GNU/Linux for example) make it possible to install only the headers and other development files. If you intend to build Rivet statically (compiled into the Apache web server instead of loaded dynamically), you definitely need the sources. We recommend that you build Rivet as a loadable shared library, for maximum flexibility, meaning that you also build Apache to be able to load modules. Other than that, the default Apache install is fine. We will tell Rivet where it is located via the --with-apxs option to configure (see below).

    The source code for the Apache web server may be found by following the links here: http://httpd.apache.org/.

  5. Uncompress Sources

    We will assume that you have Apache installed at this point. You must uncompress the Rivet sources in the directory where you wish to compile them.

    gunzip rivet-X.X.X.tar.gz
    tar -xvf rivet-X.X.X.tar.gz

  6. Building Rivet

    1. On Linux or Unix systems, Rivet uses the standard ./configure ; make ; make install technique.

      There are several rivet specific options to configure that might be useful (or needed):

      --with-tcl
      This points to the directory where the tclConfig.sh file is located.
      --with-tclsh
      This points to the location of the tclsh executable.
      --with-apxs
      The location of the apxs program that provides information about the configuration and compilation options of Apache modules.
      --with-apache-version=[1|2]
      This option tells the build scripts whether you want to build Rivet for the Apache 1.x or Apache 2.x server.
      --with-apache-include[=DIR]
      Locates the Apache include files on your computer, if they're not in standard directory.
      --enable-version-display=[yes|no]
      This option enables Rivet to display its version in the logfiles when Apache is started. The default is to keep Rivet version hidden.
      --with-rivet-target-dir=DIR
      This option tells the install script where Rivet's Tcl packages have to be copied.

      Example: configuring the build system to compile Rivet for an apache 2.x server, using tcl8.5 and specifying a custom name for the apxs program.

      ./configure --with-tcl=/usr/lib/tcl8.5/ --with-tclsh=/usr/bin/tclsh8.5 \
      	    --with-apxs=/usr/bin/apxs2 --with-apache=/usr --with-apache-version=2
      	
    2. Run make

      At this point, you are ready to run make, which should run to completion without any errors (a warning or two is ok, generally).

    3. Install

      Now, you are ready to run the make install to install the resulting files. This should copy the shared object (like mod_rivet.so, if one was successfully created, into Apache's libexec directory, as well as install some support scripts and various code.

  7. Apache Configuration Files

    Rivet is relatively easy to configure - we start off by adding the module itself:

    LoadModule rivet_module	/usr/lib/apache2/modules/mod_rivet.so
    	

    This tells Apache to load the Rivet shared object, wherever it happens to reside on your file system. Now we have to tell Apache what kind of files are "Rivet" files and how to process them:

    AddType application/x-httpd-rivet .rvt
    AddType application/x-rivet-tcl .tcl

    These tell Apache to process files with the .rvt and .tcl extensions as Rivet files.

    The characters encoding can be changed using the header type command, but you can also change the default charset for the whole site:

    AddType 'application/x-httpd-rivet;charset=utf-8' rvt

    All the pages generated by Rivet on this site will be sent with a Content-Type:'text/html;charset=utf-8' header.

    You may also wish to use Rivet files as index files for directories. In that case, you would do the following:

    DirectoryIndex index.html index.htm index.shtml index.cgi index.tcl index.rvt

    For other directives that Rivet provides for Apache configuration, please see the section called “Rivet Apache Directives”.


Rivet Apache Directives

These directives are used within the Apache httpd server configuration files to modify Apache Rivet's behavior. Their precedence is as follows: RivetDirConf, RivetUserConf, RivetServerConf, meaning that DirConf will override UserConf, which will in turn override ServerConf.

RivetServerConf (CacheSize | GlobalInitScript | ChildInitScript | ChildExitScript | BeforeScript | AfterScript | ErrorScript | UploadDirectory | UploadMaxSize | UploadFilesToVar | SeparateVirtualInterps | HonorHeaderOnlyRequests)
RivetServerConf specifies a global option that is valid for the whole server. If you have a virtual host, in some cases, the option specified in the virtualhost takes precedence over the 'global' version.
CacheSize ?size?
Sets the size of the internal page cache, where size is the number of byte-compiled pages to be cached for future use. Default is MaxRequestsPerChild / 5, or 50, if MaxRequestsPerChild is 0.
This option is completely global, even when using separate, per-virtual host interpreters.
GlobalInitScript ?script?
Tcl script that is run when each interpreter is initialized. script is an actual Tcl script, so to run a file, you would do:
RivetServerConf GlobalInitScript "source /var/www/foobar.tcl"
This option is ignored in virtual hosts.
ChildInitScript ?script?
Script to be evaluated when each Apache child process is initialized. This is the recommended place to load modules, create global variables, open connections to other facilities (such as databases) and so on.
In virtual hosts, this script is run in addition to any global childinitscript.
ChildExitScript ?script?
Script to be evaluated when each Apache child process exits. This is the logical place to clean up resources created in ChildInitScript, if necessary.
In virtual hosts, this script is run in addition to any global childexitscript.
BeforeScript ?script?
Script to be evaluated before each server parsed (.rvt) page. This can be used to create a standard header, for instance. It could also be used to load code that you need for every page, if you don't want to put it in a GlobalInitScript ChildInitScript when you are first developing a web site.
[Note]Note
This code is evaluated at the global level, not inside the request namespace where pages are evaluated.
In virtual hosts, this option takes precedence over the global setting.
AfterScript ?script?
Script to be called after each server parsed (.rvt) page.
In virtual hosts, this option takes precedence over the global setting.
ErrorScript ?script?
When Rivet encounters an error in a script, it constructs an HTML page with some information about the error, and the script that was being evaluated. If an ErrorScript is specified, it is possible to create custom error pages. This may be useful if you want to make sure that users never view your source code.
In virtual hosts, this option takes precedence over the global setting.
UploadDirectory ?directory?
Directory to place uploaded files.
In virtual hosts, this option takes precedence over the global setting.
UploadMaxSize ?size?
Maximum size for uploaded files.
In virtual hosts, this option takes precedence over the global setting.
UploadFilesToVar (yes | no)
This option controls whether it is possible to upload files to a Tcl variable. If you have a size limit, and don't have to deal with large files, this might be more convenient than sending the data to a file on disk.
SeparateVirtualInterps (yes | no)
If on, Rivet will create a separate Tcl interpreter for each Apache virtual host. This is useful in an ISP type situation where it is desirable to separate clients into separate interpreters, so that they don't accidentally interfere with one another.
This option is, by nature, only available at the global level.
HonorHeaderOnlyRequests (yes | no)
If a HEAD requests is issued by the client Rivet detects this case and sends back to the client a standard header response. If the real header has to be examined (e.g. for debugging) you can turn this options on.
This option is, by nature, only available at the global level
RivetDirConf (BeforeScript | AfterScript | ErrorScript | UploadDirectory)
These options are the same as for RivetServerConf, except that they are only valid for the directory where they are specified, and its subdirectories. It may be specified in Directory sections.
RivetUserConf (BeforeScript | AfterScript | ErrorScript | UploadDirectory)
These options are the same as for RivetServerConf, except that they are only valid for the directory where they are specified, and its subdirectories.

Rivet Tcl Commands and Variables

Name

var, var_qs, var_post — get the value of a form variable.

Synopsis

var (get | list | exists | number | all)
var_qs (get | list | exists | number | all)
var_post (get | list | exists | number | all)

Description

The var command retrieves information about GET or POST variables sent to the script via client request. It treats both GET and POST variables the same, regardless of their origin. Note that there are two additional forms of var: var_qs and var_post. These two restrict the retrieval of information to parameters arriving via the querystring (?foo=bar&bee=bop) or POSTing, respectively.

var get ?varname? ??default??
Returns the value of variable varname as a string (even if there are multiple values). If the variable doesn't exist as a GET or POST variable, the ?default? value is returned, otherwise "" - an empty string - is returned.
var list ?varname?
Returns the value of variable varname as a list, if there are multiple values.
var exists ?varname?
Returns 1 if varname exists, 0 if it doesn't.
var number
Returns the number of variables.
var all
Return a list of variable names and values.

See Example 3, “Variable Access”.


Name

upload — handle a file uploaded by a client.

Synopsis

upload (channel | save | data | exists | size | type | filename)

Description

The upload command is for file upload manipulation. See the relevant Apache Directives to further configure the behavior of this Rivet feature.

upload channel ?uploadname?
When given the name of a file upload uploadname, returns a Tcl channel that can be used to access the uploaded file.
upload save ?uploadname? ?filename?
Saves the uploadname in the file filename.
upload data ?uploadname?
Returns data uploaded to the server. This is binary clean - in other words, it will work even with files like images, executables, compressed files, and so on.
upload exists ?uploadname?
Returns true if an upload named ?uploadname? exists. This can be used in scripts that are meant to be run by different forms that send over uploads that might need specific processing.
upload size ?uploadname?
Returns the size of the file uploaded.
upload type
If the Content-type is set, it is returned, otherwise, an empty string.
upload filename ?uploadname?
Returns the filename on the remote host that uploaded the file.
upload tempname ?uploadname?
Returns the name of the temporary file on the local host that the file was uploaded into.
upload names
Returns the variable names, as a list, of all the files uploaded.

See upload.


Name

load_response — load form variables into an array.

Synopsis

load_response ?arrayName?

Description

Load any form variables passed to this page into an array. If load_response is called without arguments the array response is created in the scope of the caller. If the variables var1,var2,var3... having values val1,val2,val3... are passed to the page, the resulting array will be a collection mapping var1,var2,var3... to their corresponding values. load_response was inspired by the same NeoWebScript procedure in the way it deals with multiple assignments: if a variable is assigned more than once the corresponding array element will be a list of the values for the variable. This can be useful in the case of forms with checkbox options that are given the same name. Calling load_response several times for the same array results in adding more values to the array at every call. When needed it is left to the caller to empty the array between two subsequent calls.


Name

load_headers — get client request's headers.

Synopsis

load_headers ?array_name?

Description

Load the headers that come from a client request into the provided array name, or use headers if no name is provided.


Name

load_cookies — get any cookie variables sent by the client.

Synopsis

load_cookies ?array_name?

Description

Load the array of cookie variables into the specified array name. Uses array cookies by default.


Name

load_env — get the request's environment variables.

Synopsis

load_env ?array_name?

Description

Load the array of environment variables into the specified array name. Uses array ::request::env by default.

As Rivet pages are run in the ::request namespace, it isn't necessary to qualify the array name for most uses - it's ok to access it as env.


Name

env — Loads a single "environmental variable" into a Tcl variable.

Synopsis

env ?varName?

Description

If it is only necessary to load one environmental variable, this command may be used to avoid the overhead of loading and storing the entire array.


Name

include — includes a file into the output stream without modification.

Synopsis

include ?filename_name?

Description

Include a file without parsing it for processing tags <? and ?>. This is the best way to include an HTML file or any other static content.


Name

parse — parses a Rivet template file.

Synopsis

parse ?filename?

Description

Like the Tcl source command, but also parses for Rivet <? and ?> processing tags. Using this command, you can use one .rvt file from another.


Name

headers — set and parse HTTP headers.

Synopsis

headers (set | redirect | add | type | numeric)

Description

The headers command is for setting and parsing HTTP headers.

headers set ?headername? ?value?
Set arbitrary header names and values.
headers redirect ?uri?
Redirect from the current page to a new URI. Must be done in the first block of TCL code.
headers add ?headername? ?value?
Add text to header headername.
headers type ?content-type?
This command sets the Content-type header returned by the script, which is useful if you wish to send content other than HTML with Rivet - PNG or jpeg images, for example.
headers numeric ?response code?
Set a numeric response code, such as 200, 404 or 500.

Name

makeurl — construct url's based on hostname, port.

Synopsis

makeurl ?filename?

Description

Create a self referencing URL from a filename. For example:

makeurl /tclp.gif

returns http://[hostname]:[port]/tclp.gif. where hostname and port are the hostname and port of the server in question.


Name

cookie — get and set cookies.

Synopsis

cookie ?set? ?cookieName? ??cookiValue?? ?-days expireInDays? ?-hours expireInHours? ?-minutes expireInMinutes? ?-expires Wdy, DD-Mon-YYYY HH:MM:SS GMT? ?-path uriPathCookieAppliesTo? ?-secure 1/0?
cookie ?get? ?cookieName?

Description

cookie gets or sets a cookie. When you get a cookie, the command returns the value of the cookie, or an empty string if no cookie exists.


Name

clock_to_rfc850_gmt — create a rfc850 time from [clock seconds].

Synopsis

clock_to_rfc850_gmt ?seconds?

Description

Convert an integer-seconds-since-1970 click value to RFC850 format, with the additional requirement that it be GMT only.


Name

html — construct html tagged text.

Synopsis

html ?string? ?arg...?

Description

Print text with the added ability to pass HTML tags following the string. Example:

html "Test" b i

produces: <b><i>Test</i></b>


Name

incr0 — increment a variable or set it to 1 if nonexistant.

Synopsis

incr0 ?varname? ?num?

Description

Increment a variable varname by num. If the variable doesn't exist, create it instead of returning an error.


Name

parray — Tcl's parray with html formatting.

Synopsis

parray ?arrayName? ??pattern??

Description

An html version of the standard Tcl parray command. Displays the entire contents of an array in a sorted, nicely-formatted way. Mostly used for debugging purposes.


Name

abort_page — Stops outputing data to web page, similar in purpose to PHP's die command.

Synopsis

abort_page

Description

This command flushes the output buffer and stops the Tcl script from sending any more data to the client. A normal Tcl script might use the exit command, but that cannot be used in Rivet without actually exiting the apache child process!


Name

no_body — Prevents Rivet from sending any content.

Synopsis

no_body

Description

This command is useful for situations where it is necessary to only return HTTP headers and no actual content. For instance, when returning a 304 redirect.


Name

escape_string — convert a string into escaped characters.

Synopsis

escape_string ?string?

Description

Scans through each character in the specified string looking for special characters, escaping them as needed, mapping special characters to a quoted hexadecimal equivalent, returning the result.

This is useful for quoting strings that are going to be part of a URL.

[Note]Note
You must require the Rivet package in order to gain access to this command

Name

escape_sgml_chars — escape special SGML characters in a string.

Synopsis

escape_sgml_chars ?string?

Description

Scans through each character in the specified string looking for any special (with respect to SGML, and hence HTML) characters from the specified string, and returns the result. For example, the right angle bracket is escaped to the corrected ampersand gt symbol.

[Note]Note
You must require the Rivet package in order to gain access to this command

Name

escape_shell_command — escape shell metacharacters in a string.

Synopsis

escape_shell_command ?string?

Description

Scans through each character in the specified string looking for any shell metacharacters, such as asterisk, less than and greater than, parens, square brackets, curly brackets, angle brackets, dollar signs, backslashes, semicolons, ampersands, vertical bars, etc.

For each metacharacter found, it is quoted in the result by prepending it with a backslash, returning the result.

[Note]Note
You must require the Rivet package in order to gain access to this command

Name

unescape_string — unescape escaped characters in a string.

Synopsis

unescape_string ?string?

Description

Scans through each character in the specified string looking for escaped character sequences (characters containing a percent sign and two hexadecimal characters, unescaping them back to their original character values, as needed, also mapping plus signs to spaces, and returning the result.

This is useful for unquoting strings that have been quoted to be part of a URL.

[Note]Note
You must require the Rivet package in order to gain access to this command

Name

apache_log_error — log messages to the Apache error log

Synopsis

apache_log_error ?priority? ?message?

Description

The apache_log_error command logs a message to the Apache error log, whose name and location have been set by the ErrorLog directive.

Priority must be one of debug, info, notice, warning, err, crit, alert, or emerg.


Name

apache_table — access and manipulate Apache tables in the request structure.

Synopsis

apache_table (get | set | exists | unset | names | array_get | clear)

Description

The apache_table command is for accessing and manipulating Apache tables in the request structure.

The table name must be one of notes, headers_in, headers_out, err_headers_out, or subprocess_env.

apache_table get ?tablename? ?key?
When given the name of an Apache table tablename and the name of a key tablename, returns the value of the key in the table, or an empty string.
apache_table set ?tablename? ?key? ?value?
apache_table set ?tablename? ?list?
Stores the value in the table tablename under the key key.
For the list form, list contains a list of zero or more pairs of key-value pairs to be set into the table tablename.
apache_table exists ?tablename? ?key?
Returns 1 if the specified key, key, exists in table tablename, else 0.
apache_table unset ?tablename? ?key?
Removes the key-value pair referenced by key from the table tablename.
apache_table names ?tablename?
Returns a list of all of the keys present in the table tablename.
apache_table array_get ?tablename?
Returns a list of key-value pairs from the table tablename.
apache_table clear ?tablename?
Clears the contents of the specified table.

Examples and Usage

Some examples of Rivet usage follow. Some prior Tcl knowledge is assumed. If you don't know much Tcl, don't worry, it's easy, and there are some good resources available on the web that will get you up to speed quickly. Go to the web sites section and have a look.

Example 1. Hello World

As with any tool, it's always nice to see something work, so let's create a small "Hello World" page.

Assuming you have Apache configured correctly, create a file called hello.rvt where Apache can find it, with the following content:

<?
puts "Hello World"
?>

If you then access it with your browser, you should see a blank page with the text "Hello World" (without the quotes) on it.


Example 2. Generate a Table

In another simple example, we dynamically generate a table:

<? puts "<table>\n"
for {set i 1} { $i <= 8 } {incr i} {
    puts "<tr>\n"
    for {set j 1} {$j <= 8} {incr j} {
        set num [ expr $i * $j * 4 - 1]
        puts [ format "<td bgcolor=\"%02x%02x%02x\" > $num $num $num </td>\n" \
		   $num $num $num ]
    }
    puts "</tr>\n"
}
puts "</table>\n" ?>

If you read the code, you can see that this is pure Tcl. We could take the same code, run it outside of Rivet, and it would generate the same HTML!

The result should look something like this:


Example 3. Variable Access

Here, we demonstrate how to access variables set by GET or POST operations.

Given an HTML form like the following:

     <form action="vars.rvt">
      <table>
	<tbody>
	  <tr>
	    <td><b>Title:</b></td>
	    <td><input name="title"></td>
	  </tr>
	  <tr>
	    <td><b>Salary:</b></td>
	    <td><input name="salary"></td>
	  </tr>
	  <tr>
	    <td><b>Boss:</b></td>
	    <td><input name="boss"></td></tr>
	  <tr>
	    <td><b>Skills:</b></td>
	    <td>
	      <select name="skills" multiple="multiple">
		<option>c</option>
		<option>java</option>
		<option>Tcl</option>
		<option>Perl</option>
	      </select>
	    </td>
	  </tr>
	  <tr>
	    <td><input type="submit"></td>
	  </tr>
	</tbody>
      </table>
    </form>

We can use this Rivet script to get the variable values:

<?
set errlist {}
if { [var exists title] } {
    set title [var get title]
} else {
    set errlist "You need to enter a title"
}

if { [var exists salary] } {
    set salary [var get salary]
    if { ! [string is digit $salary] } {
	lappend errlist "Salary must be a number"
    }
} else {
    lappend errlist "You need to enter a salary"
}

if { [var exists boss] } {
    set boss [var get boss]
} else {
    set boss "Mr. Burns"
}

if { [var exists skills] } {
    set skills [var list skills]
} else {
    lappend errlist "You need to enter some skills"
}

if { [llength $errlist] != 0 } {
    foreach err $errlist {
	puts "<b> $err </b>"
    }
} else {
    puts "Thanks for the information!"
    ?>
    <table>
      <tbody>
	<tr>
	  <td><b>Title:</b></td>
	  <td><? puts $title ?></td>
	</tr>
	<tr>
	  <td><b>Boss:</b></td>
	  <td><? puts $boss ?></td>
	</tr>
	<tr>
	  <td><b>Salary:</b></td>
	  <td><? puts $salary ?></td>
	</tr>
	<tr>
	  <td><b>Skills:</b></td>
	  <td><? puts $skills ?></td>
	</tr>
      </tbody>
    </table>
    <?
}
?>

The first statement checks to make sure that the boss variable has been passed to the script, and then does something with that information. If it's not present, an error is added to the list of errors.

In the second block of code, the variable salary is fetched, with one more error check - because it's a number, it needs to be composed of digits.

The boss variable isn't required to have been sent - we set it to "Mr. Burns" if it isn't among the information we received.

The last bit of variable handing code is a bit trickier. Because skills is a listbox, and can potentially have multiple values, we opt to receive them as a list, so that at some point, we could iterate over them.

The script then checks to make sure that errlist is empty and outputting a thankyou message. If errlist is not empty, the list of errors it contains is printed.


Example 4. File Upload

The upload command endows Rivet with an interface to access files transferred over http as parts of a multipart form. The following HTML in one file, say, upload.html creates a form with a text input entry. By clicking the file chooser button the file browser shows up and the user selects the file to be uploaded (the file path will appear in the text input). In order to make sure you're uploading the whole file you must combine the action of the enctype and method attributes of the <form...> tag in the way shown in the example. Failure to do so would result in the client sending only the file's path, rather than the actual contents.

<form action="foo.rvt" enctype="multipart/form-data" method="post">
<input type="file" name="MyUpload"></input>
<input type="submit" value="Send File"></input>
</form>

In the script invoked by the form (upload.rvt) upload ?argument ...? commands can be used to manipulate the various files uploaded.

<?
upload save MyUpload /tmp/uploadfiles/file1
puts "Saved file [upload filename MyUpload] \
	([upload size MyUpload] bytes) to server"
?>

Don't forget that the apache server must have write access to the directory where files are being created. The Rivet Apache directives have a substantial impact on the upload process, you have to carefully read the docs in order to set the appropriate directives values that would match your requirements.

It is also important to understand that some upload commands are effective only when used in a mutually exclusive way. Apache stores the data in temporary files which are read by the upload save ?upload name? ?filename? or by the upload data ?upload name? command. Subsequent calls to these 2 commands using the same ?upload name? argument will return no data on the second call. Likewise upload channel ?upload name? will return a Tcl file channel that you can use in regular Tcl scripts only if you haven't already read the data, for example with a call to the upload data ?upload name? command.


Example 5. File Download

In general setting up a data file for being sent over http is as easy as determining the file's URI and letting Apache's do all that is needed. If this approach fits your design all you have to do is to keep the downloadable files somewhere within Apache's DocumentRoot (or in any of the directories Apache has right to access).

When a client sends a request for a file, Apache takes care of determining the filetype, sends appropriate headers to the client and then the file content. The client is responsible for deciding how to handle the data accordingly to the "content-type" headers and its internal design. For example when browsers give up trying to display a certain "content-type" they display a download dialog box asking for directions from the user.

Rivet can help if you have more sofisticated needs. For instance you may be developing an application that uses webpages to collect input data. This information might be passed on to scripts or programs for processing. In this case a real file representing the data doesn't exist and the content is generated on demand by the server. In other circumstances you may need to dynamically inhibit the download of specific files and hide them away, Your scripts may expunge from the pages every link to these files (your pages are dynamic, aren't they?) and move them out of way, but it looks like a cumbersome solution.

Putting Tcl and Rivet in charge of the whole download mechanism helps in building cleaner and safer approaches to the download problem.

In this example a procedure checks for the existence of a parameter passed in by the browser. The parameter is the name (without extension) of a pdf file. Pdf files are stored in a directory whose path is in the pdf_repository variable.

This code is reported as an example of how to control the protocol using the headers command.

# Code example for the transmission of a pdf file. 

if {[var exists pdfname]} {
    set pdfname [var get pdfname]

# let's build the full path to the pdf file. The 'pdf_repository'
# directory must be readable by the apache children

    set pdf_full_path [file join $pdf_repository ${pdfname}.pdf]
    if {[file exists $pdf_full_path]} {

# Before the file is sent we inform the client about the file type and
# file name. The client can be proposed a filename different from the
# original one. In this case, this is the point where a new file name
# must be generated.

	headers type			"application/pdf"
	headers add Content-Disposition "attachment; filename=${pdfname}.pdf"
	headers add Content-Description "PDF Document"

# The pdf is read and stored in a Tcl variable. The file handle is
# configured for a binary read: we are just shipping raw data to a
# client. The following 4 lines of code can be replaced by any code
# that is able to retrieve the data to be sent from any data source
# (e.g. database, external program, other Tcl code)

	set paper	    [open $pdf_full_path r]
	fconfigure	    $paper -translation binary
	set pdf		    [read $paper]
	close $paper

# Now we got the data: let's tell the client how many bytes we are
# about to send (useful for the download progress bar of a dialog box)

	headers add Content-Length  [string length $pdf]

# Let's send the actual file content

	puts $pdf
    } else {
	source pdf_not_found_error.rvt
    }
} else {
    source parameter_not_defined_error.rvt
}

Before the pdf is sent the procedure sets the Content-Type, Content-Disposition, Content-Description and Content-Length headers to inform the client about the file type, name and size. Notice that in order to set the Content-Type header Rivet uses a specialiezed form of the headers command. Headers must be sent before data gets sent down the output channel. Messing with this prescription causes an error to be raised (in fact the protocol itself is been violated)

More information about the meaning of the mime headers in the http context can be found at http://www.w3.org/Protocols/rfc2616/rfc2616.html


Example 6. XML Messages and Ajax

The headers command is crucial for generating XML messages that have to be understood by JavaScript code used in Ajax applications.

Ajax is a web programming technique that heavily relies on the abilty of a web browser to run in backround JavaScript functions. JavaScript functions can be run as callbacks of events generated by a user interaction but they can also react to other I/O events, for example network events. Modern browsers endow JavaScript with the ability to build http GET/POST requests to be sent to a remote webserver. Generally these requests refer to scripts (e.g. Tcl scripts run by Rivet) which inherit as variables the arguments encoded in the request. The output produced by these scripts is sent back to the browser where callbacks functions extract information and hand it down to functions that directly manipulate a page's DOM. Therefore through Ajax becomes possible to build web applications that are more responsive and flexible: instead of going through the cycle of request-generation-transfer-display of a whole page, Ajax scripts request from a webserver only the essential data to be displayed. Ajax emphasizes the requirement of separation between data and user interface, saves the server from sending over the same html code and graphics if only a fraction of a page has to be updated, allows the programmer to design flexible solutions for complex forms and makes possible to find new innovative approaches to simple problems (e.g. Google tips that show up as you type in a query). A downside of this approach is the large number of complexities, subtleties and incompatibilities that still exist in the way different versions of popular browsers handle the DOM elements of a page.

JavaScript can handle the communication between client and server through an instance of a specialized object. For quite a long time 2 approaches existed, the non-IE world (Firefox,Safari,Opera...) used the XMLHttpRequest class to create this object, whereas IE (before IE7) used the ActiveXObject class. With the release of IE7 Microsoft introduced native support for XMLHttpRequest class objects thus enabling programmers with a unique method for the development of dynamic pages.

By creating an instance of this class a POST or GET request can be sent to the server and the response is stored in a property ('returnedText') of the communication object. It's become widely customary to encode these responses in XML messages. You can invent your own message structure (either based on XML or anything else), but one has to be aware that if the http headers are properly set and the message returned to the client is a well formed XML fragment, also the property XMLResponse is assigned with a reference to an object that represents the DOM of the XML response. By means of the XML W3C DOM interface the programmer can easily manipulate the data embedded in the XML message.

In this example a Rivet script initializes an array with the essential data regarding a few of the major composers of the european music. This array plays the role of a database. The script sends back to the client two types of responses: a catalog of the composers or a single record of a composer.

#
# Ajax query servelet: a pseudo database is built into the dictionary 'composers' with the
# purpose of emulating the role of a real data source. 
# The script answers with  2 types of responses: a catalog of the record ids and a database 
# entry matching a given rec_id. The script obviously misses the error handling and the
# likes. Just an example to see rivet sending xml data to a browser. The full Tcl, JavaScript
# and HTML code are available from http://people.apache.org/~mxmanghi/rivet-ajax.tar.gz

# This example requires Tcl8.5 or Tcl8.4 with package 'dict' 
# (http://pascal.scheffers.net/software/tclDict-8.5.2.tar.gz)
# 

# A pseudo database. rec_id matches a record in the db

set composers [dict create  1 {first_name Claudio middle_name "" last_name Monteverdi	\
			       lifespan 1567-1643 era Renaissance/Baroque}		\
			    2 {first_name Johann middle_name Sebastian last_name Bach	\
			       lifespan 1685-1750 era Baroque }				\
			    3 {first_name Ludwig middle_name "" last_name "van Beethoven" \
			       lifespan 1770-1827 era Classical/Romantic}		\
			    4 {first_name Wolfgang middle_name Amadeus last_name Mozart	\
				lifespan 1756-1791 era Classical }			\
			    5 {first_name Robert middle_name "" last_name Schumann	\
				lifespan 1810-1856 era Romantic} ]

# we use the 'load' argument in order to determine the type of query
#
# load=catalog:		    we have to return a list of the names in the database
# load=composer&res_id=<id>: the script is supposed to return the record
#			    having <id> as record id

if {[var exists load]} {

# the xml declaration is common to every message (error messages included)

    set xml "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
    switch [var get load] {
	catalog {
	    append xml "<catalog>\n"
	    foreach nm [dict keys $composers] {
		set first_name [dict get $composers $nm first_name]
		set middle_name [dict get $composers $nm middle_name]
		set last_name  [dict get $composers $nm last_name]
	        append xml "    <composer key=\"$nm\">$first_name "
		if {[string length [string trim $middle_name]] > 0} {
		    append xml "$middle_name "
		}
		append xml "$last_name</composer>\n"
	    }
	    append xml "</catalog>\n"
	}
	composer {
	    append xml "<composer>\n"
	    if {[var exists rec_id]} {
		set rec_id [var get rec_id]
		if {[dict exists $composers $rec_id]} {

		    foreach {k v} [dict get $composers $rec_id] {
			append xml "<$k>$v</$k>\n"
		    }

		}
	    }
	    append xml "</composer>\n"
	}
    }

# we have to tell the client this is an XML message. Failing to do so
# would result in an XMLResponse property set to null

    headers type "text/xml"
    headers add Content-Length [string length $xml]
    puts $xml
}

For sake of brevity the JavaScript and HTML will not listed here. They can be downloaded (along with the Tcl script) stored in the rivet-ajax.tar.gz archive. By simply opening this tar archive in a directory accessible by your apache server and pointing your browser to the rivetService.html page you should see a page with a drop-down list. Every time a different name is picked from the list a new query is sent and logged in the apache access.log file, even though the html is never reloaded.



Rivet Tcl Packages

In addition to the core Apache module, Rivet provides a number of Tcl packages that include potentially useful code.

  • commserver is a package providing a server that can be used for IPC. Still experimental. Requires the comm package from tcllib.
  • dio is a database abstraction layer.
  • dtcl is a compatibility package for mod_dtcl applications.
  • form - for creating forms.
  • rivet - some additional, useful routines.
  • tclrivet

DIO - Database Interface Objects

Name

DIO — Database Interface Objects

Synopsis

::DIO::handle interface ?objectName? (-option | option | -option | option | ...)

Description

DIO is designed to be a generic, object-oriented interface to SQL databases. Its main goal is to be as generic as possible, but since not all SQL databases support the exact same syntaxes, keeping code generic between databases is left to the abilities of the programmer. DIO simply provides a way to keep the Tcl interface generic.

interface - The name of the database interface. Currently supported interfaces are Postgresql and Mysql.

If objectName is specified, DIO creates an object of that name. If there is no objectName given, DIO will automatically generate a unique object ID

Options

-host ?hostname?
The hostname of the computer to connect to. If none is given, DIO assumes the local host.
-port ?portNumber?
The port number to connect to on hostname.
-user ?username?
The username you wish to login to the server as.
-pass ?password?
The password to login to the server with.
-db ?database?
The name of the database to connect to.
-table ?tableName?
The default table to use when using built-in commands for storing and fetching.
-keyfield ?keyFieldname?
The default field to use as the primary key when using built-in commands for storing and fetching.
-autokey (1 | 0)
If this option is set to 1, DIO will attempt to determine an automatic key for keyField when storing and fetching. In most databases, this requires that the sequence also be specified. In the case of MySQL, where sequences do not exist, autokey must be used in conjunction with a table which has a field specified as AUTO.
-sequence ?sequenceName?
If DIO is automatically generating keys, it will use this sequence as a means to gain a unique number for the stored key.

DIO Object Commands

objectName ?array? ?request?
Execute request as a SQL query and create an array from the first record found. The array is set with the fields of the table and the values of the record found.
objectName ?autokey? (value | boolean)
Return the current autokey value. If value is specified, it sets a new value for the autokey option.
objectName ?close?
Close the current database connection. This command is automatically called when the DIO object is destroyed.
objectName ?count?
Return a count of the number of rows in the specified (or current) table.
objectName ?db? ?value?
Return the current database. If value is specified, it sets a new value for the database. In most cases, the DIO object will automatically connect to the new database when this option is changed.
objectName ?delete? ?key? (-option | option | ...)
Delete a record from the database where the primary key matches key.
objectName ?destroy?
Destroy the DIO object.
objectName ?errorinfo? ?value?
errorinfo contains the value of the last error, if any, to occur while executing a request. When a request fails for any reason, this variable is filled with the error message from the SQL interface package.
objectName ?exec? ?request?
Execute request as an SQL query. When the exec command is called, the query is executed, and a DIO result object is returned. From there, the result object can be used to obtain information about the query status and records in a generic way. See Result Object Commands
objectName ?fetch? ?key? ?arrayName? (-option | option | ...)
Fetch a record from the database where the primary key matches key and store the result in an array called arrayName.
objectName ?forall? ?request? ?arrayName? ?body?
Execute an SQL select request and iteratively fill the array named arrayName with elements named with the matching field names, and values containing the matching values, repeatedly executing the specified code body for each row returned.
objectName ?host? ?value?
Return the current host value. If value is specified, it sets a new value for the host.
objectName ?insert? ?arrayName? (-option | option | ...)
Insert fields from arrayName into the specified table in the database.
objectName ?interface?
Return the database interface type, such as Postgresql or Mysql.
objectName ?keyfield? ?value?
Return the current keyfield. If value is specified, it sets a new value for the keyfield. Value can contain multiple key fields as a Tcl list, if the table has multiple key fields.
objectName ?keys? ?pattern? (-option | option | ...)
Return a list of keys in the database. If pattern is specified, only the keys matching will be returned.
objectName ?lastkey?
Return the last key that was used from sequence. If sequence has not been specified, this command returns an empty string.
objectName ?list? ?request?
Execute request as a SQL query and return a list of the first column of each record found.
objectName ?makekey? ?arrayName? ?keyfield?
Given an array containing key-value pairs and an optional list of key fields (we use the object's keyfield if none is specified), if we're doing auto keys, create and return a new key, otherwise if it's a single key, just return its value from the array, else if there are multiple keys, return all the keys' values from the array as a list.
objectName ?nextkey?
Increment sequence and return the next key to be used. If sequence has not been specified, this command returns an empty string.
objectName ?open?
Open the connection to the current database. This command is automatically called from any command which accesses the database.
objectName ?pass? ?value?
Return the current pass value. If value is specified, it sets a new value for the password.
objectName ?port? ?value?
Return the current port value. If value is specified, it sets a new value for the port.
objectName ?quote? ?string?
Return the specified string quoted in a way that makes it acceptable as a value in a SQL statement.
objectName ?search? (-option | option | ...)
Search the current table, or the specified table if -table tableName is specified, for rows matching one or more fields as key-value pairs, and return a query result handle. See Result Object Commands
For example,
set res [DIO search -table people -firstname Bob]
objectName ?sequence? ?value?
Return the current sequence value. If value is specified, it sets a new value for the sequence.
objectName ?store? ?arrayName? (-option | option | ...)
Store the contents of arrayName in the database, where the keys are the field names and the array's values are the corresponding values. Do an SQL insert if the corresponding row doesn't exist, or an update if it does.
The table name must have been previously set or specified with ?-table?, and the key field(s) must have been previously set or specified with ?-keyfield?.
Please note that the store method has significantly higher overhead than the update or insert methods, so if you know you are inserting a row rather than updating one, it is advisable to use the insert method and, likewise, if you know you are updating rather than inserting, to use the update method.
objectName ?string? ?request?
Execute request as a SQL query and return a string containing the first record found.
objectName ?table? ?value?
Return the current table. If value is specified, it sets a new value for the table.
objectName ?update? ?arrayName? (-option | option | ...)
Updates the row matching the contents of arrayName in the database. The matching row must already exist. The table can have already been set or can be specified with ?-table?, and the key field(s) must either have been set or specified with ?-keyfield?.
objectName ?user? ?value?
Return the current user value. If value is specified, it sets a new value for the user.

Result Object Commands

resultObj ?autocache? ?value?
Return the current autocache value. If value is specified, it sets a new value for autocache.
If autocache is true, the result object will automatically cache rows as you use them. This means that the first time you execute a forall command, each row is being cached in the result object itself and will no longer need to access the SQL result. Default is true.
resultObj ?cache?
Cache the results of the current SQL result in the result object itself. This means that even if the database connection is closed and all the results of the DIO object are lost, this result object will still maintain a cached copy of its records.
resultObj ?errorcode? ?value?
Return the current errorcode value. If value is specified, it sets a new value for errorcode.
errorcode contains the current code from the SQL database which specifies the result of the query statement which created this object. This variable can be used to determine the success or failure of a query.
resultObj ?errorinfo? ?value?
Return the current errorinfo value. If value is specified, it sets a new value for errorinfo.
If an error occurred during the SQL query, DIO attempts to set the value of errorinfo to the resulting error message.
resultObj ?fields? ?value?
Return the current fields value. If value is specified, it sets a new value for fields.
fields contains the list of fields used in this query. The fields are in order of the fields retrieved for each row.
resultObj ?forall? ?-type? ?varName? ?body?
Execute body over each record in the result object.
Types:
-array
Create varName as an array where the indexes are the names of the fields in the table and the values are the values of the current row.
-keyvalue
Set varName to a list containing key-value pairs of fields and values from the current row. (-field value -field value)
-list
Set varName to a list that contains the values of the current row.
resultObj ?next? ?-type? ?varName?
Retrieve the next record in the result object.
Types:
-array
Create varName as an array where the indexes are the names of the fields in the table and the values are the values of the current row.
-keyvalue
Set varName to a list containing key-value pairs of fields and values from the current row. (-field value -field value)
-list
Set varName to a list that contains the values of the current row.
resultObj ?numrows? ?value?
Return the current numrows value. If value is specified, it sets a new value for numrows.
numrows is the number of rows in this result.
resultObj ?resultid? ?value?
Return the current resultid value. If value is specified, it sets a new value for resultid.
resultid in most databases is the result pointer which was given us by the database. This variable is not generic and should not really be used, but it's there if you want it.
resultObj ?rowid? ?value?
Return the current rowid value. If value is specified, it sets a new value for rowid.
rowid contains the number of the current result record in the result object. This variable should not really be accessed outside of the result object, but it's there if you want it.

DIODisplay - Database Interface Objects Display Class

Name

DIODisplay — Database Interface Objects Display Class

Synopsis

DIODisplay (objectName | #auto) (-option | option | -option | option | ...)

Description

DIODisplay is an HTML display class that uses a DIO object to do the database work and a form object to do the displaying.

Options

-DIO dioObject
The DIO object to be used in conjunction with this display object. This is a required field.
-cleanup (1 | 0)
If cleanup is true, when the display object is shown, it will automatically destroy the DIO object, the form object and itself. Default is true.
-confirmdelete (1 | 0)
If confirmdelete is true, attempting to delete a record from the database first requires that the user confirm that they wish to delete it. If it is false, deletion occurs without prompting the user. Defaults to true.
-errorhandler procName
The name of a procedure to handle errors when they occur. During the processing of requests and pages, sometimes unexpected errors can occur. This procedure will handle any errors. It is called with a single argument, the error string. Use of the Tcl errorInfo and errorCode variables is also recommended though.
If no errorhandler is specified, the handle_error method within the Display object will handle the error.
-fields fieldList
Specify a list of fields to be used in this object. The fields list is actually created by using the field command to add fields to the display, but this option can be useful to sometimes over-set the list of fields created.
-form formObject
A Rivet form object to use when displaying a form. If one is not specified, the display object will automatically create one when it is necessary.
-functions functionList
A list of functions to be displayed in the main menu. This is a list of functions the user is allowed to execute.
-pagesize pageSize
How many records to show per page on a search or list. Default is 25.
-rowfields fieldList
A list of fields to display in each row of a search or list. When a search or list is conducted and the resulting rows are displayed, this list will limit which fields are displayed. Default is all fields.
-rowfunctions functionList
A list of functions to display in each row of a search or list.
-searchfields fieldList
A list of fields to allow a user to search by. This list will appear in the main screen as a drop-down box of fields the user can search on.
-title title
The title of the display object. This will be output as the title of the HTML document.

DIO Display Object Commands

objectName cleanup ?value?
Return the current cleanup value. If value is specified, it sets a new value for the cleanup option.
objectName delete key
Delete the specified key from the database.
The default action of this method is to call the DIO object's delete command. This method can be overridden.
objectName destroy
Destroy the diodisplay object.
objectName DIO ?value?
Return the current DIO value. If value is specified, it sets a new value for DIO.
objectName errorhandler ?value?
Return the current errorhandler value. If value is specified, it sets a new value for errorhandler.
objectName fetch key arrayName
Fetch the specified key from the database and store it as an array in arrayName.
The default of this method is to call the DIO object's fetch command. This method can be overridden.
objectName field fieldName (-arg | arg...)
Create a new field object and add it to the display. When a field is added to the display, a new object of the DIODisplayField class is created with its values. See [FIXME - LINK] DIO Display Fields for options and values.
objectName fields ?value?
Return the current fields value. If value is specified, it sets a new value for fields.
objectName form ?value?
Return the current form value. If value is specified, it sets a new value for form.
objectName function functionName
Add a new function to the list of possible functions. The display object will only execute methods and procs which are defined as functions by the object. This is to protect the program from executing a different procedure other than what is allowed. The function command adds a new function to the list of allowable functions.
objectName functions ?value?
Return the current functions value. If value is specified, it sets a new value for functions. See [FIXME - LINK DIO Display Functions] for a list of default functions.
objectName pagesize ?value?
Return the current form pagesize. If value is specified, it sets a new value for pagesize.
objectName rowfields ?value?
Return the current form rowfields. If value is specified, it sets a new value for rowfields.
objectName rowfooter
Output the footer of a list of rows to the web page.
This method can be overridden.
objectName rowfunctions ?value?
Return the current rowfunctions value. If value is specified, it sets a new value for rowfunctions.
objectName rowheader
Output the header of a list of rows to the web page. By default, this is an HTML table with a top row listing the fields in the table.
This method can be overridden.
objectName searchfields ?value?
Return the current searchfields value. If value is specified, it sets a new value for searchfields.
objectName show
Show the display object.
This is the main method of the display class. It looks for a variable called mode to be passed in through a form response and uses that mode to execute the appropriate function. If mode is not given, the Main function is called.
This function should be called for every page.
objectName showform
Display the form of the object.
This method displays the form for this display object. It is used in the Add and Edit methods but can be called separately if needed.
This method can be overridden if the default look of a form needs to be changed. By default, the form displayed is simply the fields in a table, in order.
objectName showrow arrayName
Display a single row of a resulting list or search.
This method is used to display a single row while displaying the result of a list or search. arrayName is a fetched array of the record.
This method can be overriden if the default look of a row needs to be changed. By default, each row is output as a table row with each field as a table data cell.
objectName showview
Display the view of the object.
This method displays the view for this display object. It is used in the Details methods but can be called separately if needed.
This method can be overridden if the default look of a view needs to be changed. By default, the view displayed is simply the fields in a table, in order.
objectName store arrayName
Store the specified arrayName in the database.
The default of this method is to call the DIO object's store command. This method can be overridden.
objectName text ?value?
Return the current text value. If value is specified, it sets a new value for text.
objectName title ?value?
Return the current title value. If value is specified, it sets a new value for title.
objectName type ?value?
Return the current type value. If value is specified, it sets a new value for type.
objectName value ?value?
Return the current value value. If value is specified, it sets a new value for value.

DIO Display Functions

These functions are called from the show method when a form response variable called mode is set. If no mode has been set, the default mode is Main. The show method will handle the necessary switching of functions. The show method also handles checking to make sure the function given is a true function. If not, an error message is displayed. New functions can be added as methods or by use of the function command, and any of the default functions can be overridden with new methods to create an entirely new class. These are the default functions provided.

Add
Show a form that allows the user to add a new entry to the database. This function calls showform to display the form for adding the entry.
Cancel
The Cancel function does nothing but redirect back to the Main function. This is handy for forms which have a cancel button to point to.
Delete
If confirmdelete is true (the default), the Delete function will ask the user if they're sure they want to delete the record from the database. If confirmdelete is false, or if the user confirms they wish to delete, this function calls the DoDelete function to do the actual deletion of a record.
Details
Display the view of a single record from the database. This function calls the showview method to display a single record from the database.
DoDelete
This function actually deletes a record from the database. Once it has deleted the record, it redirects the user back to the Main function.
Edit
Show a form that allows the user to edit an existing entry to the database. This function calls showform to display the form for editing the entry and fills in the fields with the values retrieved from the database.
List
This function lists the entires contents of the database. Each record is output in a row using the showrow method.
Main
This function is the main function of the display object. If there is no mode, or once most commands complete, the user will be redirected to this function. The default Main function displays a list of functions the user can execute, a list of searchfields the user can search on, and a query field. This query field is used by all of the other functions to determine what the user is trying to find.
In the case of a search, query specifies what string the user is looking for in the specified search field. In the case of delete, details or edit, the query specifies the database key to access.
Save
This function saves any data passed to using the store method. This is primarily used by the add and edit commands to store the results of the form the user has filled out.
Search
This function searches the database for any row whose searchBy field matches query. Once any number of records are found, Search displays the results in rows.

DIO Display Fields

Display fields are created with the field command of the DIODisplay object. Each field is created as a new DIODisplayField object or as a subclass of DIODisplayField. The standard form fields use the standard field class, while specialized field types use a class with different options but still supports all of the same commands and values a generic field does.

displayObject field fieldname (-option | option...)

These are the standard options supported by field types:

-formargs arguments
When a field is created, any argument which is not a standard option is assumed to be an argument passed to the form object when the field is shown in a form. These arguments are all appended to the formargs variable. You can use this option to override or add options after the initial creation of an object
-readonly (1 | 0)
If readonly is set to true, the field will not display a form entry when displaying in a form.
-text text
The text displayed next to the form or view field. By default, DIODisplay tries to figure out a pretty way to display the field name. This text will override that default and display whatever is specified.
-type fieldType
The type of field this is. This type is used when creating the field in the form object. fieldType can be any of the accepted form field types. See [FIXME - LINK DIO Field Types] for a list of types available.

All other arguments, unless specified in an individual field type, are passed directly to the form object when the field is created. So, you can pass -size or -maxsize to specify the length and maximum length of the field entry. Or, if type were textarea, you could define -rows and -cols to specify its row and column count.

DIO Display Field Types

The following is a list of recognized field types by DIODisplay. Some are standard HTML form fields, and others are DIODisplay fields which execute special actions and functions.


Session Package

Introduction

This is session management code. It provides an interface to allow you to generate and track a browser's visit as a "session", giving you a unique session ID and an interface for storing and retrieving data for that session on the server.

This is an alpha/beta release -- documentation is not in final form, but everything you need should be in this file.

Using sessions and their included ability to store and retrieve session-related data on the server, programmers can generate more secure and higher-performance websites. For example, hidden fields do not have to be included in forms (and the risk of them being manipulated by the user mitigated) since data that would be stored in hidden fields can now be stored in the session cache on the server. Forms are then faster since no hidden data is transmitted -- hidden fields must be sent twice, once in the form to the broswer and once in the response from it.

Robust login systems, etc, can be built on top of this code.

Requirements

Currently has only been tested with Postgresql, MySql and Oracle. All DB interfacing is done through DIO, though, so it should be relatively easy to add support for other databases.

Preparing To Use It

Create the tables in your SQL server. With Postgres, do a psql www or whatever DB you connect as, then a backslash-i on session-create.sql

(If you need to delete the tables, use session-drop.sql)

The session code by default requires a DIO handle called DIO (the name of which can be overridden). We get it by doing a

	      RivetServerConf ChildInitScript "package require DIO"
	      RivetServerConf ChildInitScript "::DIO::handle Postgresql DIO -user www"
	    

Example Usage

In your httpd.conf, add:

RivetServerConf ChildInitScript "package require Session; Session SESSION"

This tells Rivet you want to create a session object named SESSION in every child process Apache creates.

You can configure the session at this point using numerous key-value pairs (which are defined later in this doc). Here's a quick example:

RivetServerConf ChildInitScript "package require Session; Session SESSION \
  -cookieLifetime 120 -debugMode 1"

Turn debugging on -debugMode 1 to figure out what's going on -- it's really useful, if verbose.

In your .rvt file, when you're generating the <HEAD> section:

SESSION activate

Activate handles everything for you with respect to creating new sessions, and for locating, validating, and updating existing sessions. Activate will either locate an existing session, or create a new one. Sessions will automatically be refreshed (their lifetimes extended) as additional requests are received during the session, all under the control of the key-value pairs controlling the session object.

Using Sessions From Your Code

The main methods your code will use are:

SESSION id
After doing a SESSION activate, this will return a 32-byte ASCII-encoded random hexadecimal string. Every time this browser comes to us with a request within the timeout period, this same string will be returned (assuming they have cookies enabled).
SESSION is_new_session
returns 1 if it's a new session or 0 if it has previously existed (i.e. it's a zero if this request represents a "return" or subsequent visit to a current session.)
SESSION new_session_reason
This will return why this request is the first request of a new session, either "no_cookie" saying the browser didn't give us a session cookie, "no_session" indicating we got a cookie but couldn't find it in our session table, or "timeout" where they had a cookie and we found the matching session but the session has timed out.
SESSION store ?packageName? ?key? ?data?
Given the name of a package, a key, and some data. Stores the data in the rivet session cache table.
SESSION fetch ?packageName? ?key?
Given a package name and a key, return the data stored by the store method, or an empty string if none was set. (Status is set to the DIO error that occurred, it can be fetched using the status method.)

Session Configuration Options

The following key-value pairs can be specified when a session object (like SESSION above) is created:

sessionLifetime
how many seconds the session will live for. 7200 == 2 hours
sessionRefreshInterval
If a request is processed for a browser that currently has a session and this long has elapsed since the session update time was last updated, update it. 900 == 15 minutes. so if at least 15 minutes has elapsed and we've gotten a new request for a page, update the session update time, extending the session lifetime (sessions that are in use keep getting extended).
cookieName
name of the cookie stored on the user's web browser default rivetSession
dioObject
The name of the DIO object we'll use to access the database (default DIO)
gcProbability
The probability that garbage collection will occur in percent. (default 1%, i.e. 1)
gcMaxLifetime
the number of seconds after which data will be seen as "garbage" and cleaned up -- defaults to 1 day (86400)
refererCheck
The substring you want to check each HTTP referer for. If the referer was sent by the browser and the substring is not found, the session will be deleted. (not coded yet)
entropyFile
The name of a file that random binary data can be read from. (/dev/urandom) Data will be used from this file to help generate a super-hard-to-guess session ID.
entropyLength
The number of bytes which will be read from the entropy file. If 0, the entropy file will not be read (default 0)
scrambleCode
Set the scramble code to something unique for the site or your app or whatever, to slightly increase the unguessability of session ids (default "some random string")
cookieLifetime
The lifetime of the cookie in minutes. 0 means until the browser is closed (I think). (default 0)
cookiePath
The webserver subpath that the session cookie applies to (defaults to /)
cookieSecure
Specifies whether the cookie should only be sent over secure connections, 0 = any, 1 = secure connections only (default 0)
sessionTable
The name of the table that session info will be stored in (default rivet_session)
sessionCacheTable
The name of the table that contains cached session data (default rivet_session_cache)
debugMode
Set debug mode to 1 to trace through and see the session object do its thing (default 0)
debugFile
The file handle that debugging messages will be written to (default stdout)

Session Methods

The following methods can be invoked to find out information about the current session, store and fetch server data identified with this session, etc:

SESSION status
Return the status of the last operation
SESSION id
Get the session ID of the current browser. Returns an empty string if there's no session (will not happen is SESSION activate has been issued.)
SESSION new_session_reason
Returns the reason why there wasn't a previous session, either "no_cookie" saying the browser didn't give us a session cookie, "no_session" indicating we got a cookie but couldn't find it in the session table, or "timeout" when we had a cookie and a session but the session had timed out.
SESSION store ?packageName? ?key? ?data?
Given a package name, a key string, and a data string, store the data in the rivet session cache.
SESSION fetch ?packageName? ?key?
Given a package name and a key, return the data stored by the store method, or an empty string if none was set. Status is set to the DIO error that occurred, it can be fetched using the status method.
SESSION delete
Given a user ID and looking at their IP address we inherited from the environment (thanks, Apache), remove them from the session table. (the session table is how the server remembers stuff about sessions). If the session ID was not specified the current session is deleted.
SESSION activate
Find and validate the session ID if they have one. If they don't have one or it isn't valid (timed out, etc), create a session and drop a cookie on them.

Getting Additional Randomness From The Entropy File

RivetServerConf ChildInitScript "Session SESSION -entropyFile /dev/urandom \
  -entropyLength 10 -debugMode 1"

This options say we want to get randomness from an entropy file (random data pseudo-device) of /dev/urandom, to get ten bytes of random data from that entropy device, and to turn on debug mode, which will cause the SESSION object to output all manner of debugging information as it does stuff. This has been tested on FreeBSD and appears to work.


Form: An HTML Form Fields Generation Utility

Introduction

The form package is a utility for generating html forms. A form object command saves the programmer from typing the cumbersome html code of input elements, working out a solution for better standardization and readability of the code. form requires that only the minimum necessary to distinguish the element is typed, greatly simplyfing the development of forms. Options to the command are treated as a list of parameter-value pairs that become the defaults for the corresponding attributes of the form.

A form object has specialized menthods to generate all of the standard input fields, i.e. text, password, hidden, generic button, submit or reset buttons and image. form creates select input fields, radiobutton and checkbox boolean options groups. Also new inputs introduced with HTML5 are supported: color, date, datetime, datetime-local, email, file, month, number, range, search, tel, time, url, week.

Other input elements can be generated using the general purpose 'field' method.

Name

form — a Tcl command object for creating HTML forms

Synopsis

form form_name ?-option1 value_1? ?-option2 value_2? ?...?

creates and returns a new Tcl command named form_name.

Options

-method ?post|get?
The http method for sending the form data back to the server. Possible values are get or post
[Note]Note
At the time of writing only the 'get' method is implemented
-name ?form_name?
a name for the form being created: this value becomes the value of the attribute 'name' in the <form> tag.
-defaults ?default_values?
an array of default values to be assigned to the fields of the form. Every name in the array is matched with an input field, when a given field gets added to the form it is initialized with the value of the corresponding variable in the array. This option works well in conjuction with the load_response command of Rivet when default values come from another form.
-action ?URL?
The URL the data are being sent to. If no ?-action? switch is specified the data are sent to the form's URL.

Form Object Commands

Form object commands follow the usual syntax of Tcl commands with a ?subcommand? argument playing the role of a switch among various functionalities of the command. Form objects also need the ?name? parameter which is to become the value of the 'name' attribute in an input field. This argument is the key that has to be used by the server-side script to retrieve the input field value.

form_object subcommand ?name? ?-option1 value1? ?-option2 value2? ?...?

Options passed to a subcommand are copied into the tag as attribute="value" pairs. Some subcommands (e.g. form, radiobuttons and checkboxes) treat specific options in a way that fits the specific organization and function of these fields.

Exceptions to this general syntax are the field and end subcommands. field is an abstract input field creation method and requires an additional parameter specifiyng the type of field to create. Every concrete input field generation command uses this subcommand internally to print the final html.

Subcommands

start ?name? ?-method get | post? ?-name form_name? ?-defaults default_values? ?-action URL? ?args?
Print the <form> tag with all its attributes. This command must be called as first in the form generation process. The following is a sample of code creating a form named 'formname' whose data will be sent via the GET method. Initial form fields values will be obtained from array response
form myform -defaults response -method get -name formname
myform start
myform text	  text_entry -size 20
myform select option_selected -values {opt1 opt2 opt3 opt4}
myform submit submit -value Search
myform end
The code prints a form that sends a text entry content and the option value associated with a radiobutton. The URL of the server script is the same that created the form. Use the ?-url? option to specify a different url.
Options
-method ?post|get?
The method to be used to encode the form data. Possible values are get or post
[Note]Note
Currently, only the 'get' method is implemented
-name ?form_name?
a name for the form being generated: this value becomes the value of the attribute 'name' in the <form> tag.
-defaults ?default_values?
an array of default values to be assigned to the fields of the form. Every name in the array is matched with an input field, when a given field gets added to the form it is initialized with the value of the corresponding variable in the array. This option works well in conjuction with the load_response command of Rivet when default values come from another form.
-action ?URL?
The URL the data will be sent to. If no ?-action? switch is specified the data are sent to the form's URL.
end
Print the </form> closing tag. This command must be called last in the form generation process
field ?name? ?type? ?args?
Print a field of the given ?type? and ?name?, including any default key-value pairs defined for this field type and optional key-value pairs included with the statement
Options
-opt1 ?val1?
Option description
radiobuttons ?name? ?-values values? ?-labels labels? ?args?
the radiobutton creates a whole radiobutton group with the values and labels specified in the argument list. If no ?-labels? switch is passed to the subcommand the values are printed as labels of the radiobutton.
Options
-values ?values_list?
List of values associated with the radiobuttons to be displayed
-labels ?labels_list?
List of labels to be printed with every radiobutton. There must be a label for every radiobutton
Example:
form myform -defaults response -method get -name formname
myform start
myform text text_entry -size 20
myform radiobuttons fruit -values {big medium small} \
              -labels {Watermelon Orange Strawberry} \
              -class myradiobclass
myform submit submit -value Search
myform end
will print the following HTML code.
<input type="radio" name="fruit" class="myradiobclass" value="big" />Watermelon
<input type="radio" name="fruit" class="myradiobclass" value="medium" />Orange
<input type="radio" name="fruit" class="myradiobclass" value="small" />Strawberry
if the response array has a variable for the name 'fruit' the corresponding radiobutton field is automatically checked. The options ?values? and ?labels? are used internally and don't get into the tag attributes. If a ?labels? option is not given, labels are assigned using the ?values? list.
checkbox ?name? ?-label label? ?-value value? ?args?
The checkbox subcommand emits a checkbox type input field with the name, label and value attributes set according to the parameters passed to the subcommand.
Options
-values ?values_list?
List of values associated with the checkboxes to be displayed
-labels ?labels_list?
List of labels to be printed with every checkbox. There must be a label for every checkbox
Example:
myform checkbox options -value opt1   -label "Option 1"
myform checkbox options -value opt2   -label "Option 2"
myform checkbox options -value opt3   -label "Option 3"
myform checkbox options -value opt4   -label "Option 4"
Provided opt2 was in response array (in the list valued 'options' variable) that initialized the form, the output would look like this
<input type="checkbox" name="options" label="Option 1" value="opt1" />Option 1
<input type="checkbox" name="options" label="Option 2" value="opt2" 
       	 checked="checked" />Option 2
<input type="checkbox" name="options" label="Option 3" value="opt3" />Option 3
<input type="checkbox" name="options" label="Option 4" value="opt4" />Option 4
password ?name? ?args?
Same as text, but the input is obfuscated so as not to reveal the text being typed
hidden ?name? ?args?
hidden input element: typicall embedded in a form in order to pass status variables.
submit ?name? ?args?
emits the code for a classical HTML submit button. Example: the following code
	form myform -defaults response -method get -name feedsearch
	myform start
	myform submit submit -value Search
Would emit a form like this
	<form...>
	<input type="submit" name="submit" value="Search" /> 
	</form>
button ?name? ?args?
emits the code for a button field having ?name? as name
reset ?name? ?args?
Classical HTML reset button that resets the input fields back to their initial values
image ?name? ?args?
Emits an image input field
checkbox ?name? ?args?
Emits a checkbox input field
radio ?name? ?args?
Emits a radiobutton input field
color ?name? ?args?
Emits an HTML 5 "color" form field
date ?name? ?args?
Emits an HTML 5 "date" form field
datetime ?name? ?args?
Emits an HTML 5 "datetime" form field
datetime_local ?name? ?args?
Emits an HTML 5 "datetime_local" form field
email ?name? ?args?
Emits an HTML 5 "email" form field
file ?name? ?args?
Emits an HTML 5 "file" form field
month ?name? ?args?
Emits an HTML 5 "month" form field
number ?name? ?args?
Emits an HTML 5 "number" form field
range ?name? ?args?
Emits an HTML 5 "range" form field
search ?name? ?args?
Emits an HTML 5 "search" form field
tel ?name? ?args?
Emits an HTML 5 "tel" form field
time ?name? ?args?
Emits an HTML 5 "time" form field
url ?name? ?args?
Emits an HTML 5 "url" form field
week ?name? ?args?
Emits an HTML 5 "week" form field

Calendar Package

Introduction

The package is based on the Calendar class, a class capable of printing an ascii calendar table that closely resembles the output of the typical Unix cal command. The internal code is written entirely in Tcl, therefore doesn't rely on the existance of cal on the system. XmlCalendar inherits the basic methods and adds XML tagging to the table. XmlCalendar prints an XML calendar table whose header, weekdays banner and days rows tags are configurable. Also specific days or specific weeks can be given arbitrary attributes.

Calendar core methods are based on the cal procedure written by Richard Suchenwirth and published on the Tcl Wiki

[Note]Note
The Calendar package uses Tcl dict command to manage markup information. Hence either Tcl8.5 or Tcl8.4 with package dict are required.

Name

Calendar — Utility class the builds and prints a calendar table

Synopsis

Calendar calendar_name

Calendar object subcommands

The main public command for a calendar object is emit that returns a calendar table

The method emit when invoked with a single argument takes it as an year number and prints the whole calendar of that year. When invoked with 2 arguments takes the first as a month, either expressed in its shortened form ('Jan','Feb',...) or as a number in the range 1-12. The second argument is a year number.

calendar_obj emit
calendar_obj emit ?month? ?year?
calendar_obj emit ?month | year?
The method 'emit' if invoked without arguments returns an ASCII formatted calendar of the current month
set cal [Calendar #auto]
set current_month [$cal emit]
puts $current_month
      Jun 2010
  Su Mo Tu We Th Fr Sa
        1  2  3  4  5
  6  7  8  9 10 11 12
 13 14 15 16 17 18 19
 20 21 22 23 24 25 26
 27 28 29 30

Name

XmlCalendar — Prints XML formatted calendar tables

Synopsis

XmlCalendar calendar_name ?-option1 option_list? ?-option2 option_list? ?...?

An XmlCalendar object is created and returned. XmlCalendar objects print XML formatted calendar tables. The markup can be customized using the configuration options.

Configuration options accept odd-length lists as values. An option_list has the following structure

tag_name attr11 val1 attr2 val2 ...

The first element of an option list is a tag name, the remaining terms are therefore an even-length sublist which is interpreted as a sequence of attribute-value pairs that will in turn become attributes of the tag.

Methods

cal_obj emit -opt1 val1 -opt2 val2
cal_obj emit ?month? ?year? -opt1 val1 -opt2 val2
cal_obj emit ? month | year? -opt1 val1 -opt2 val2
The method 'emit' if invoked without arguments returns an XML calendar table of the current month

Options

-container (tag_name attr11 val1 attr2 val2 ...)
Assigns an options list to the XML element that will hold the whole table.
The default tag for the container is 'calendar', with no attributes.
-header (tag_name attr11 val1 attr2 val2 ...)
Assigns tag name and attributes to the XML header element (default: calheader)
-body (tag_name attr11 val1 attr2 val2 ...)
Assigns tag name and attributes to the XML body element of the table (default: calbody)
-banner (tag_name attr11 val1 attr2 val2 ...)
Assigns tag name and attributes to the XML banner element of the table (default: monthyear)
The header of a calendar table is made of a banner, showing the Month and Year number, and of a weekdays bar.
-foot (tag_name attr11 val1 attr2 val2 ...)
Assigns tag name and attributes to the XML foot element of the table (default: calfoot).
[Note]Note
This option was added for completeness, but it's not implemented yet
-banner_month (tag_name attr11 val1 attr2 val2 ...)
Tag name and attributes for the XML element holding the month name (default:month)
-banner_year (tag_name attr11 val1 attr2 val2 ...)
Tag name and attributes for the XML element holding the month name (default: year)
-weekdays (tag_name attr11 val1 attr2 val2 ...)
Tag name and attributes for the XML element holding the weekday header (default: weekdays)
-weekdays_cell (tag_name attr11 val1 attr2 val2 ...)
Tag name and attributes for the XML element holding the each single weekday (default: wkdays)
-days_row (tag_name attr11 val1 attr2 val2 ...)
Tag name and attributes for the XML element holding the each row of the calendar table (default: week)
-days_cell (tag_name attr11 val1 attr2 val2 ...)
Tag name and attributes for the XML element representing a cell in the calendar table (default: day)
-cell_function proc
If set this option is the name of a procedure that gets called for every day of the month. The procedure must accept 4 argument representing day, month, year and weekday and must return an odd-length list interpreted in the same way as options lists.
-current_day day
This option works as a simple method for pointing out a specific day of the month. If set with a day number that day will get the class attribute automatically set as "current"
-current_weekday 0-6 | today
This option works as a simple method for pointing out a specific weekday of the month. If set with a weekday index (0: Sunday, 6: Saturday) the corresponding cell in the weekdays header will get the class attribute automatically set as "current_wkday"

Name

HtmlCalendar — Concrete class derived from XmlCalendar

Synopsis

HtmlCalendar calendar_name ?-option1 option_list? ?-option2 option_list? ?...?

Concrete XmlCalendar class for printing html calendar tables. The markup of the class is xhtml compliant and prints a code fragment for inclusion in a webpage. The following is the class definition.

::itcl::class HtmlCalendar {
    inherit XmlCalendar
    
    constructor {args} {XmlCalendar::constructor $args} {
    $this configure -container    table \
                    -header       thead \
                    -body         tbody \
                    -banner       tr    \
                    -banner_month {th colspan 3 style "text-align: right;"} \
                    -banner_year  {th colspan 4 style "text-align: left;"}  \
                    -weekdays     tr    \
                    -weekday_cell th    \
                    -days_row     tr    \
                    -days_cell    td 
    }
}

A sample output from HtmlCalendar (with some styling)


Resources - How to Get Help

Mailing Lists

The Rivet mailing list is the first place you should turn for help. If you haven't found the solution to your problem in the documentation or you have a question, idea, or comment about the Rivet code itself send email to . To subscribe to the list, post email to .

The mailing list archives are available at http://mail-archives.apache.org/mod_mbox/tcl-rivet-dev/

Newsgroup

The news:comp.lang.tcl newsgroup is a good place to ask about Tcl questions in general. Rivet developers also follow the newsgroup, but it's best to ask Rivet-specific questions on the Rivet list.

Web Sites

There are several web sites that cover Apache and Tcl extensively.

Bug Tracking System

Apache Rivet uses the Apache Bug Tracking system at http://issues.apache.org/bugzilla/. Here, you can report problems, or check and see if existing issues are already known and being dealt with.

IRC

Occasionally, someone from the Rivet team is on IRC at irc.freenode.net, channel #tcl.

Editing Rivet Template Files

Rivet makes available code for two popular editors, emacs and vim to facilitate the editing of Rivet template files. The key concept is that the editor is aware of the <? and ?> tags and switches back and forth between Tcl and HTML modes as the cursor moves. These files, two-mode-mode.el and rvt.vim are available in the contrib/ directory.


Rivet Internals

This section easily falls out of date, as new code is added, old code is removed, and changes are made. The best place to look is the source code itself. If you are interested in the changes themselves, the Subversion revision control system (svn) can provide you with information about what has been happening with the code.

Initialization

When Apache is started, (or when child Apache processes are started if a threaded Tcl is used), Rivet_InitTclStuff is called, which creates a new interpreter, or one interpreter per virtual host, depending on the configuration. It also initializes various things, like the RivetChan channel system, creates the Rivet-specific Tcl commands, and executes Rivet's init.tcl. The caching system is also set up, and if there is a GlobalInitScript, it is run.

RivetChan

The RivetChan system was created in order to have an actual Tcl channel that we could redirect standard output to. This lets us use, for instance, the regular puts command in .rvt pages. It works by creating a channel that buffers output, and, at predetermined times, passes it on to Apache's IO system. Tcl's regular standard output is replaced with an instance of this channel type, so that, by default, output will go to the web page.

The global Command

Rivet aims to run standard Tcl code with as few surprises as possible. At times this involves some compromises - in this case regarding the global command. The problem is that the command will create truly global variables. If the user is just cut'n'pasting some Tcl code into Rivet, they most likely just want to be able to share the variable in question with other procs, and don't really care if the variable is actually persistant between pages. The solution we have created is to create a proc ::request::global that takes the place of the global command in Rivet templates. If you really need a true global variable, use either ::global or add the :: namespace qualifier to variables you wish to make global.

Page Parsing, Execution and Caching

When a Rivet page is requested, it is transformed into an ordinary Tcl script by parsing the file for the <? ?> processing instruction tags. Everything outside these tags becomes a large puts statement, and everything inside them remains Tcl code.

Each .rvt file is evaluated in its own ::request namespace, so that it is not necessary to create and tear down interpreters after each page. By running in its own namespace, though, each page will not run afoul of local variables created by other scripts, because they will be deleted automatically when the namespace goes away after Apache finishes handling the request.

[Note]Note
One current problem with this system is that while variables are garbage collected, file handles are not, so that it is very important that Rivet script authors make sure to close all the files they open.

After a script has been loaded and parsed into it's "pure Tcl" form, it is also cached, so that it may be used in the future without having to reload it (and re-parse it) from the disk. The number of scripts stored in memory is configurable. This feature can significantly improve performance.

Debugging Rivet and Apache

If you are interested in hacking on Rivet, you're welcome to contribute! Invariably, when working with code, things go wrong, and it's necessary to do some debugging. In a server environment like Apache, it can be a bit more difficult to find the right way to do this. Here are some techniques to try.

The first thing you should know is that Apache can be launched as a single process with the -X argument:

httpd -X
.

On Linux, one of the first things to try is the system call tracer, strace. You don't even have to recompile Rivet or Apache for this to work.

strace -o /tmp/outputfile -S 1000 httpd -X

This command will run httpd in the system call tracer, which leaves its output (there is potentially a lot of it) in /tmp/outputfile. The -S option tells strace to only record the first 1000 bytes of a syscall. Some calls such as write can potentially be much longer than this, so you may want to increase this number. The results are a list of all the system calls made by the program. You want to look at the end, where the failure presumably occured, to see if you can find anything that looks like an error. If you're not sure what to make of the results, you can always ask on the Rivet development mailing list.

If strace (or its equivalent on your operating system) doesn't answer your question, it may be time to debug Apache and Rivet. To do this, you will need to run the ./configure.tcl script with the -enable-symbols option, and recompile.

Since it's easier to debug a single process, we'll still run Apache in single process mode with -X:

@ashland [~] $ gdb /usr/sbin/apache.dbg
GNU gdb 5.3-debian
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "powerpc-linux"...
(gdb) run -X
Starting program: /usr/sbin/apache.dbg -X
[New Thread 16384 (LWP 13598)]
.
.
.
      

When your apache session is up and running, you can request a web page with the browser, and see where things go wrong (if you are dealing with a crash, for instance).


Upgrading from mod_dtcl or NeoWebScript

Rivet is a break from the past, in that we, the authors, have attempted to take what we like best about our past efforts, and leave out or change things we no longer care for. Backwards compatibility was not a primary goal when creating Rivet, but we do provide this information which may be of use to those wishing to upgrade from mod_dtcl or NWS installations.

mod_dtcl

Rivet was originally based on the dtcl code, but it has changed (improved!) quite a bit. The concepts remain the same, but many of the commands have changed.

NeoWebScript

TODO