Examination of the configuration for Etoyoc.com
The webserver http://www.etoyoc.com is the personal web server of Sean Woods. It manages several virtual hosts (www.etoyoc.com, www.gbeefabrics.com) as well as a mix of dynamically generated content and static files. This page is an explaination of how the site is configured to run toadhttpd in the hopes that the example may answer one or more questions you may have about your own setup.Overview
To keep things ... sane... Sean has broken his configuration file up into several chunks. config.tcl is just the launching point to get to those files.
Files on his linode are hosted from /opt/www. Under that directory is:
htdocs/ | The doc_root for the default host (http://www.etoyoc.com) |
---|---|
sites/gbeefabrics | The configuration file and static content for http://www.gbeefabrics.com |
sites/fossil | The configuration file and static content for http://fossil.etoyoc.com |
sites/yoda | The configuration file and static content Sean's personal website http://www.etoyoc.com/yoda |
sites/pff | The configuration file, static content, customizations, etc for an online volunteer registration database the Sean runs for the Philadelphia Folk Festivals' Camping Committee. (http://pff.etoyoc.com) |
To make things extra interesting, Sean builds all of his content at home, and synchronizes the file systems on linode via rsync. So there are several points in the configuration where an alternate URL is provided if the request was for localhost, and not a public domain name.
config.tcl
Sean's config.tcl is less a configuration file than a launching point to load other configuration files:### # The server's configuration can be modified with calls to "my config set" ### my config set doc_ttl 900 ### # Package require the stock plugins we need ### package require toadhttpd::sqlite package require toadhttpd::bouncer package require toadhttpd::clique package require toadhttpd::fossil package require toadhttpd::trivia package require toadhttpd::bootstrap package require toadhttpd::nadger ### # Some plugins inject behavior into the server object # do that process ### my plugin bouncer my plugin sqlite my plugin clique my plugin bootstrap my plugin nadger # Remember this location set srvhere [file dirname [file normalize [info script]]] ### # Some plugins require additional site specific configuration # information ### my config set notifier_sender {The Blue Fairy} my config set notifier_originator REDACTED_EMAIL_ADDRESS my config set notifier_ports REDACTED_SMTP_PORTS my config set notifier_server REDACTED_SMTP_SERVER my config set notifier_username REDACTED_SMTP_USER my config set notifier_password REDACTED_SMTP_PASSWORD namespace eval ::etoyoc {} ### # Load long-form customizations from the libtml directory ### foreach file [glob -nocomplain [file join $srvhere libtml *.tcl]] { try { source $file } on error {err errdat} { puts "File: $file" puts "Error: $err" puts "Trace: [dict get $errdat -errorinfo]" } } ### # Go out to each site and load any customizations they need ### foreach site [glob -nocomplain [file join $srvhere sites *]] { foreach file [glob -nocomplain [file join $site libtml *.tcl]] { try { source $file } on error {err errdat} { puts "File: $file" puts "Error: $err" puts "Trace: [dict get $errdat -errorinfo]" } } } ### # Send requests for popular script attack vectors to # the black hole implemented in the bouncer plugin ### my uri add % { /ccvv /admin% /test/wp-admin% /wp-login.php% /CGI/Execute /PhpMyAdmin% %.php /manager/html /wls-wsat% /.DS_Store% /.git% /.hg% /.idea% /.ssh% /.well-known% %phpunit% /sftp_config.% } { mixin toadhttpd::content.honeypot } ### # Load the site specific URL rules ### foreach site [glob -nocomplain [file join $srvhere sites *]] { if {![file exists [file join $site config.tcl]]} continue try { set sitename [file tail $site] source [file join $site config.tcl] } on error {err errdat} { puts "Site: $site" puts "Error: $err" puts "Trace: [dict get $errdat -errorinfo]" } }
sites/fossil/config.tcl
### # Sites can load their own packages ### package require toadhttpd::fossil set here [file dirname [info script]] if {[file exists /opt/fossil]} { ### # On the server we only deliver fossil repos in the /opt/fossil directory ### foreach file [glob -nocomplain /opt/fossil/*.fossil] { set proj [file rootname [file tail $file]] ### # Add to the global robots.txt file that we REALLY do not want to see robots hitting # any of the following URIs that fossil hosts ### foreach area {login leaves files brlist taglist reportlist setup test tkthistory help vdiff} { my robots.txt add /fossil/$proj/$area } } my uri add fossil.etoyoc.com {/fossil/ /fossil/index%} { mixin httpd::content.redirect LOCATION http://fossil.etoyoc.com/fossil } my uri add fossil.etoyoc.com /fossil { mixin toadhttpd::content.fossil_root fossil_root /opt/fossil prefix fossil } my uri add fossil.etoyoc.com /fossil/% { mixin toadhttpd::content.fossil_node_scgi prefix fossil fossil_root /opt/fossil } } else { ### # On the dev machine, offer up any fossil repository # that can be located by "fossil list" ## my uri add localhost {/fossil /fossil/ /fossil/index%} { mixin toadhttpd::content.fossil_root prefix fossil } my uri add localhost /fossil/% { TTL 0 mixin toadhttpd::content.fossil_node_scgi prefix fossil } }
sites/gbeefabrics/libtml/gbee.tcl
This file add some customized behaviorsnamespace eval ::gbee {} tool::define ::gbee::style { superclass ::etoyoc::style method html_css {} { upvar 1 is_localhost is_localhost if {$is_localhost} { set site_root /gbee set site_rel {} set stylesheet /gbee/style.css } else { set site_root {} set site_rel {} set stylesheet /style.css } append result "<link rel=\"stylesheet\" href=\"${stylesheet}\">" append result \n [uplevel 1 {subst {<style media="screen" type="text/css"> body { background: url(${site_root}/images/bee-fleece.jpg) repeat; font-family: serif; color:#000066; font-size: 12pt; } .container { background-color: white; } .normal { background-color: white; } </style>}}] } method html_header {title args} { set result {} uplevel 1 { set is_localhost [expr {[lindex [split [my http_info getnull HTTP_HOST] :] 0] eq "localhost"}] } upvar 1 is_localhost is_localhost if {$is_localhost} { set site_root /gbee set site_rel {} set stylesheet /gbee/style.css } else { set site_root {} set site_rel {} set stylesheet /style.css } append result "<HTML><HEAD>" if {$title ne {}} { append result "<TITLE>$title</TITLE>" } append result [my html_css] append result "</HEAD><BODY><!-- Gbee header -->" append result \n {<div id="top-menu">} if {[dict exists $args banner]} { append result "<img src=\"[dict get $args banner]\">" } else { append result "<img src=\"${site_root}/images/gbee-logo.jpg\">" } if {[dict exists $args sideimg]} { append result "\n<div name=\"sideimg\"><img align=right src=\"[dict get $args sideimg]\" width=25%></div>" } append result {<div id="content">} return $result } method html_footer_content {args} { return [subst {<font size="-1">All content copyright [clock format [clock seconds] -format %Y], GBee Fabrics | <tt>email: <a href="gbee_fabrics@etoyoc.com">gbee_fabrics@etoyoc.com</a> </tt></b>}] } }
sites/gbeefabrics/config.tcl
set here [file dirname [file normalize [info script]]] ### # Gbeefabrics uses a custom content generator to deliver # news and events ### my uri add www.gbeefabrics.com {/news% /events%} { CONTENT_TTL 86400 mixinmap {reply ::etoyoc::content.news style ::gbee::style} prefix /news path [file join $here htdocs news] } ### # All other traffic for gbee fabrics is static content # served from sites/gbeefabrics/htdocs # Note: We use an alternate style plugin ### my uri add www.gbeefabrics.com % { TTL 86400 mixinmap {reply httpd::content.file style ::gbee::style} path [file join $here htdocs] } ### # The website has had a few different names that # have been printed to buisness cards, redirect them # to the canonical location ### my uri add gbee%.etoyoc.com % { mixin httpd::content.redirect LOCATION http://www.gbeefabrics.com } ### # On the dev box, I just need some rules to deliver the website # to http://localhost/gbee ### my uri add localhost {/gbee/news% /gbee/events%} { TTL 0 mixinmap {reply ::etoyoc::content.news style ::gbee::style} prefix /gbee/news path [file join $here htdocs news] } my uri add localhost /gbee% { CONTENT_TTL 0 mixinmap {reply httpd::content.file style ::gbee::style} path [file join $here htdocs] prefix /gbee }