just some stuff

apache2, lua and wsapi

15 Oct 2010

So, finally i sat down and setup apache2 with lua via wsapi + fcgid. i have been using apache2 and haserl before, which is relatively easy to set up and insanely fast. however, wsapi offers some advantages, also it works with fast cgi, which is the point of this tutorial. i have not done any speed comparisons between those two options though, maybe i will some day.

My reference system here is Debian GNU/Linux squeeze (the 'testing' branch in July 2010), i doubt that there will be many changes until Debian squeeze is released. I assume you are familiar with lua in general when you read this, so i won't explain the lua code in detail.

First of all you will need a http server, i am using apache2 here, if you haven't installed it yet, do so, that's rather simple:

# apt-get install apache2


2. lua setup

you will need the lua wsapi package:

# apt-get install liblua5.1-wsapi1


this will get you some other packages you need, however, it seems the dependency to coxpcall is missing, so we add this manually to avoid later frustration:

# apt-get install liblua5.1-coxpcall0


As a side note: If you a lua fan and wonder why i do not use luarocks, this is just my way of doing it. i generally do not like bypassing the debian package manager, and squeeze does provide working packages for all we need here.

lua wsapi comes in two flavours, as wsapi.cgi and wsapi.fcgi: we will use fcgi, which is an implementation of the fastcgi protocol ( http://en.wikipedia.org/wiki/FastCGI ).

3. apache2 with fcgid

so far so good, now we want to make apache to play with the fcgi wrapper: edit your vhost file ( e.g. /etc/apache2/sites-available/default ), it should look like something like this:

<VirtualHost *:80> 
ServerAdmin webmaster@localhost 
DocumentRoot /var/www/mysite 
IdleTimeout 60 
IPCCommTimeout 180 
DefaultMinClassProcessCount 0 

<Directory /> Options FollowSymLinks AllowOverride None </Directory>

<Directory /var/www/mysite> Options Indexes FollowSymLinks MultiViews ExecCGI AllowOverride None Order allow,deny allow from all DirectoryIndex index.lua AddHandler fcgid-script .lua FCGIWrapper /usr/bin/wsapi.fcgi .lua </Directory> </VirtualHost>


For the fcid options used here, please refer to <a href="http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html">http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html</a>, there are many more option. As i understand it the options have being renamed between apache 2.2 and 2.3, so look out for that!

We assume here your website is installed in /var/www/mysite/ , you can choose whatever you want, of course.

now, we need apache2 to use fcgid ( fcgid is very similar to the older fastcgi module, with the difference that fastcgi was had some licensing problems).

# apt-get install libapache2-mod-fcgid 
# a2enmod fcgid 
# /etc/init.d/apache2 reload


4. set up a webapplication in lua



so far there is nothing in /var/www/mysite to execute, lets see what to do about this index.lua , it could look like this:

#!/usr/bin/wsapi.cgi module(..., package.seeall) 

function run(wsapi_env) local headers = { ["Content-type"] = "text/html" }

local function output() local html = [[ <html><body> <b>Hello World<b> </body></html> ]] coroutine.yield(html) end

return 200, headers, coroutine.wrap(output)

end



as you see, wsapi does not just execute a lua script and sends the stdout (as you would do with haserl or php-cgi) , the output actually needs to be send explicetly to the fcgi. coroutine.yield(string)

also, if you are familiar you will see that the script is actually a lua module. wsapi will automatically execute the run() function. this might feel odd on the first sight, but in fact is was the way i wrote code even with haserl, as it makes your coding much more organised, in fact it will you savely keep away from noodle code.

5. server environment variables



#!/usr/bin/wsapi.cgi 

module(..., package.seeall)

function run(wsapi_env) local headers = { ["Content-type"] = "text/html"; }

local function output() local html = [[ <html><body> <b>Hello World<b> Server: ]]..wsapi_env.SERVER_NAME...[[ </body></html> ]]

coroutine.yield(html) end

return 200, headers, coroutine.wrap(output)

end


6. fetching input

besides running lua scripts, you most probably want to fetch and parse user input. other then haserl, you need to explicetly request the GET/POST input from the server before using is. to do so, you will need to load the needed API in your script first.

#!/usr/bin/wsapi.cgi 

module(..., package.seeall) function run(wsapi_env) local headers = { [Content-type] = "text/html" } PARAM = wsapi.request.new(wsapi_env)

if not PARAM.GET.myparam then PARAM.GET.myparam = "no input given" end local function output() local html = [[ <html><body> <b>Hello World<b> POST PARAMETER: ]]..PARAM.GET.myparam...[[ </body></html> ]]

coroutine.yield(html)

end return 200, headers, coroutine.wrap(output)

end



7. running a test

www-data 15013  0.0  0.0   5504  1964 ?  S  10:26 0:00  \_ /usr/sbin/apache2 -k start
www-data 15014  0.0  0.0   6136  1980 ?  S  10:26 0:00  \_ /usr/sbin/apache2 -k start
www-data 15072  0.0  0.0   2816  1260 ?  S  10:26 0:00  |   \_ lua5.1 wsapi.fcgi
www-data 15015  0.0  0.1 227684  3068 ?  Sl 10:26 0:00  \_ /usr/sbin/apache2 -k start
www-data 15016  0.0  0.1 227548  2468 ?  Sl 10:26 0:00  \_ /usr/sbin/apache2 -k start


8. one step further, using suexec



looking at the output of "ps -faux" you will see that the fcgi process is running as user "www-data", the default apache user on debian. some people do not like that, as on unixoid systems such users have specific permissions, also all your vhosts will run under the same username. suexec is a mechanism which allows you to run the actual script under a different user then apache2.

# apt-get install apache2-suexec a2enmod suexec /etc/init.d/apache2 now comes a little catch: for security reasons suexec has the path under which it can execute cgi wrappers configured at compile time, being /var/www/ on debian by default. you can of course get the source package and change that, alternatively you can copy your wsapi.fcgi to something like /var/www/bin-mysite/ .

suexec will expect that the fcgi wrapper is owned by the same user as the scripts, therefore do

almost there. now edit your vhost file again to make it use suexec and the new wrapper path:</p> <span style="font-family:Courier New,courier">

<VirtualHost *:80>
ServerAdmin webmaster@localhost        
DocumentRoot /var/www/mysite     
SuexecUserGroup startx startx        
IdleTimeout 60
IPCCommTimeout 180        
DefaultMinClassProcessCount 0        

<Directory /> Options FollowSymLinks AllowOverride None </Directory>

<Directory /var/www/mysite> Options Indexes FollowSymLinks MultiViews ExecCGI AllowOverride None Order allow,deny allow from all DirectoryIndex index.lua AddHandler fcgid-script .lua FCGIWrapper /var/www/startx-bin/wsapi.fcgi .lua </Directory> </VirtualHost>


adjust cgi path in script now restart apache:

# apache2ctl restart


open the website again in your browser, now "ps faux" shows:

# ps faux 
root 9215 0.0 0.1 6136 3764 ? Ss Jul14 0:01 /usr/sbin/apache2 -k start 
www-data 10317 0.0 0.0 5504 1948 ? S Jul14 0:00 \_ /usr/sbin/apache2 -k start 
www-data 10318 0.0 0.0 6136 1988 ? S Jul14 0:00 \_ /usr/sbin/apache2 -k start 
startx 14986 0.0 0.0 2816 1256 ? S 10:21 0:00 | \_ lua5.1 wsapi.fcgi 
www-data 10319 0.0 0.1 227684 3048 ? Sl Jul14 0:00 \_ /usr/sbin/apache2 -k start 
www-data 10320 0.0 0.1 227700 3092 ? Sl Jul14 0:00 \_ /usr/sbin/apache2 -k start


wsapi.fcgi is now running as user "startx", as planned. be aware that this will affect file system permissions, e.g. when you upload or write to files. if susexec is not happy with permissions, you will find error messages in /var/log/apache2/susexec.log .