Edit: A couple days ago the developers released Hunchentoot 1.0.0 . I have updated this article for the current version. Details for pre 1.0 versions reside at the bottom of this article.



This article explains how to install Hunchentoot 1.0.0 behind Apache on Ubuntu 8.10 Intrepid. It assumes you have Apache installed and you have some basic familiarity of Lisp, Slime, Emacs, Apache and Linux. The article uses SBCL as the Common Lisp implementation.



The steps in this article follow closely to the blog Lost in Technopolis's article "Running Common Lisp behind Apache" but are updated for Ubuntu and Hunchentoot 1.0. I recommend reviewing that article also so you become aware of other options while using Hunchentoot and SBCL.





Configure Apache



<VirtualHost *:80>

# Admin email, Server Name (domain name) and any aliases

ServerAdmin YOUR-EMAIL

ServerName YOUR-DOMAIN.com

ServerAlias www.YOUR-DOMAIN.com



# Index file and Document Root (where the public files are located)

DirectoryIndex index.html

DocumentRoot /home/USERNAME/public_html/YOUR-DOMAIN.com/public



# Custom log file locations

LogLevel warn

ErrorLog /home/USERNAME/public_html/YOUR-DOMAIN.com/log/error.log

CustomLog /home/USERNAME/public_html/YOUR-DOMAIN.com/log/access.log combined



ProxyRequests Off

<Proxy *>

allow from all

</Proxy>

ProxyPass /static !

ProxyPass / http://127.0.0.1:8000/

ProxyPassReverse / http://127.0.0.1:8000/



</virtualhost>



/etc/apache2/mods-available/proxy.conf

sudo a2ensite test

sudo a2enmod proxy

sudo a2enmod proxy_http

sudo /etc/init.d/apache2 restart

Install SBCL

wget http://prdownloads.sourceforge.net/sbcl/sbcl-1.0.23-x86-linux-binary.tar.bz2

bzip2 -cd sbcl-1.0.23-x86-linux-binary.tar.bz2 | tar xvf -

cd sbcl-1.0.23-x86-linux

sudo sh install.sh

Configure Slime

cd ~/apps

wget http://common-lisp.net/project/slime/snapshots/slime-current.tgz

tar xvf slime-current.tgz

mv slime-2009-02-15 slime

sbcl

(load "/PATH-TO-YOUR-SLIME-DIR/swank-loader.lisp")

(swank-loader:init)

(quit)



Install Hunchentoot

sudo sbcl

(require 'asdf-install)

(asdf-install:install :hunchentoot)

(asdf-install:install :cl-who)

(hunchentoot:start (make-instance 'hunchentoot:acceptor :port 4242))

Create start-up scripts

sudo adduser --system --group --no-create-home htoot

cd /etc/init.d

sudo nano htoot

#! /bin/sh

### BEGIN INIT INFO

# Provides: skeleton

# Required-Start: $remote_fs

# Required-Stop: $remote_fs

# Default-Start: 2 3 4 5

# Default-Stop: 0 1 6

# Short-Description: Example initscript

# Description: This file should be used to construct scripts to be

# placed in /etc/init.d.

### END INIT INFO



# Author: William Bruschi (william.bruschi@gmail.com)



# Do NOT "set -e"



# PATH should only include /usr/* if it runs after the mountnfs.sh script

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin

DESC="Hunchentoot"

NAME=sbcl



# STARTUPSCRIPT should point to your hunchentoot lisp startup script

STARTUPSCRIPT=/ENTER-YOUR-PATH_HERE/start-hunchentoot.lisp



DAEMON=/usr/local/bin/$NAME

DAEMON_ARGS="--load $STARTUPSCRIPT"

SCRIPTNAME=/etc/init.d/htoot





# Exit if the package is not installed

[ -x "$DAEMON" ] || exit 0



# Read configuration variable file if it is present

[ -r /etc/default/$NAME ] && . /etc/default/$NAME



# Load the VERBOSE setting and other rcS variables

. /lib/init/vars.sh



# Define LSB log_* functions.

# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.

. /lib/lsb/init-functions



#

# Function that starts the daemon/service

#

do_start()

{

# Return

# 0 if daemon has been started

# 1 if daemon was already running

# 2 if daemon could not be started

start-stop-daemon --start --quiet -u htoot --exec $DAEMON --test > /dev/null \

|| return 1



sudo -b -u htoot $DAEMON $DAEMON_ARGS &

# Add code here, if necessary, that waits for the process to be ready

# to handle requests from services started subsequently which depend

# on this one. As a last resort, sleep for some time.

}



#

# Function that stops the daemon/service

#

do_stop()

{

# Stop the Hunchentoot and Swank servers before killing the daemon

(telnet localhost 6440 &) > /dev/null

# Wait for servers to shutdown

sleep 10



# Return

# 0 if daemon has been stopped

# 1 if daemon was already stopped

# 2 if daemon could not be stopped

# other if a failure occurred

start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME

RETVAL="$?"

[ "$RETVAL" = 2 ] && return 2

# Wait for children to finish too if this is a daemon that forks

# and if the daemon is only ever run from this initscript.

# If the above conditions are not satisfied then add some other code

# that waits for the process to drop all resources that could be

# needed by services started subsequently. A last resort is to

# sleep for some time.

start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON

[ "$?" = 2 ] && return 2

return "$RETVAL"

}



#

# Function that sends a SIGHUP to the daemon/service

#

do_reload() {

#

# If the daemon can reload its configuration without

# restarting (for example, when it is sent a SIGHUP),

# then implement that here.

#

start-stop-daemon --stop --signal 1 --quiet --name $NAME

return 0

}



case "$1" in

start)

[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"

do_start

case "$?" in

0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;

2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;

esac

;;

stop)

[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"

do_stop

case "$?" in

0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;

2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;

esac

;;

#reload|force-reload)

#

# If do_reload() is not implemented then leave this commented out

# and leave 'force-reload' as an alias for 'restart'.

#

#log_daemon_msg "Reloading $DESC" "$NAME"

#do_reload

#log_end_msg $?

#;;

restart|force-reload)

#

# If the "reload" option is implemented then remove the

# 'force-reload' alias

#

log_daemon_msg "Restarting $DESC" "$NAME"

do_stop

case "$?" in

0|1)

do_start

case "$?" in

0) log_end_msg 0 ;;

1) log_end_msg 1 ;; # Old process is still running

*) log_end_msg 1 ;; # Failed to start

esac

;;

*)

# Failed to stop

log_end_msg 1

;;

esac

;;

*)

#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2

echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2

exit 3

;;

esac



:

sudo chmod 755 htoot

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;; start-hunchentoot.lisp

;;;;

;;;; Author: William Bruschi

;;;; Date: 02-14-2009

;;;;

;;;; Starts Hunchentoot and Swank, then listens for a shutdown

;;;; command on the specified port.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



(require 'asdf)

(require 'hunchentoot)

(require 'cl-who)

(require 'sb-bsd-sockets)



(defpackage :webserver

(:use :common-lisp :hunchentoot :cl-who))



(in-package :webserver)



(defparameter *hunchentoot-port* 8000)

(defparameter *shutdown-port* 6440)

(defparameter *swank-loader*

"/PATH-TO-SLIME/slime/swank-loader.lisp")

(defparameter *swank-port* 4006)



;;; Start the hunchentoot server

(defparameter *hunchentoot-server*

(start (make-instance 'acceptor :port *hunchentoot-port*)))

(princ "Hunchentoot server started on port ")

(princ *hunchentoot-port*) (terpri)



;;; Load any sites here







(in-package :webserver)



;;; Start swank

(load *swank-loader*)

(swank-loader:init)

(swank:create-server :port *swank-port* :dont-close t)

(princ "Loaded Swank on port ")

(princ *swank-port*)(terpri)



;;; Wait and listen for shutdown command

(let ((socket (make-instance 'sb-bsd-sockets:inet-socket

:type :stream :protocol :tcp)))



;; Listen on a local port for a TCP connection

(sb-bsd-sockets:socket-bind socket #(127 0 0 1) *shutdown-port*)

(sb-bsd-sockets:socket-listen socket 1)



;; When it comes, close the sockets and continue

(multiple-value-bind (client-socket addr port)

(sb-bsd-sockets:socket-accept socket)

(sb-bsd-sockets:socket-close client-socket)

(sb-bsd-sockets:socket-close socket)))



;; Shut down Hunchentoot

(princ "Stopping Hunchentoot...")(terpri)

(stop *hunchentoot-server*)



;; Shut down Swank and anyone else by terminating all threads

(dolist (thread (sb-thread:list-all-threads))

(unless (equal sb-thread:*current-thread* thread)

(sb-thread:terminate-thread thread)))

(sleep 1)

(sb-ext:quit)

sudo /etc/init.d/htoot start

Starting Hunchentoot at bootup

cd /etc/init.d

sudo update-rc.d htoot defaults

Connecting to your server via Slime

Troubleshooting

Conclusion

Weblocks - A continuations based web framework

HT-AJAX

- AJAX framework for Hunchentoot

- AJAX framework for Hunchentoot LispCast

- A series of videos about creating a Reddit clone using Hunchentoot

Hunchentoot Pre 1.0

Install Mod_Lisp

sudo nano /etc/apt/sources.list

deb http://fr.archive.ubuntu.com/ubuntu intrepid main universe

sudo aptitude install libapache2-mod-lisp

sudo a2enmod lisp

sudo /etc/init.d/apache2 restart

LispServer 127.0.0.1 8000 "hunchentoot"



<location "/">

SetHandler lisp-handler

</location>

<location "/static">

SetHandler None

</location>

Hunchentoot is more than capable of being a stand alone webserver, but if your server uses Apache to serve other sites then we need to create a site configuration file so Apache can let Hunchentoot handle requests. Some people also prefer to let Apache handle static pages and Hunchentoot handle the dynamic pages.First we will configure a new site for our Hunchentoot server. This site will serve all URLs to Hunchentoot except for the /static directory. It looks for Hunchentoot on port 8000. Create a new file in /etc/apache2/sites-available called "htoot" and enter the text below. Be sure to modify it to suit your needs.Since we are configuring proxy rules on a site-level basis, comment out everything in the global proxy configuration file. If you run other sites that use mod_proxy be sure they are secure as well!Enable the site with this command:Enable the proxy modules:Restart Apache:Now if you point your browser to your site, an Apache error message should display since we haven't install Hunchentoot yet. You can test your configuration by creating a "/static" directory in your DocumentRoot folder and pointing your browser there.Next up we need to install a lisp implementation on our machine. I created a ~/apps directory on my server and downloaded SBCL. Be sure to change the download file to the version of SBCL you're using. I usually go to the SBCL website to get the correct link for my system.Unpack it:And run the installation script:All done!Slime will be used to access the server while it's running. All we have to do is load "swank-loader.lisp" in the lisp image to use it. Download and upack Slime's package:Be sure to modify the above commands for your particular version of Slime. You should now have slime installed in your ~/apps/slime directory. Now run swank-loader.lisp in SBCL so Slime creates its necessary folders:We will use SBCL's ASDF-INSTALL tool to install Hunchentoot and all of its dependencies. Start SBCL with root privileges:Start the install:Choose to do a System-Wide installation. I chose to skip gpg checking because it's a long and tedious process, but if you're inclined to do so this website is a great source: http://www.pps.jussieu.fr/~jch/software/pgp-validating.html When complete, install cl-who:To test the installation, in SBCL execute the following:Point your browser to your server's 4242 port, such as http://your-domain.com:4242, and you should see the Hunchentoot default page. Quit SBCL before continuing.For security purposes, we will create a new system user to run Hunchentoot called htoot:The Ubuntu way of launching services is to put a control script in the /etc/init.d directory. Luckily, for those of you who are not experienced at writing these scripts, Ubuntu comes with a handy skeleton file that makes a great starting point.First go to /etc/init.d and create a new file with nano called htoot:Copy and paste the following code, being sure to replace the STARTUPSCRIPT file to where you want to put your Hunchentoot start-up lisp script that we will create next:Save the file and exit nano, then make the file executable:The script above starts Hunchentoot by telling SBCL to load the Hunchentoot start-up script, which we will create next. To stop Hunchentoot, we simply telnet to a specific port that the SBCL instance listens to. With this configuration, the Hunchentoot instance runs under the user 'htoot'.Next change to the directory where you want to place the Hunchentoot start-up lisp script and create a new file called "start-hunchentoot.lisp". Copy and paste the code below. Be sure to read over this code, changing the path to your swank-loader file and any other options as you see fit.When you have lisp sites that you want loaded upon start up, you can place their load commands after the ';;; Load any sites here' comment above.Now we're ready to launch Hunchentoot:Point your browser to your site. Instead of the error seen before, you should see the Hunchentoot default page!To start Hunchentoot when Ubuntu boots, issue the following commands:Our start-up scripts also starts up the swank server so we can connect to it via Slime and Emacs. View the Slime Manual for the steps on how to do so. Remember that swank is using port 4006, not the default 4005!1. Sometimes if I use the htoot script to restart Hunchentoot, start-up will fail because port 6440 is in use. Issue the command "netstat -a | grep 6440" and verify it's free before trying to start Hunchentoot. Usually you'll have to wait a few seconds before the previous telnet command to stop Hunchentoot finishes.2. If you have problems starting Hunchentoot during boot-up, you can modify the htoot script to direct output to a file, such as /var/log/boot to help debug. I once did this and found I had issues starting Slime when Ubuntu booted.I hope this article will help people get up and running with Hunchentoot on Ubuntu, which in my opinion is web programming paradise. Feel free to leave any tips or suggestions below.Other links of interest:Older versions of Hunchentoot support the Apache module mod_lisp. Instructions for installing mod_lisp and configuring Apache are as follows:First ensure your software repository list contains the proper source.Open your software repository listIf the file lacks the following line, add it to the end.Save the changes and exit nano. Next install mod_lisp:The file lisp.load should now reside in /etc/apache2/mods-available/. Next enable it in Apache:Then restart Apache:The file lisp.load should now be seen in /etc/apache2/mods-enabled/In the Apache site configuration file, add something like the following:This lets Hunchentoot handle all of your sites pages except for the /static directory.Note that if you are using an older version of Hunchentoot, be sure to modify start-hunchentoot.lisp to use the older start-server and stop-server functions instead of start and stop.