SB-FastCGI
Table of Contents
1. Introduction
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.
You can get it from https://github.com/KDr2/sb-fastcgi.
If you want use sb-fastcgi on some other Common Lisp implementations rather than SBCL, you can refer to CL-FastCGI.
2. Getting 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")
3. Low-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 the http-response corresponding to http-requestreq
. - fcgx-read(-all) is for reading data from the input stream of the http-request. usually, you must use it to get the POST data.
- fcgx-getparam is for getting parameter from the http-request
header, e.g.
(fcgx-getparam req "QUERY_STRING")
will return the QUERYSTRING of the http-requestreq
. - fcgx-getenv is like fcgx-getparam,
(fcgx-getenv req)
return all the headers/parameters of the http-requestreq
, in a assoc-list format.
4. Write 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 modfastcgi, 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-socket or a unix-domain-socket.
- the implementations with the postfix "-threaded" runs in multi-thread mode.
See sb-fastcgi/test.lisp
for more examples.
5. The 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-function we 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 the reponse 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.
6. Bug Report
- on github : https://github.com/KDr2/sb-fastcgi
- email me : zhuo.dev<
@
>gmail.com
7. Discuss and Comment
Have few questions or feedback? Feel free to send me(killian.zhuo📧gmail.com) an email!