Content tagged apache

Within a couple weeks of learning Lisp I found programming in any other language unbearably constraining. Paul Graham

The more I play with Lisp, the more I agree with the statement above. The truth is, however, that the barrier to entry is incredibly high for Lisp. This is probably due to the vicious circle: there is fairly few people programming in it in comparison to other languages, so there is relatively little documentation over the Internet; the lack of documentation, in turn, makes it hard for new people to start using Lisp. The effect of this is twofold. If you search for the answers, they are likely not readily available, except in the code itself - this may be pretty frustrating and cause projects to move slowly. On the other hand, the people who make it past the obstacles are pretty good at what they do, so the community is great and the quality of the work they do can be mind-blowing.

But let's get back to the beginning. I want to start writing web apps in Common Lisp. After some research, it turned out that people recommend using one of the frameworks based on Clack for this purpose. I went to the web site, found an example and typed it into repl.

1(ql:quickload :clack)
4  (lambda (env)
5    '(200
6      (:content-type "text/plain")
7      ("Hello, Clack!"))))

Within seconds I had Hunchentoot running and all was fine and dandy. It was, in fact, much easier than what I typically have to do when I am dealing with Python. Before I could go any further, though, I needed to figure out how I deploy this in production in my hosting environment, with Apache as the web server. And here's where the fun begins. :) Apparently, I can run Clack in FastCGI mode, so, in principle, I can just pass :server :fcgi to clackup and be all set. Not so, unfortunately. Clack's fastcgi module, by default, opens a TCP listening socket and expects the web server to connect to it. Apache, however, can communicate with FastCGI only via Unix domain sockets. This is not a problem, because I can pass an :fd parameter to clackup telling it to communicate over this descriptor instead of a TCP connection. All I need to do, is to figure out the path to the socket, open it, and pass the descriptor to Clack.

So, how do you figure out where Apache keeps it's FastCGI unix sockets? The documentation won't tell you, and even if it does, how do you know which one you should connect to? Let's look at the relevant part of the source of Apache's mod_fcgid:

1        || (rv = apr_os_file_put(&file, &unix_socket, 0,
2                                 procnode->proc_pool)) != APR_SUCCESS
3        || (rv = apr_procattr_child_in_set(procattr, file, NULL)) != APR_SUCCESS) {
4        ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server,
5                     "mod_fcgid: couldn't set child process attributes: %s",
6                     unix_addr.sun_path);
7        close(unix_socket);
8        return rv;

It seems that Apache will spawn a child process, replace it's stdin with the unix socket and execve the child's executable. All I need to do then, is pass :fd 0 to Clack to make things work. And indeed they do. After compiling the script below, I could see "Hello, Clack!" with Apache.

 1(ql:quickload 'clack :silent t)
 2(ql:quickload 'lack-middleware-backtrace :silent t)
 3(ql:quickload 'clack-handler-fcgi :silent t)
 5(defun main ()
 6  (clack:clackup
 7    (lambda (env)
 8      '(200
 9        (:content-type "text/plain")
10        ("Hello, Clack!")))
11   :server :fcgi
12   :use-thread nil
13   :fd 0))
15(sb-ext:save-lisp-and-die "test.fcgi" :toplevel #'main :executable t)

This is a fairly trivial thing and it still took me around half an hour to figure out. I am a fairly experienced engineer, but this would likely be an impossible obstacle for someone rather new to programming. By comparison, you can write a web "Hello, World!" in Python or Ruby within minutes without much prior experience.