Wapp

Wapp - A Web-Application Framework for TCL
Login

1.0 Introduction

Wapp is a lightweight framework that simplifies the construction of web application written in TCL. The same Wapp-based application can be launched in multiple ways:

  1. From the command-line, with automatic web-browser startup

  2. As a stand-alone web server

  3. As a CGI script

  4. As an SCGI program

All four methods use the same application code and present the same interface to the application user. An application can be developed on the desktop using stand-alone mode (1), then deployed as a stand-alone server (2), or a CGI script (3), or as an SCGI program (4).

2.0 Hello World!

Wapp is designed to be easy to use. A hello-world program is as follows:

#!/usr/bin/tclsh
package require wapp
proc wapp-default {req} {
   wapp-subst {<h1>Hello, World!</h1>\n}
}
wapp-start $::argv

The application defines one or more procedures that accept HTTP requests and generate appropriate replies. For an HTTP request where the initial portion of the URI path is "abcde", the procedure named "wapp-page-abcde" will be invoked to construct the reply. If no such procedure exists, "wapp-default" is invoked instead. The latter technique is used for the hello-world example above.

The procedure generates a reply using one or more calls to the "wapp-subst" command. Each "wapp-subst" command appends new text to the reply, applying various substitutions as it goes. The only substitution in this example is the \n at the end of the line.

The "wapp-start" command starts up the application.

To run this application, copy the code above into a file named "main.tcl" and then enter the following command:

tclsh main.tcl

That command will start up a web-server bound to the loopback IP address, then launch a web-browser pointing at that web-server. The result is that the "Hello, World!" page will automatically appear in your web browser.

To run this same program as a traditional web-server on TCP port 8080, enter:

tclsh main.tcl --server 8080

Here the built-in web-server listens on all IP addresses and so the web page is available on other machines. But the web-broswer is not automatically started in this case, so you will have to manually enter "http://localhost:8080/" into your web-browser in order to see the page.

To run this program as CGI, put the main.tcl script in your web-servers file hierarchy, in the appropriate place for CGI scripts, and make any other web-server specific configuration changes so that the web-server understands that the main.tcl file is a CGI script. Then point your web-browser at that script.

Run the hello-world program as SCGI like this:

tclsh main.tcl --scgi 9000

Then configure your web-server to send SCGI requests to TCL port 9000 for some specific URI, and point your web-browser at that URI.

3.0 A Slightly Longer Example

Wapp keeps track of various "parameters" or "params" that describe each HTTP request. Those parameters are accessible using routines like "wapp-param NAME" The following sample program gives some examples:

package require wapp
proc wapp-default {} {
  set B [wapp-param BASE_URL]
  wapp-trim {
    <h1>Hello, World!</h1>
    <p>See the <a href='%html($B)/env'>Wapp
    Environment</a></p>
  }
}
proc wapp-page-env {} {
  wapp-allow-xorigin-params
  wapp-subst {<h1>Wapp Environment</h1>\n<pre>\n}
  foreach var [lsort [wapp-param-list]] {
    if {[string index $var 0]=="."} continue
    wapp-subst {%html($var) = %html([list [wapp-param $var]])\n}
  }
  wapp-subst {</pre>\n}
}
wapp-start $::argv

In this application, the default "Hello, World!" page has been extended with a hyperlink to the /env page. The "wapp-subst" command has been replaced by "wapp-trim", which works the same way with the addition that it removes surplus whitespace from the left margin, so that the generated HTML text does not come out indented. The "wapp-trim" and "wapp-subst" commands in this example use "%html(...)" substitutions. The "..." argument is expanded using the usual TCL rules, but then the result is escaped so that it is safe to include in an HTML document. Other supported substitutions are "%url(...)" for URLs on the href= and src= attributes of HTML entities, "%qp(...)" for query parameters, "%string(...)" for string literals within javascript, and "%unsafe(...)" for direct literal substitution. As its name implies, the %unsafe() substitution should be avoid whenever possible.

The /env page is implemented by the "wapp-page-env" proc. This proc generates HTML that describes all of the query parameters. Parameter names that begin with "." are for internal use by Wapp and are skipped for this display. Notice the use of "wapp-subst" to safely escape text for inclusion in an HTML document.

4.0 Parameters

To better understand Wapp parameters, try running the previous sample program, but extend the /env URL with extra path elements and query parameters. For example: http://localhost:8080/env/longer/path?q1=5&title=hello+world%21

Notice how the query parameters in the input URL are decoded and become parameters. The same thing occurs with POST parameters and cookies - they are all converted into parameters accessible using the "wapp-param" interface.

4.1 Security By Default

For security reasons, this automatic decoding of GET and POST parameters only occurs if inbound request is from the "same origin" or if the special "wapp-allow-xorigin-params" interface is called. An inbound request is from the same origin if it is in response to clicking on a hyperlink or form on a page that was generated by the same website. Manually typing in a URL does not constitute the "same origin". Hence, in the example above the "wapp-allow-xorigin-params" interface is used so that you can manually extend the URL to add new query parameters.

If query parameters can have side effects, then you should omit the wapp-allow-xorigin-params call. Only invoke wapp-allow-xorigin-params for web pages that only query information. Do not invoke wapp-allow-xorigin-params on pages where the parameters can be used to change server-side state.

4.2 Environment Parameters

Wapp also provides parameters that describe the execution environment. These parameter look like CGI environment variables. To prevent environment information from overlapping and overwriting query parameters, all the environment information uses upper-case names and all query parameters are required to be lower case. If an input URL contains an upper-case query parameter (or POST parameter or cookie), that parameter is silently omitted.

The following environment parameters are always available:

All of the above are standard CGI environment values. The following are supplemental environment parameters are added by Wapp:

4.1 URL Parsing Example

For the input URL "http://example.com/cgi-bin/script/method/extra/path?q1=5" and for a CGI script named "script" in the /cgi-bin/ directory, the following CGI environment values are generated:

The first five elements of the example above, HTTP_HOST through QUERY_STRING, are standard CGI. The final four elements are Wapp extensions.

5.0 Wapp Commands

The following utility commands are available for use by applications built on Wapp:

The following additional interfaces are envisioned, but are not yet implemented:

The following interfaces are deprecated. They currently exist for compatibility but might disappear at any moment.

6.0 Developing Applications Using Wapp

You can use whatever development practices you are comformable with. But if you want some hints for getting started, consider the following:

  1. Compile the "wapptclsh" executable. You do not need a separate interpreter to run Wapp. A standard "tclsh" will work fine. But "wapptclsh" contains the a built-in copy of "wapp.tcl" and it has SQLite compiled in. We find it convenient to use. The sequel will assume you have "wapptclsh" somewhere on your $PATH.

  2. Seed your application using one of the short scripts shown above, or perhaps one of the examples in this repository.

  3. Make a few simple changes to the code.

  4. Run "wapptclsh yourcode.tcl" to test your changes.

  5. Goto 3. Continue until your application is working.

  6. Move the "yourcode.tcl" file to your server for deployment.

During the loop between steps (3) and (5), there is no web server sitting in between the application and your browser, which means there is no translation or interpretation of traffic. This can help make debugging easier. Also, you can add "puts" commands to the application to get interactive debugging information on your screen while the application is running.

7.0 Limitations

Each Wapp process is single-threaded. The fileevent command is used to allow accepting multiple simultaneous HTTP requests. However, as soon as a complete request is received, and the "wapp-page-NAME" proc runs, all other processing is suspended until that proc completes. Hence, a long-running "wapp-page-NAME" proc can cause the system to become unresponsive. If this is a problem, the Wapp application can be run in CGI mode. In CGI mode, the overlying webserver will start a separate Wapp process for each request, and there are no limits on the number of simultaneous Wapp processes.

The POST data parser for Wapp currently only understands application/x-www-form-urlencoded content. Wapp does not (currently) have a decoder for multipart/form-data content. A multipart/form-data decoder might be added in the future. Or, individual applications can implement their own multipart/form-data decoder using the raw POST data held in the CONTENT parameter.

8.0 Design Rules

All global procs and variables used by Wapp begin with the four character prefix "wapp". Procs and variable intended for internal use begin with the seven character prefix "wappInt".