Updated 2012-06-08 14:16:58 by dkf

One simple way to embed tclhttpd into an tcl application is to just create a slave interpreter and load TclHttpd inside the slave. This is easy and works quite well and moves all problems with colliding names (there is one exception, the global env array) etc. out of the way.
# example code
# StartEmbeddedHttpd
# path is the path to the tclhttpd bin/httpd.tcl file
# args are commandline args to pass to tclhttpd

proc StartEmbeddedHttpd {path args} {
    set httpd [interp create] 

    # set tclhttpd command line options
    $httpd eval "set argc [llength $args]"
    set cmdargv "set argv [list $args ]"
    $httpd eval $cmdargv

    # now load the server and start it
    set cmd [list source $path]

    # the server does not return, it enters [vwait forever] , so we have to use the after 0 trick
    # errors have to be handled by bgerror

    after 0 $httpd eval $cmd

    return $httpd

proc ShutdownEmbeddedHttpd {interp} {
    $interp eval Httpd_Shutdown

proc DestroyEmbeddedHttpd {interp} {
    $interp delete

Usage example:
% StartEmbeddedHttpd /tclhttpd/bin/httpd.tcl -port 9001

# now we can even access our own server via http 
% package require http
% set tok [http::geturl http://localhost:9001/]

It is even possible to start multiple servers if they use different ports.

TV Is another interpreter always or possibly a separate thread in tcl (sorry to say last time I didn't read in enough to know by heart or doc)? Otherwise one part of such application might hang the other. And beware than when you are on the internet, which is good and desirable of course, anyone can access the server, and there ARE certain risks in running httpd out of the box... And fun, of course.

Jacob Levy Nice! I should note that this depends on the event loop being active.

To answer TV's question -- interpreters can belong to the same or to another thread. If you use the thread extension, the other thread will run an interpreter that belongs only to it. The interpreters returned from "interp create" always belong to the same thread as the invoking interpreter.

Michael Schlenker Some tinkering with tclhttpd and some other supporting stuff is missing on my way to run tclhttpd in a safe interpreter. The basic idea is to give tclhttpd only the right it really needs, i think the socket command exposed is nearly all really needed if only dynamic pages are served (via DirectUrl or other Domain Handlers that do not need filesystem access). This idea is a kind of chroot() call for an embedded tclhttpd. One usage example where it would be nice:

An application that does database access, and presents it via a WebGUI, the WebGUI or tclhttpd has a hole in it that allows attackers to introspect the server and execute tcl commands. If it runs in a slave interpreter the attacker can introspect anything in the slave and access the filesystem. If tclhttpd runs in a safe slave, all the attacker can do is introspect the server itself, gaining no real benefit from his intrusion.

Some steps have to be done to get there:

  • Strip tclhttpd down, so it can be run in a safe slave with only socket exposed additionaly (or in the SafeBase, package loading should be ok)
  • Make CGI optional
  • Make the DocDomain optional
  • Make auth work without filesystem access, by providing auth hooks (Think of PAM as an example for this architecture)
  • Make logging work without filesystem access, by providing logging hooks that get called