Archives | Categories

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-request req.
  • 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-request req.
  • fcgx-getenv is like fcgx-getparam, (fcgx-getenv req) return all the headers/parameters of the http-request req, 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 the wsgi-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. In wsgi-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

7. Discuss and Comment

Have few questions or feedback? Feel free to send me(killian.zhuo📧gmail.com) an email!

Copyright © KDr2, SOME RIGHTS RESERVED UNDER CC BY-NC 4.0.

Built with Emacs 28.2 (Org mode 9.5.5).

Last updated: 2022-01-28 Fri 13:42.