[AMG]: This page provides detailed information about the design, implementation, and philosophy of the [Wibble] web server. <> ---- **Name** "Wibble" is similar in sound to "[Wub]", and according to the Jargon File [http://www.catb.org/jargon/html/W/wibble.html], it is one possible pronunciation of "[www]". ---- **Zone handlers** Zones are analogous to domains in Wub and [TclHttpd]. Zones are matched against the [URI] by prefix, with an important exception for directory names. I use the term "zone" instead of "domain" because I don't want any confusion with [DNS] domain names. Someday I may add support for virtual hosts via the Host: header, which means that DNS domain/host names will be included in zone names [http://wiki.tcl.tk/27380#pagetoc232f2da4]. Handlers can be stacked, so zones are defined by a combination of handlers which are executed in sequence. Each handler receives a state [dict]ionary as its last argument. The state dictionary contains at least two sub-dictionaries: request and options. The request dictionary is derived from the HTTP request. The contents of options indicate the match prefix and suffix, plus the (possible) filesystem path of the requested object, plus any other options specified when the zone handler was registered. The state dictionary can also contain custom sub-dictionaries that were put there by previous zone handlers. The handler returns using the [[nexthandler]] or [[sendresponse]] command. [[nexthandler]] takes any number of state dictionaries as arguments, which will be passed to subsequent handlers. [[sendresponse]] takes the final response dict to send to the client. Zones also stack. For example, if no handlers for zone /foo return a response, then the handlers for / are tried. Just as the handlers within a zone must be specified in the order they are to be executed, the zones themselves must be specified in order of decreasing specificity. To inhibit this stacking behavior, be sure that a default handler is defined for the zone, e.g. '''notfound'''. The '''$wibble::zonehandlers''' variable defines the zones and their handlers. '''$wibble::zonehandlers''' is a list of three-element triples: zone prefix, zone handler [command prefix], and options dictionary. The [[wibble::handle]] command is used to update this variable. Statically, the zone handlers form a stack or list. But dynamically (during program execution), the zone handlers can branch from a list into a tree, which is traversed in a breadth-first manner to search for a response to send to the client. The tree branches whenever [[nexthandler]] is given more than one argument; each argument forms a new alternative handler stack operating on a modified state dictionary. When [[nexthandler]] is given zero arguments, the "node" is a leaf node, the tip of a dead branch; the state dictionary that was passed to the handler is removed from consideration. Simply returning from a zone handler is the same as calling [[nexthandler $state]] when $state has not been modified. ---- **State dictionary** ***Entries received from client*** * '''request socket''': The name of the Tcl [channel] that is connected to the client. * '''request peerhost''': Network address of the client. * '''request peerport''': TCP port number of the client. * '''request method''': HTTP method (GET, PUT, POST, HEAD, etc.). * '''request uri''': HTTP URI, including query string. * '''request path''': Path of client-requested object, excluding query string. * '''request protocol''': HTTP/1.0 or HTTP/1.1, whatever the client sent. * '''request header''': Dictionary of HTTP header fields from the client. * '''request rawheader''': List of HTTP header lines. * '''request accept''': Dictionary mapping from Accept-X: headers to acceptable values, sorted by descending qvalue. * '''request query''': Dictionary of query string elements. * '''request rawquery''': Query string in text form. * '''request post''': Dictionary of POST elements. * '''request rawpost''': POST in text form. ***Locally derived entries*** * '''request port''': Local TCP server port number. * '''request rawtime''': Time of request in seconds since the epoch. * '''request time''': Time of request, formatted with [[[clock format]]]. * '''options prefix''': Zone prefix name. * '''options suffix''': Client-requested object path, sans prefix and query string. * '''options fspath''': Object path with '''root''' option prepended. Only defined if '''root''' option is defined. ***Common configuration options*** * '''options root''': Filesystem directory corresponding to zone root directory. * '''options indexfile''': Name of "index.html" file to append to directory requests. Support for configuration options varies between zone handlers. Zone handlers can also take positional configuration options by including them in the command argument to [[wibble::handle]], which is actually a [list] constituting a [command prefix]. ***Element structure*** '''header''', '''query''', and '''post''' are all dictionaries whose elements share a common structural convention. When it's possible for an element to exist but not have a value, or to both have a value and have attributes, the element is represented as a dictionary with the value stored with key "" (empty string) and with the attributes in the remaining keys. `[[[dict exists] $query foo]]` checks if the `foo` key exists. `[[dict exists $query foo ""]]` checks if the `foo` key exists ''and'' has a value. `[[dict get $query foo ""]]` gets that value. The vars zone will help you sort this all out. This HTML snippet: ======none
====== Results in this vars output: &| `post foo content-disposition {}` | `form-data` |& &| `post foo content-disposition name` | `foo` |& &| `post foo {}` | `value of foo` |& &| `post bar content-disposition {}` | `form-data` |& &| `post bar content-disposition name` | `bar` |& &| `post bar {}` | `value of bar` |& &| `post quux content-disposition {}` | `form-data` |& &| `post quux content-disposition name` | `quux` |& &| `post quux content-disposition filename` | `data.txt` |& &| `post quux content-type {}` | `application/octet-stream` |& &| `post quux {}` | `contents of data.txt` |& &| `rawpost` | `(len=585)` |& This shows that `foo`=`value%20of%20foo` and `bar`=`value%20of%20bar`. All three form inputs have `content-disposition`=`form-data`, but their `content-disposition name` attributes differ. `content-disposition filename` is available for `quux`, and there's a `content-type` too. http://localhost:8080/vars?foo=value+of+foo&bar=value+of+bar&key+with+no+value produces the following vars output: &| `query foo {}` | `value of foo` |& &| `query bar {}` | `value of bar` |& &| `query {key with no value}` | |& &| `rawquery` | `?foo=value+of+foo&bar=value+of+bar&key+with+no+value` |& Notice the presence of `{}` (a.k.a. `""`) at the end of the query keys with values and the absence of same at the end of query keys without values. ---- **Response dictionary** * '''status''': The numeric HTTP status to send. 200 is OK, 404 is Not Found, etc. * '''header''': Dictionary of HTTP header fields to send to the client. * '''content''': Message body to send to the client. * '''contentfile''': Name of a file containing the message body to send to the client. ---- **Predefined zone handlers** * '''vars''': Echo state dictionary. * '''dirslash''': Redirect directory requests lacking a trailing slash. * '''indexfile''': Add '''indexfile''' to directory requests. * '''dirlist''': Serve directory listings. * '''template''': Compile *.tmpl files into *.script files. * '''script''': Execute *.script files. * '''static''': Serve static files (not directories). * '''notfound''': Send 404. <> Wibble