sb-fastcgi
2024-10-12
FastCGI wrapper for SBCL
1Introduction
sb-fastcgi is a Common Lisp fastcgi API toolkit for SBCL, It contains a group of low-level API's which like the c API of fastcgi, a group of fcgi-server implementations, and a high-level wsgi style interface.
If you want use sb-fastcgi on some other Common Lisp implementations rather than SBCL, you can refer to CL-FastCGI.
2Getting Start
Frist, install SBCL and sb-fastcgi asdf-package. Then get a shared
object of fastcgi, usually named libfcgi.so.*, You can get it by
installing the package libfcgi-dev in your box(e.g. apt-get
install libfcgi-dev
on Debian), after doing that, the shared
object can be found at /usr/lib/libfcgi.so.
Now, you need load the asdf-package and the libfcgi.so:
;;; A1. load sb-fascgi asdf
(asdf:operate 'asdf:load-op 'sb-fastcgi)
;;; A2. load libfcgi.so
(sb-fastcgi:load-libfcgi "/usr/lib/libfcgi.so.0.0.0")
3Low-Level API's
sb-fastcgi provides a group of low-level API's corresponding to the c-API of fastcgi, see the list below:
- fcgx-init
- fcgx-init-request
- fcgx-accept
- fcgx-finish
- fcgx-puts
- fcgx-read
- fcgx-read-all
- fcgx-getparam
- fcgx-getenv
Usually you need not use the first four functions (fcgx-init / fcgx-init-request / fcgx-accept / fcgx-finish), there are some fcgi-server implementations in sb-fastcgi(will be demonstrated in next section), you can use these implementations rather than use the first four functions in the list above.
The last five functions (fcgx-puts / fcgx-read / fcgx-read-all/ fcgx-getparam / fcgx-getenv) is very useful :
- fcgx-puts is for writting content to a http-response, e.g. use
(fcgx-puts req "CONETNT")
to write the string "CONTENT" to thehttp-response corresponding to http-requestreq
. - fcgx-read(-all) is for reading data from the input stream of thehttp-request. usually, you must use it to get the POST data.
- fcgx-getparam is for getting parameter from the http-requestheader, e.g.
(fcgx-getparam req "QUERY_STRING")
will return theQUERY_STRING of the http-requestreq
. - fcgx-getenv is like fcgx-getparam,
(fcgx-getenv req)
return allthe headers/parameters of the http-requestreq
, in a assoc-listformat.
4Write a FCGI-APP
In this section we will finish our first FCGI-APP with sb-fastcgi.
First we write a function called simple-app
, which has one
argument(the http-request), in this function, we use fcgx-puts to
write a simple string to the http-response:
(defun simple-app (req)
(let ((c (format nil "Content-Type: text/plain
Hello, I am a fcgi-program using Common-Lisp")))
(sb-fastcgi:fcgx-puts req c)))
Then we can run this simple-app:
(sb-fastcgi:simple-server #'simple-app)
the simple-server runs the simple-app on stdin, so you can integrate it with apache via mod_fastcgi, or use spawn-fcgi to run it on a listening socket.
There are four fcgi-server implementations in sb-fastcgi now:
(sb-fastcgi:simple-server #'simple-app)
(sb-fastcgi:simple-server-threaded #'simple-app)
(sb-fastcgi:socket-server #'simple-app)
(sb-fastcgi:socket-server-threaded #'simple-app)
- the implementations with the prefix "simple-" runs on stdin.
- the implementations with the prefix "socket-" runs on a inet-socketor a unix-domain-socket.
- the implementations with the postfix "-threaded" runs inmulti-thread mode.
See sb-fastcgi/test.lisp
for more examples.
5The WSGI Style API
If you do not know WSGI, get to know it first, and then see this example:
;;; C1. app using WSGI-style interface
(defun wsgi-app (env start-response)
(funcall start-response "200 OK" '(("X-author" . "Who?")
("Content-Type" . "text/html")))
(list "ENV (show in alist format): <br>" env))
;;; C2. run app above on 0.0.0.0:9000 (by default)
(defun run-app-1 ()
(sb-fastcgi:socket-server-threaded
(sb-fastcgi:make-serve-function #'wsgi-app)
:inet-addr "0.0.0.0"
:port 9000))
- the function
wsgi-app
defined a wsgi-style app - the function call
(sb-fastcgi:make-serve-function #'wsgi-app)
convert thewsgi-app
to a fcgi-app-function in sb-fastcgi. - use
sb-fastcgi:socket-server-threaded
to run the fcgi-app-functionwe just get in the last step.
The wsgi-app can be nested:
;;; C4. a nested WSGI-style app example
(defun wsgi-app2 (app)
(lambda (env start-response)
(let ((content-0 (funcall app env start-response))) ; call outter app
;;reset X-author in headers
(funcall start-response nil '(("X-author" . "KDr2!")))
(append '("Prefix <br/>") content-0 '("<br/>Postfix")))))
;;; C5. run (test-app1 test-app2) nested app
(defun run-app-2 ()
(sb-fastcgi:socket-server-threaded
(sb-fastcgi:make-serve-function (wsgi-app2 #'wsgi-app))
:inet-addr "0.0.0.0"
:port 9000))
- the function
wsgi-app2
accept a wsgi-style-app in its argument,and return a new wsgi-style-app. Inwsgi-app2
you can change thereponse headers, filter or translate the response body... - run the nested wsgi-style-app.
Nested wsgi-style-app make you webapp like an onion, and you can add/remove a layer from it conveniently, use sb-fastcgi's WSGI-Style API you can build/run your webapp or webapp-framework easily.
See sb-fastcgi/test.lisp
for more examples.