现在我来说明在 4 种开源 Common Lisp 平台上运行 CL-HTTP 的方法,四种平台分别是 SBCL、 Clozure CL、 CMUCL 和 Macintosh Common Lisp (MCL)。 我相信我所提供的这些信息对某些 Common Lisp 爱好者来说将是梦寐以求的。
背景
过去我也曾在博客里多次提到 CL-HTTP,多年来也一直在实际地学习和使用这个软件。概括地说,CL-HTTP 是一个纯 Common Lisp 实现的高级 Web 应用服务器。和 Common Lisp 社区的其他流行 HTTP 服务器,例如 Hunchentoot 和 AllegroServe 相比,CL-HTTP 不仅提供了 HTTP 1.1 协议的完整实现,还提供了一体化的解决方案,包括基于 Lisp 宏的动态 HTML/XHTML 代码生成,面向对象的 Web 交互设计,权限控制,HTTPS 和 HTTP Proxy 支持(带有缓存)等,另外它还提供 HTTP 客户端,SMTP/POP3/FTP 支持以及其他数不清的高级特性。 CL-HTTP 是目前开源 Common Lisp 项目中规模最大、最复杂的,其核心 server 模块源代码 5 万多行,所有模块连同各种平台的移植在内共计 35 万行 Common Lisp 代码(bz2 压缩一下其实只有不到 7M),可谓是浩如烟海,Lisp 新手往往迷失在其庞大的目录树里。
CL-HTTP 最初是在 Symbolics Lisp Machine 上开发的,1994 年开始公开了源代码。作者是 MIT AI 实验室 ( CSAIL) 的教授 John C. Malley,目前主要维护者还包括 Rainer Joswig 和来自几个商业 CL 平台的专家,尤其是 LispWorks 公司的 Martin Simmons。CL-HTTP 的开发工作非常缓慢,但也极度稳定可靠,不过主要是在上一代的很多商业平台上。CL-HTTP 的源代码 SVN 是不公开的,仅少量核心开发者具有访问权限,但是一般可以通过发信给邮件列表向开发者索取最新的源代码。这给追踪其源代码变化带来了一定困难,因此我自己维护了一个私有的 SVN 库将历史上获得的所有代码快照全部提交在里面,再注入我自己的一些补丁。
获得最新的 CL-HTTP 源码
CL-HTTP 网站上目前 公开下载的最后版本 70.190a 已经是很老的版本了,目前的最新版本是 70.218,SVN revision 至少为 436。Rainer Joswig 定期将 SVN 上的最新版本打包放在它的个人主页上,下载地址是:
http://lispm.dyndns.org/distributions/
其中目前的最新版本的下载文件名是 cl-http-70-218-s436.tar.gz。不过由于上游合并补丁的速度非常慢,即便是这个最新版本仍然有一些问题导致 SBCL 下无法顺利加载。推荐使用的是本文附件中的版本,它来自我自行维护的 CL-HTTP 私有 SVN 库,修复了很多 bug,补丁已提交到上游但尚未被合并。有兴趣的读者可以同时下载两个文件然后自行比对其中的差异。
源代码结构
CL-HTTP 的源代码可以大致分为下列 7 个部分:
- 平台无关的核心代码,包括 HTTP/HTTPS 协议本身的实现和 HTML、URL 等标准的实现代码;
- 平台无关的周边代码,包括 HTML 解析器、客户端、SMTP/FTP 等协议的实现等;
- 平台无关的用户贡献代码,包括 POP3 客户端、XML 工具箱以及最重要的可移植模板 (port-template) 代码等;
- 平台相关的核心代码,包括适用于 CMUCL、LispWorks、MCL、SCL 等主要平台的网络和工具性代码等;
- 平台相关的周边代码,例如 LispWorks 和 MCL 下的图形界面控制台等;
- 平台相关的用户贡献代码,例如 LispWorks 下的 UTF-8 支持、远程 Listener 和数据库认证接口等;
- 示例站点的源代码和静态文件。
以下简要介绍 CL-HTTP 的源代码目录结构,最顶层各目录的说明如下表所示:
目录名 |
类别 |
用途说明 |
acl |
平台相关代码 |
Allegro CL 移植 |
client |
平台无关的周边代码 |
HTTP 客户端 |
clim |
平台无关的周边代码 |
基于 CLIM 的图形控制台 |
cmucl |
平台相关代码 |
CMU Common Lisp 移植 |
contrib |
平台无关的用户贡献代码 |
各种有用的扩展,尤其包括可移植模板 (port-template) |
examples |
示例站点 |
示例站点的源代码 |
ftp |
平台无关的周边代码 |
FTP 客户端(代理) |
html-parser |
平台无关的周边代码 |
HTML 解析器 |
lambda-ir |
平台无关的周边代码 |
全文索引 |
lcl |
平台相关代码 |
Lucid/Liquid Common Lisp 移植 |
lispm |
平台相关代码 |
最初在 Symbolics Lisp Machine 上开发的全部代码 |
lw |
平台相关代码 |
LispWorks 移植 |
mcl |
平台相关代码 |
Macintosh Common Lisp 移植 |
proxy |
平台无关的核心代码 |
HTTP Proxy(正反向都有) |
scl |
平台相关代码 |
Scieneer Common Lisp 移植 |
server |
平台无关的核心代码 |
核心 HTTP 协议实现和 HTML 生成代码等 |
smtp |
平台无关的周边代码 |
SMTP 协议实现 |
standards |
静态文件 |
相关 RFC 标准的文本,通过示例站点可访问到 |
w3p |
平台无关的周边代码 |
W3P (Presentation-based Interface) |
w4 |
平台无关的周边代码 |
W4 Constraint-Guided Web Walker |
www |
示例站点 |
示例站点的静态页面和其他数据文件 |
在 SBCL 上启动 CL-HTTP 的方法
正确在 SBCL 上启动 CL-HTTP 的先决条件是必须拥有一个支持多线程 (sb-thread) 的 SBCL,其次一定要使用比较新的版本(目前最后发布的 1.0.49 经测试是可用的)。遗憾的是,目前 SBCL 官方站点提供的二进制版本只在 Linux/x86 和 Linux/AMD64 上默认打开了多线程支持,其他平台要么不支持多线程,要么就是默认关闭的。SBCL 目前官方的 Windows 版本多线程支持不好,推荐改用 Anton Kovalenko 维护的 sbcl-win32-threads 分支,遗憾的是,我一时找不到预编译版本的下载地址了。Mac OS X 用户需要先安装官方的二进制版本,然后使用 SBCL 源代码重新编译支持多线程的版本。这需要在源代码顶层目录里放置一个名为 customize-target-features.lisp 的文件,内容如下所示:
(setf features (remove x features))))
具体的编译和安装方法可参见 SBCL 源代码目录里的 INSTALL 文件。以下假设一个支持多线程的 SBCL 已顺利安装。
在 SBCL 下编译 CL-HTTP 最重要的一环是加载启动文件,它的相对路径是 contrib/kpoeck/port-template/load.lisp。
首先进入 CL-HTTP 源代码根目录,然后启动一个 SBCL 环境并加载上述文件:
binghe@binghe-mac:~/Lisp/self/cl-http$ sbcl
This is SBCL 1.0.49, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
* (load "contrib/kpoeck/port-template/load.lisp")
; loading #P"/Users/binghe/Lisp/self/cl-http/contrib/kpoeck/port-template/load.lisp"
;; loading #P"/Users/binghe/lib/sbcl/sb-introspect/sb-introspect.asd"
;; loading #P"/Users/binghe/lib/sbcl/sb-introspect/introspect.fasl"
;; loading #P"/Users/binghe/Lisp/self/cl-http/contrib/kpoeck/port-template/translations.lisp"
; file: /Users/binghe/Lisp/self/cl-http/contrib/kpoeck/port-template/load.lisp
; in: DEFUN LOAD-THE-THING
; (POTENTIALLY-COMPILE-FILE VALUE)
;
; caught STYLE-WARNING:
; undefined function: POTENTIALLY-COMPILE-FILE
;
; compilation unit finished
; Undefined function:
; POTENTIALLY-COMPILE-FILE
; caught 1 STYLE-WARNING condition
; in: DEFUN LOAD-CL-HTTP-FILES
; (LOAD-CL-HTTP-FILES-INTERNAL *FILES-CL-HTTP*)
;
; caught STYLE-WARNING:
; undefined function: LOAD-CL-HTTP-FILES-INTERNAL
;
; compilation unit finished
; Undefined function:
; LOAD-CL-HTTP-FILES-INTERNAL
; caught 1 STYLE-WARNING condition
Excecute (COMPILE-ALL) to compile the system.
Excecute (LOAD-CL-HTTP-TESTER) to test the system without sockets.
To start the simple-server do (http::start-examples) .
T
忽略那些警告,注意结尾处的三句话:执行 (COMPILE-ALL) 可以编译整个系统,执行 (LOAD-CL-HTTP-TESTER) 可以在不使用网络的情况下测试整个系统;使用 (HTTP::START-EXAMPLES) 启动示例站点。
正确的启动方法是,首先执行 (COMPILE-ALL) 然后 跳过测试,直接使用 (HTTP::START-EXAMPLES) 来启动示例站点。一次完全编译可能需要花几分钟时间,并且每次只能做完全编译(以后会说明怎样节省时间)。启动成功后有类似下面的提示:
[2011-06-30 23:42:32] HTTP Proxy Service Enabled on port 8000 for NIL.
[2011-06-30 23:42:32] HTTP service enabled for: http://localhost:8000/
如果顺利走到这一步了,那么恭喜你,可以打开浏览器访问 http://localhost:8000/,然后就可以看到本文插图中的示例站点 Web 页面了。
(后记:附件的源代码有所更新,修复了一个 port-template 下的小问题,请看到的读者下载新版本 (r75))
新的下载地址:
cl-http-70-218-s436-binghe-r75-fixed-v.part1.rar
cl-http-70-218-s436-binghe-r75-fixed-v.part2.rar
or files.me.com/binghe.lisp/aw4rfb
(未完待续)