Wibble zone handlers

AMG: This page collects useful and illustrative zone handlers for the Wibble web server.

Fetching backrefs...

Reloading the server source

AMG: Here is a reload zone handler that reloads the server source on command.

proc ::wibble::zone::reload {state} {
    set ::wibble::zonehandlers {}
    foreach script [dict get $state options scripts] {
        uplevel #0 [list source $script]
    }
    dict set state response status 200
    dict set state response header content-type "" text/plain
    dict set state response content "Server reloaded.  Have a nice day.\n"
    sendresponse [dict get $state response]
}

wibble::handle /reload reload scripts [list [info script]]

Depending on the structure of your application, you may want to remove the set ::wibble::zonehandlers {} line.


Interfacing with Wub domains

CMcC - 2010-06-27 23:08:49

Something like the following shim ought to interface most Wub domains to a wibble server.

You'd have to construct the Wub domain with appropriate arguments, then in the wibble::handle definition, pass an option "domain $domain"

Some Wub domains won't work, as they require capabilities that wibble doesn't provide, but many should.

Enjoy.

proc ::wibble::zone::wub {state} {
    dict with state request {}

    # remap some important request fields
    dict set r -uri $uri
    dict set r -clientheaders [dict keys $state request]
    set r [dict merge $r [dict get $state request] [Url parse $uri 1]]

    try {
        {*}$domain do $r
    } on error {r options} {
        return
    }

    # remap some important result fields
    set response [dict remove $r [dict keys $r -*]]
    dict set response content [dict get $r -content]
    dict set response status [dict get $r -code]
    sendresponse $response
}

AMG: I've been trying to keep this zone handler up-to-date with the latest version of Wibble, but I haven't tested any of my changes. I'd appreciate it if someone gave it a look to make sure it still works.

AMG: This is definitely gonna take some more work due to the change in response header formats. Would someone with Wub experience please lend a hand?


Parsing Tclhttpd Style template files

SDW: I am in the process of porting my websites over to Wibble. But I have a lot of content that already works under Tclhttpd. So I wrote a quick and dirty little patch to wibble that will allow it to reply to Tclhttpd style template files:

# Interpret tclhttpd style template files.
proc ::wibble::zone::tclhttpd {state} {
    if {[file readable [dict get $state options fspath].tml]} {
        set fin [open [dict get $state options $fspath].tml]]
        set data [read $fin]
        close $fin
        dict set state response status 200
        dict set state response header content-type "" text/html
        dict set state response content [subst $data]
        sendresponse [dict get $state response]
    }
}

::wibble::handle / tclhttpd root $root

AMG: Thanks. Boy, am I glad I named the Wibble template files *.tmpl!


Rejecting access to files by name

AMG: This zone handler gives a 403 Forbidden when the client requests a file whose name is in a list of glob-style patterns. Specify this list in the $patterns zone option. It's most useful in combination with the [templatefile] and [scriptfile] zone handlers.

proc ::wibble::zone::rejectname {state} {
    dict with state request {}; dict with state options {}
    set name [file tail $path]
    foreach pattern $patterns {
        if {[string match $pattern $name]} {
            forbidden $state
        }
    }
}

::wibble::handle / rejectname patterns {*.tmpl *.script}

However, there's a huge bug that allows this code to be spoofed: [L1 ]. Until that's fixed, you might want to also reject filenames that contain NUL. I'd put that in myself, but I don't know how to handle it universally. The URI contains a path which can be checked, but it also contains an optional query suffix that may contain more filenames. The query suffix may also contain binary data that can legitimately embed NULs; watch out for that. And the POST may have filenames too, but it's also very likely to contain binary data.


jblz 2011-1-03

Here is some proof-of-concept code for sinatra-like url-routing within wibble:

# zone-handler for sinatra-like routing
proc wibble::zone::quibble {state} {
    dict with state request {}; dict with state options {}
        
        GET /foo/:bar {
                dict set response content "<h1>:bar is [dict get $data bar]</h1>"
        }
        
        GET /foo/:bar/baz/:fozz {
                append res "<h1>:bar is [dict get $data bar]</h1>"
                append res "<h1>:fozz is [dict get $data fozz]</h1>"
                dict set response content $res
        }
}

proc ::wibble::qrouter {mthd dpath body} {
        upvar state cstate
        upvar response qresponse
        upvar data rdata
        
        dict with cstate request {}; dict with cstate options {}

        # check if the HTTP method is correct
        if { $method ne $mthd} return
        
        set def_path [lrange [split $dpath /] 1 end]
        set def_length [llength $def_path]        
        set req_path [lrange [split $path /] 1 end]        
        set req_length [llength $req_path]
        
        # check if the number of elements match
        if {$def_length != $req_length} return
        
        set data {}
        foreach d $def_path r $req_path {
                if {$d ne $r} {
                        if {[string index $d 0] ne ":"} {
                                return
                        } else {
                                set varname [string range $d 1 end]
                                dict set rdata $varname $r
                        }
                }
        }
        dict set qresponse status 200
        dict set qresponse header content-type "" text/html
        uplevel 1 $body
        sendresponse $qresponse
}

interp alias {} GET {} ::wibble::qrouter GET
interp alias {} POST {} ::wibble::qrouter POST

AMG: I made a quick pass at updating the above for the new header format style, but I don't know if I did it right. I must admit I don't understand this code. Likely that's because it's 1am and I need some sleep. ;^) Please give it a test and let me know if it's working right. Also, some explanation would be much appreciated.


WebSocket

AMG: See the WebSocket page. There you will find procedures that implement the WebSocket protocol in terms of the Wibble architecture. There's also an example showing how to use it to create an interactive site containing both a calculator and a clock. This combination is special because it has both synchronous and asynchronous I/O, which is impossible with vanilla HTTP and normally requires AJAX long polling and two sockets: at any moment, either the client or the server could talk. With WebSocket, it's simple.