Commit 055559f6 authored by Dave Cooper's avatar Dave Cooper
Browse files

Added temporary snapshot of current AllegroServe (will carry here

until it manages to get into Quicklisp). Changed default `make serve`
command to publish & serve from CL/aserve instead of Python.
parent 6ee54989
# Emacs autosave
......@@ -11,3 +11,4 @@ system-index.txt
all: build
cd output; python ../
serve: static favicon
sbcl --load build.lisp
mkdir -p output/static
# cp -rf content/static/* output/static
cp -rf content/static/* output/static/
cp content/favicon.ico output/
......@@ -16,3 +16,8 @@ build: static favicon
rm -rf output
mkdir output
# Python server - kept around for compatibility & benchmark testing:
cd output; python ../
......@@ -49,47 +49,55 @@ in `layout/static/` takes precedence.
## Execution
### From command line (recommended)
### From Lisp ### (recommended)
Using Make.
(The following assumes you have a reasonably recent Quicklisp loaded)
The Makefile contains various useful commands.
* Either (in one fell swoop):
Among them:
* `make` - to generate the site.
* `make serve` - to run a simple site server (needs python)
* `make clean` - to remove the generated files from output directory
(load ".../cl-site/build.lisp") ;; replace with your actual path to cl-site/
### From Lisp ###
This will also start serving the site on port 8008 (or subsequent port
numbers, if 8008 is busy). So you can visit the site to test it at:
(The following assumes you have a reasonably recent Quicklisp loaded)
Either (in one fell swoop):
* Or (step by step):
First, make sure your `cl-site/` directory (the directory containing
this file) is pushed onto `ql:*local-project-directories*` or you
have this directory moved or linked under your
`quicklisp/local-projects/` directory (see `build.lisp` for example
of how to push onto `ql:*local-project-directories*`).
(load ".../cl-site/build.lisp") ;; replace with your actual path to cl-site/
Then, load the system with this command:
(ql:quickload :cl-site)
Or (step by step):
To generate the site:
First, make sure your `cl-site/` directory (the directory containing
this file) is pushed onto `ql:*local-project-directories*` or you have
this directory moved or linked under your `quicklisp/local-projects/`
directory (see `build.lisp` for example of how to push onto
Then, load the system with this command:
The output files will be generated in the `output/` directory.
(ql:quickload :cl-site)
To clean any old files from output/ directory:
To generate the site:
The output files will be generated in the `output/` directory.
### From command line
To clean any old files from output/ directory:
Using Make.
The Makefile contains various useful commands.
Among them:
* `make` - to generate the site.
* `make serve` - to run a simple site server on port 8008 or subsequent (currently needs Allegro CL, SBCL, or CCL)
* `make clean` - to remove the generated files from output directory
* `make pyserve` - to run a secondary (python) server on port 8000 (crashes if 8000 is busy).
Prequel to the Gnu Lesser General Public License
Copyright (c) 2016 Franz Inc., Berkeley, CA 94704
Franz Inc. has adopted the concept of the GNU Lesser General Public
License version 2.1 ("LGPL") to govern the use and distribution of
AllegroServe. However, LGPL uses terminology that is more appropriate
for a program written in C than one written in Lisp. Nevertheless,
LGPL can still be applied to a Lisp program if certain clarifications
are made. This document details those clarifications.
Accordingly, the license for AllegroServe consists of this document
plus LGPL. Wherever there is a conflict between this document and
LGPL, this document takes precedence over LGPL.
A "Library" in Lisp is a collection of Lisp functions, data and
foreign modules. The form of the Library can be Lisp source code (for
processing by an interpreter) or object code (usually the result of
compilation of source code or built with some other
mechanisms). Foreign modules are object code in a form that can be
linked into a Lisp executable. When we speak of functions we do so in
the most general way to include, in addition, methods and unnamed
functions. Lisp "data" is also a general term that includes the data
structures resulting from defining Lisp classes.
A Lisp application may include the same set of Lisp objects as does a
Library, but this does not mean that the application is necessarily a
"work based on the Library" it contains.
The AllegroServe Library consists of everything in the AllegroServe
distribution file set before any modifications are made to the files.
If any of the functions or classes in the AllegroServe Library are
redefined in other files, then those redefinitions ARE considered a
work based on the AllegroServe Library. If additional methods are
added to generic functions in the AllegroServe Library, those
additional methods are NOT considered a work based on the AllegroServe
Library. If AllegroServe classes are subclassed, these subclasses are
NOT considered a work based on the AllegroServe Library. If the
AllegroServe Library is modified to explicitly call other functions
that are neither part of Lisp itself nor an available add-on module to
Lisp, then the functions called by the modified AllegroServe Library
ARE considered a work based on the AllegroServe Library. The goal is
to ensure that the AllegroServe Library will compile and run without
getting undefined function errors.
It is permitted to add proprietary source code to the AllegroServe
Library, but it must be done in a way such that the AllegroServe
Library will still run without that proprietary code present.
Section 5 of the LGPL distinguishes between the case of a library
being dynamically linked at runtime and one being statically linked at
build time. Section 5 of the LGPL states that the former results in an
executable that is a "work that uses the Library." Section 5 of the
LGPL states that the latter results in one that is a "derivative of
the Library", which is therefore covered by LGPL. Since Lisp only
offers one choice, which is to link the Library into an executable at
build time, we declare that, for the purpose applying LGPL to the
AllegroServe Library, an executable that results from linking a "work
that uses the AllegroServe Library" with the Library is considered a
"work that uses the Library" and is therefore NOT covered by LGPL.
Because of this declaration, section 6 of LGPL is not applicable to
the AllegroServe Library. However, in connection with each
distribution of this executable, you must also deliver, in accordance
with the terms and conditions of the LGPL, the source code of
AllegroServe Library (or your derivative thereof) that is incorporated
into this executable.
The AllegroServe Webserver
Table of contents
* Description
* Author
* Author comments
* Documentation
* Platforms
* Dependencies
* Installation
* Configuration
* Licence
* Notes
* Examples
* Open Source
AllegroServe has these components:
* HTTP/1.1 compliant web server capable of serving static and dynamic pages
* HTML generation facility that seamlessly merges html tag printing with
computation of dynamic content. The HTML generator matches perfectly
with the HTML parser (which is in another project) to allow web
pages to be read, modifed in Lisp and then regenerated.
* HTTP client functions to access web sites and retrieve data.
* Secure Socket Layer (SSL) for both the server and client.
* Web Proxy facility with a local cache.
* Comprehensive regression test suite that verifies the functionality
of the client, server, proxy and SSL
* high performance for static and dynamic web page delivery
* Licensed under terms that ensure that it will always be open source
and that encourages its use in commercial settings.
* A publish function that builds a page from static and
dynamic data and handles caching of the result.
* Access control mechanisms for publishing directories that
gives the webmaster the ability to specify which files and
directories in the tree should be visible.
* The ability to run external CGI programs.
* An improved virtual hosting system that supports different
logging and error streams for each virtual host.
We've recently added these features:
* The ability to compress and inflate files on the fly.
* Support for chunking and http/1.1
* Security up through TLS 1.0 (SSL 3.1).
See [the latest Allegro CL Release
for more information on AllegroServe changes.
John Foderaro, Franz Inc.
Author comments
The server part of AllegroServe can be used either as a standalone web
server or a module loaded into an application to provide a user
interface to the application. AllegroServe's proxy ability allows it
to run on the gateway machine between a company's internal network and
the internet. AllegroServe's client functions allow Lisp programs to
explore the web.
AllegroServe was also written and open sourced as a way to demonstrate
network programming in Allegro Common Lisp. AllegroServe was written
according to a certain coding standard to demonstrate how Lisp programs
are more readable if certain macros and special forms are avoided.
AllegroServe works on all versions of Allegro Common Lisp since 6.0.
There are no dependences for AllegroServe. In order to run the
allegroserve test suite you'll need to have the tester module
(available at <>) loaded.
### start lisp
This should work in a lisp running in a :case-insensitive-upper or
:case-sensitive-lower mode, although we do most of our running and
testing in a :case-sensitive-lower lisp. The current case mode is the
value of excl:\*current-case-mode\*
### load in the file
user(1): :ld <path-to-aserve>/
it will compile and and load all of AllegroServe, and it will load in
the examples file too.
### start the server
user(2): (net.aserve:start :port 8000)
you can omit the port argument on Windows where any process can
allocate port 80 (as long as it's unused).
### try out the server
go to a web browser and try http://your-machine-name:8000/. If the
web browser is on the same machine as AllegroServe is running you can
use http://localhost:8000/ as well. Now that you've verified that it
works, you'll want to create an aserve.fasl that you can load into your
### change lisp's current directory to the AllegroServe source
user(3): :cd <path-to-aserve>
### make a distribution
user(4): (make-aserve.fasl)
now you'll find aserve.fasl in the aserve source directory.
See the doc/aserve.html file that is part of this project for more
information on configuring AllegroServe.
For complete documentation see the contents of the doc directory,
which is part of this project or visit the online version of the
[AllegroServe documentation](
### Quick Start Documentation
cd to the directory containing the distribution and start Allegro cl
(or start Allegro and use the toplevel ":cd" command to cd to the
directory containing the aserve).
#### load aserve.fasl
user(1): :ld aserve.fasl
#### load the examples (either the compiled or source version)
user(2): :ld examples/examples
#### start the webserver
user(3): (net.aserve:start :port 8010)
#### view in a browser
#### Usage notes
* the steps to load the examples and start the server are interchangeable.
* if you're running on a PC (or running as root on Unix) you
can allocate port 80, so you don't have to specify a port
when running the net.aserve:start function.
* See the doc directory that is part of this project for more
detailed usage documenation.
The aserve source code is licensed under the terms of the
[Lisp Lesser GNU Public License](,
known as the LLGPL. The LLGPL consists of a preamble and the
LGPL. Where these conflict, the preamble takes precedence. This project is
referenced in the preamble as the LIBRARY.
Webactions is a session-based framework for building web sites mixing
static and dynamic content that builds upon AllegroServe and is part
of this project.
See the webactions/doc/webactions.html file for more information.
For other links that may be of interest are:
* [Portable AllegroServe](
* [Source to the if* macro](
* [CL-HTTP](
Examples and Information
See the doc/tutorial.html file and the contents under the examples
directory that are part of this project for more examples on how to
work with AllegroServe.
Franz Inc. Open Source Info
This project's homepage is <>. There is an
informal community support and development mailing list
for these open source projects. We encourage you to take advantage by
subscribing to the list. Once you're subscribed, email to
<> with your questions, comments, suggestions, and
;;; ASD file contributed by james anderson <>
(in-package :cl-user)
(defvar *loadswitch* :compile-if-needed)
(defparameter *aserve-root* (directory-namestring *load-pathname*))
(unless (find-class 'asdf::cl-file nil)
(defclass asdf::cl-file (asdf:cl-source-file) ())
(defmethod asdf:source-file-type ((c asdf::cl-file) (s asdf:module)) "cl"))
#-(or allegro ccl sbcl) (error "This version of aserve is not yet supported on ~a~%" (lisp-implementation-type))
(asdf:defsystem :aserve
:depends-on (#+(or ccl sbcl) :zacl)
;; this list is in cl/src/sys/ as well... keep in sync
((:module "htmlgen" :components ((:cl-file "htmlgen")
(:static-file "ChangeLog")))
(:cl-file "packages")
(:cl-file "macs")
(:cl-file "queue")
(:cl-file "main")
(:cl-file "headers")
(:cl-file "parse")
(:cl-file "decode")
(:cl-file "publish")
(:cl-file "authorize")
(:cl-file "log" )
(:cl-file "client")
(:cl-file "proxy")
(:cl-file "cgi")
(:cl-file "chunker")
(:cl-file "playback")
(:static-file "")
(:static-file "ChangeLog")
(:static-file "license-lgpl.txt")
(:static-file "LICENSE")
(:static-file "load"))
:serial t)
;; -*- mode: common-lisp; package: net.aserve -*-
;; See the file LICENSE for the full license governing this code.
;; Description:
;; classes and functions for authorizing access to entities
;;- This code in this file obeys the Lisp Coding Standard found in
(in-package :net.aserve)
(eval-when (compile) (declaim (optimize (speed 3))))
(defclass authorizer ()
;; denotes information on authorizing access to an entity
;; this is meant to be subclassed with the appropriate slots
;; for the type of authorization to be done
;; - password authorization.
(defclass password-authorizer (authorizer)
((allowed :accessor password-authorizer-allowed
;; list of conses (name . password)
;; which are valid name, password pairs for this entity
:initarg :allowed
:initform nil)
(realm :accessor password-authorizer-realm
:initarg :realm
:initform "AllegroServe")
;; Mention class in make-instance after class def to avoid bug24329.
(defun make-instance-password-authorizer+realm+allowed (realm allowed)
(make-instance 'password-authorizer :realm realm :allowed allowed))
(defmethod authorize ((auth password-authorizer)
(req http-request)
(ent entity))
;; check if this is valid request, return t if ok
;; and :done if we've sent a request for a new name and password
(multiple-value-bind (name password) (get-basic-authorization req)
(if* name
then (dolist (pair (password-authorizer-allowed auth))
(if* (and (equal (car pair) name)
(equal (cdr pair) password))
then (return-from authorize t))))
;; valid name/password not given, ask for it
(with-http-response (req *dummy-computed-entity*
:response *response-unauthorized*
:content-type "text/html"
:format :text)
(set-basic-authorization req
(password-authorizer-realm auth))
; this is done to preventing a chunking response which
; confuse the proxy (for now)..
(if* (member ':use-socket-stream (request-reply-strategy req))
then (setf (request-reply-strategy req)
(with-http-body (req *dummy-computed-entity*)
(html (:html (:body (:h1 "Access is not authorized"))))
;; location authorization
;; we allow access based on where the request is made from.
;; the pattern list is a list of items to match against the
;; ip address of the request. When the first match is made the
;; request is either accepted or denied.
;; the possible items in the list of patterns
;; :accept accept immediately
;; :deny deny immediately
;; (:accept ipaddress [bits]) accept if left 'bits' of the
;; ipaddress match
;; (:deny ipaddress [bits]) deny if the left 'bits' of the
;; ipaddress match
;; bits defaults to 32
;; the ipaddress can be an
;; integer - the 32 bit ip address
;; string
;; "" - the dotted notation for an ip address
;; "" - the name of a machine
;; when the ipaddress is a string it is converted to an integer
;; the first time it is examined.
;; When the string is a machine name then the conversion may or
;; may not work due to the need to access a nameserver to do
;; the lookup.
(defclass location-authorizer (authorizer)
((patterns :accessor location-authorizer-patterns
;; list of patterns to match
:initarg :patterns
:initform nil)))
;; Mention class in make-instance after class def to avoid bug24329.
(defun make-instance-location-authorizer+patterns (patterns)
(make-instance 'location-authorizer :patterns patterns))
(defmethod authorize ((auth location-authorizer)
(req http-request)
(ent entity))
(let ((request-ipaddress (socket:remote-host (request-socket req))))
(dolist (pattern (location-authorizer-patterns auth))
(if* (atom pattern)
then (case pattern
(:accept (return-from authorize t))
(:deny (return-from authorize nil))
(t (warn "bogus authorization pattern: ~s" pattern)
(return-from authorize nil)))
else (let ((decision (car pattern))
(ipaddress (cadr pattern))
(bits (if* (cddr pattern)
then (caddr pattern)
else 32)))
(if* (not (member decision '(:accept :deny)))
then (warn "bogus authorization pattern: ~s" pattern)
(return-from authorize nil))
(if* (stringp ipaddress)
then ; check for dotted ip address first
(let ((newaddr (socket:dotted-to-ipaddr ipaddress
:errorp nil)))
(if* (null newaddr)
then ; success!
(setq newaddr (socket:lookup-hostname ipaddress))))
(if* newaddr
then (setf (cadr pattern)
(setq ipaddress newaddr))
else ; can't compute the address
; so we'll not accept and we will deny
; just to be safe
(warn "can't resolve host name ~s" ipaddress)
(return-from authorize nil))))
(if* (not (and (integerp bits) (<= 1 bits 32)))
then (warn "bogus authorization pattern: ~s" pattern)
(return-from authorize nil))
; now we're finally ready to test things
(let ((mask (if* (eql bits 32)
then -1
else (ash -1 (- 32 bits)))))
(if* (eql (logand request-ipaddress mask)
(logand ipaddress mask))
then ; matched,
(case decision
(:accept (return-from authorize t))
(:deny (return-from authorize nil))))))))
t ; the default is to accept
;; - function authorization
(defclass function-authorizer (authorizer)
((function :accessor function-authorizer-function