lighttpd与fastcgi+cgilua原理、代码分析与安装

时间:2021-12-31 22:21:59

原理

http://www.cnblogs.com/skynet/p/4173450.html

快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。FastCGI致力于减少Web服务器CGI程式之间互动的开销,从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理,而不是web服务器。(http://www.dwz.cn/yFMap)

lighttpd与fastcgi+cgilua原理、代码分析与安装

当进来一个请求时,Web 服务器把环境变量和这个页面请求通过一个unix domain socket(都位于同一物理服务器)或者一个IP Socket(FastCGI部署在其它物理服务器)传递给FastCGI进程。

lighttpd与fastcgi+cgilua原理、代码分析与安装

l step1. Web 服务器启动时载入初始化FastCGI执行环境 。 例如IIS ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi

l step2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web 服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。

l step3. 当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发到 FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。

l step4. FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。

l step5. FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。

由于 FastCGI 程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI 技术提高 5 倍以上。它还支持分布式的部署, 即 FastCGI 程序可以在web 服务器以外的主机上执行。

总结:CGI 就是所谓的短生存期应用程序,FastCGI 就是所谓的长生存期应用程序。FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。

代码分析

1、 lighttpd 启动过程, 生成fastcgi程序管理器:

对应文件为 mod_fastcgi.c

spawn 模块, 维护可 生成的 fastcgi程序的个数, 进行循环生产(spawn)!

static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_host *host) {

fcgi_proc *proc;

for (proc = host->first; proc; proc = proc->next) {
    int status;

…..

if (fcgi_spawn_connection(srv, p, host, proc)) {
    log_error_write(srv, __FILE__, __LINE__, "s",
            "ERROR: spawning fcgi failed.");
    return HANDLER_ERROR;
}

……

}

每一个fastcgi程序生产过程:

fcgi_spawn_connection

static int fcgi_spawn_connection(server *srv,
                 plugin_data *p,
                 fcgi_extension_host *host,
                 fcgi_proc *proc) {

在此函数中:

创建lighttpd主程序监听socket:

if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
    /* server is not up, spawn it  */
    pid_t child;
    int val;

if (errno != ENOENT &&
        !buffer_string_is_empty(proc->unixsocket)) {
        unlink(proc->unixsocket->ptr);
    }

close(fcgi_fd);

/* reopen socket */
    if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
        log_error_write(srv, __FILE__, __LINE__, "ss",
            "socket failed:", strerror(errno));
        return -1;
    }

val = 1;
    if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
        log_error_write(srv, __FILE__, __LINE__, "ss",
                "socketsockopt failed:", strerror(errno));
        close(fcgi_fd);
        return -1;
    }

/* create socket */
    if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {
        log_error_write(srv, __FILE__, __LINE__, "sbs",
            "bind failed for:",
            proc->connection_name,
            strerror(errno));
        close(fcgi_fd);
        return -1;
    }

if (-1 == listen(fcgi_fd, 1024)) {
        log_error_write(srv, __FILE__, __LINE__, "ss",
            "listen failed:", strerror(errno));
        close(fcgi_fd);
        return -1;
    }

fork出fastcgi子进程:

#ifdef HAVE_FORK
        switch ((child = fork())) {
        case 0: {
            size_t i = 0;
            char *c;
            char_array env;
            char_array arg;

/* create environment */
            env.ptr = NULL;
            env.size = 0;
            env.used = 0;

arg.ptr = NULL;
            arg.size = 0;
            arg.used = 0;

          //  将主程序的生成的  通信socket  fcgi_fd , 作为fastcgi程序的 标准输入

            if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
                close(FCGI_LISTENSOCK_FILENO);
                dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
                close(fcgi_fd);
            }

/* we don't need the client socket */
            for (i = 3; i < 256; i++) {
                close(i);
            }

/* build clean environment */
            if (host->bin_env_copy->used) {
                for (i = 0; i < host->bin_env_copy->used; i++) {
                    data_string *ds = (data_string *)host->bin_env_copy->data[i];
                    char *ge;

if (NULL != (ge = getenv(ds->value->ptr))) {
                        env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
                    }
                }
            } else {
                for (i = 0; environ[i]; i++) {
                    char *eq;

if (NULL != (eq = strchr(environ[i], '='))) {
                        env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1));
                    }
                }
            }

          //  将主程序的生成的 环境 传入到 fastcgi中。

            /* create environment */
            for (i = 0; i < host->bin_env->used; i++) {
                data_string *ds = (data_string *)host->bin_env->data[i];

                env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
            }

for (i = 0; i < env.used; i++) {
                /* search for PHP_FCGI_CHILDREN */
                if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
            }

/* not found, add a default */
            if (i == env.used) {
                env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
            }

env.ptr[env.used] = NULL;

            // 对应lighttpd配置中,设置的cgilua.fcgi脚本对应的路径

           parse_binpath(&arg, host->bin_path);

/* chdir into the base of the bin-path,
             * search for the last / */
            if (NULL != (c = strrchr(arg.ptr[0], '/'))) {
                *c = '\0';

/* change to the physical directory */
                if (-1 == chdir(arg.ptr[0])) {
                    *c = '/';
                    log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[0]);
                }
                *c = '/';
            }

reset_signals();

          //  启动fastcgi程序

            /* exec the cgi */
            execve(arg.ptr[0], arg.ptr, env.ptr);

/* log_error_write(srv, __FILE__, __LINE__, "sbs",
                    "execve failed for:", host->bin_path, strerror(errno)); */

exit(errno);

break;
        }
        case -1:
            /* error */
            break;
        default:
            /* father */

/* wait */
            select(0, NULL, NULL, NULL, &tv);

switch (waitpid(child, &status, WNOHANG)) {
            case 0:
                /* child still running after timeout, good */
                break;
            case -1:
                /* no PID found ? should never happen */
                log_error_write(srv, __FILE__, __LINE__, "ss",
                        "pid not found:", strerror(errno));
                return -1;
            default:
                log_error_write(srv, __FILE__, __LINE__, "sbs",
                        "the fastcgi-backend", host->bin_path, "failed to start:");
                /* the child should not terminate at all */
                if (WIFEXITED(status)) {
                    log_error_write(srv, __FILE__, __LINE__, "sdb",
                            "child exited with status",
                            WEXITSTATUS(status), host->bin_path);
                    log_error_write(srv, __FILE__, __LINE__, "s",
                            "If you're trying to run your app as a FastCGI backend, make sure you're using the FastCGI-enabled version.\n"
                            "If this is PHP on Gentoo, add 'fastcgi' to the USE flags.");
                } else if (WIFSIGNALED(status)) {
                    log_error_write(srv, __FILE__, __LINE__, "sd",
                            "terminated by signal:",
                            WTERMSIG(status));

if (WTERMSIG(status) == 11) {
                        log_error_write(srv, __FILE__, __LINE__, "s",
                                "to be exact: it segfaulted, crashed, died, ... you get the idea." );
                        log_error_write(srv, __FILE__, __LINE__, "s",
                                "If this is PHP, try removing the bytecode caches for now and try again.");
                    }
                } else {
                    log_error_write(srv, __FILE__, __LINE__, "sd",
                            "child died somehow:",
                            status);
                }
                return -1;
            }

/* register process */
            proc->pid = child;
            proc->is_local = 1;

break;
        }
#endif
    } else {

生成程序运行结构(以nginx图借用):

分别对应静态代码的三个部分:

1、 lighttpd部分  对应 Nginx框

2、 mod_fastcgi 对应 socket和 fastcgi wrapper部分,  在主程序中负责 各个fastcgi程序管理。

3、 application 对应 fastcgi程序,对应 多个lua XXX进程。

lighttpd与fastcgi+cgilua原理、代码分析与安装

当连接到来后, mod_fastcgi通过此函数处理连接,

static handler_t fcgi_handle_fdevent(server *srv, void *ctx, int revents) {

---》

mod_fastcgi_handle_subrequest

---》分配cgi子进程

* as soon as hctx->host is unassigned, decrease the load again */
fcgi_host_assign(srv, hctx, host);

---》 向cgi进程中写数据

static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) {

---》 创建cgi环境

if (-1 == fcgi_create_env(srv, hctx, hctx->request_id)) return HANDLER_ERROR;

fcgi_set_state(srv, hctx, FCGI_STATE_WRITE);

---》添加环境

FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)),con)

s = inet_ntop_cache_get_ip(srv, &(con->dst_addr));
FCGI_ENV_ADD_CHECK(fcgi_env_add(p->fcgi_env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)),con)

环境传递?

主程序, 即fastcgi wrapper如何将 非启动的环境变量 , 传递为 cgi程序?

注意到, cgi程序启动时候, 第三个参数, 是环境变量指针:

/* exec the cgi */
execve(arg.ptr[0], arg.ptr, env.ptr);

此env为fork后生成的局部变量:

#ifdef HAVE_FORK
        switch ((child = fork())) {
        case 0: {
            size_t i = 0;
            char *c;
            char_array env;
            char_array arg;

/* create environment */
            env.ptr = NULL;
            env.size = 0;
            env.used = 0;

arg.ptr = NULL;
            arg.size = 0;
            arg.used = 0;

然后

只有继承当前 进程环境的 操作:

/* build clean environment */
if (host->bin_env_copy->used) {
    for (i = 0; i < host->bin_env_copy->used; i++) {
        data_string *ds = (data_string *)host->bin_env_copy->data[i];
        char *ge;

if (NULL != (ge = getenv(ds->value->ptr))) {
            env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
        }
    }
} else {
    for (i = 0; environ[i]; i++) {
        char *eq;

if (NULL != (eq = strchr(environ[i], '='))) {
            env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1));
        }
    }
}

/* create environment */
for (i = 0; i < host->bin_env->used; i++) {
    data_string *ds = (data_string *)host->bin_env->data[i];

env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
}

for (i = 0; i < env.used; i++) {
    /* search for PHP_FCGI_CHILDREN */
    if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;
}

/* not found, add a default */
if (i == env.used) {
    env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
}

env.ptr[env.used] = NULL;

那么环境变量是怎么传入子进程fastcgi中的呢?

我们注意到 fork之后, 父进程会记录子进程的id:

/* register process */
    proc->pid = child;
    proc->is_local = 1;

break;
}

??? 确实是是个问题!!

在子程序中,如何获取环境变量?

答案: environ 全局变量

http://linux.chinaunix.net/techdoc/develop/2007/09/19/968230.shtml

1. get all
进程的环境是一个以NULL字符结尾的字符串之集合。如:
name=something
Linux系统提供了environ指针来访问其变量内容。
如下程序showenv.c演示了通过environ指针变量访问环境变量:
extern char** environ;
main()
{
        char** env=environ;
        while(*env)
        {
                printf("%s\n",*env++);
        }
        return;
}
2. add
为进程指定新的环境,需:execle()和execve()  [in ]
如下程序addenv.c 演示了把新的环境传给上面的showenv.c:
#include
main()
{
        char *argv[]={"showenv.x",NULL},
             *envp[]={"nick=home","home=nick",NULL};
        execve("../exe/showenv.x",argv,envp);
        perror("exeve failed");
        return;
}
结果:
nick=home
home=nick
其实,Linux系统库stdlib.h提供了putenv(),它完成与addenv同样的工作。方法为:putenv("newvariable=value");如成功返回0。它只改变调用进程的环境变量,父进程不变。

例如  lua的 wsapi.lua的  lfcgi.c的 fastcgi接口的  lua扩展, 也是通过 此方法实现。

lighttpd与fastcgi+cgilua原理、代码分析与安装

lighttpd模块化设计(扩展性设计)

此服务器有很多模块, 都是可以主动注册的, 根据 lighttpd.conf配置文件, 可以实现 模块动态库的 加载:

root@fqs:/usr/local/lighttpd/lib#
root@fqs:/usr/local/lighttpd/lib# ls
mod_access.la     mod_cml.la         mod_expire.la         mod_magnet.la       mod_rrdtool.la       mod_ssi.la            mod_usertrack.la
mod_accesslog.la  mod_cml.so         mod_expire.so         mod_magnet.so       mod_rrdtool.so       mod_ssi.so            mod_usertrack.so
mod_accesslog.so  mod_compress.la    mod_extforward.la     mod_mysql_vhost.la  mod_scgi.la          mod_staticfile.la     mod_webdav.la
mod_access.so     mod_compress.so    mod_extforward.so     mod_mysql_vhost.so  mod_scgi.so          mod_staticfile.so     mod_webdav.so
mod_alias.la      mod_dirlisting.la  mod_fastcgi.la        mod_proxy.la        mod_secdownload.la   mod_status.la
mod_alias.so      mod_dirlisting.so  mod_fastcgi.so        mod_proxy.so        mod_secdownload.so   mod_status.so
mod_auth.la       mod_evasive.la     mod_flv_streaming.la  mod_redirect.la     mod_setenv.la        mod_trigger_b4_dl.la
mod_auth.so       mod_evasive.so     mod_flv_streaming.so  mod_redirect.so     mod_setenv.so        mod_trigger_b4_dl.so
mod_cgi.la        mod_evhost.la      mod_indexfile.la      mod_rewrite.la      mod_simple_vhost.la  mod_userdir.la
mod_cgi.so        mod_evhost.so      mod_indexfile.so      mod_rewrite.so      mod_simple_vhost.so  mod_userdir.so
root@fqs:/usr/local/lighttpd/lib#
root@fqs:/usr/local/lighttpd/lib#

mod_fastcgi.c中此模块对外开放的接口为 模块注册函数:

其他定义的函数, 均为 static声明, 在外部不能直接引用,  只有在此函数中 注册到 定义好的 插件结构体中, 才有暴漏的资格。

int mod_fastcgi_plugin_init(plugin *p);
int mod_fastcgi_plugin_init(plugin *p) {
    p->version      = LIGHTTPD_VERSION_ID;
    p->name         = buffer_init_string("fastcgi");

p->init         = mod_fastcgi_init;
    p->cleanup      = mod_fastcgi_free;
    p->set_defaults = mod_fastcgi_set_defaults;
    p->connection_reset        = fcgi_connection_reset;
    p->handle_connection_close = fcgi_connection_close_callback;
    p->handle_uri_clean        = fcgi_check_extension_1;
    p->handle_subrequest_start = fcgi_check_extension_2;
    p->handle_subrequest       = mod_fastcgi_handle_subrequest;
    p->handle_joblist          = mod_fastcgi_handle_joblist;
    p->handle_trigger          = mod_fastcgi_handle_trigger;

p->data         = NULL;

return 0;
}

配置文件中, 可以定制加载那些模块! 我们放开了fastcgi

## modules to load
# at least mod_access and mod_accesslog should be loaded
# all other module should only be loaded if really neccesary
# - saves some time
# - saves memory
server.modules              = (
#                               "mod_rewrite",
#                               "mod_redirect",
#                               "mod_alias",
                                "mod_access",
#                               "mod_cml",
#                               "mod_trigger_b4_dl",
#                               "mod_auth",
#                               "mod_status",
#                               "mod_setenv",
                                "mod_fastcgi",
#                               "mod_proxy",
#                               "mod_simple_vhost",
#                               "mod_evhost",
#                               "mod_userdir",
#                               "mod_cgi",
#                               "mod_compress",
#                               "mod_ssi",
#                               "mod_usertrack",
#                               "mod_expire",
#                               "mod_secdownload",
#                               "mod_rrdtool",
                                "mod_accesslog" )

那么, lighttpd是怎么实现 模块注册的呢?

要看插件模块是怎么实现的? 插件模块相当于模块的基础, 是一个基础设施, 基础模块, 底层模块, 可以类比为自然科学中的基础科学,  计算机科学与技术, 与数学的关系。  按照面向对象设计中 DIP原则(依赖倒置原则), 则应该是高层模块, 即抽象模块, 底层模块依赖高层模块, 具体模块依赖抽象模块, 反过来则不为好的设计, 即如果 高层模块(抽象模块)中含有 低层模块(具体模块), 例如  plugin模块含有 fastcgi模块的信息!

高层模块定义接口, 对应的是  plugin 数据结构!

低层模块, 实现plugin这个数据结构。例如 fastcgi实现了这个插件接口规范。

扯远了, 那个plugin模块怎么实现模块的动态加载的呢?

开头是 插件加载接口

int plugins_load(server *srv) {

实现是: 对于配置中声明的 模块, 进行遍历, 记住模块名为 module

for (i = 0; i < srv->srvconf.modules->used; i++) {
    data_string *d = (data_string *)srv->srvconf.modules->data[i];
    char *module = d->value->ptr;

构造需要加载模块的全路径: xxx/mod_fastcgi.so

buffer_copy_buffer(srv->tmp_buf, srv->srvconf.modules_dir);

buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/"));
        buffer_append_string(srv->tmp_buf, module);
#if defined(__WIN32) || defined(__CYGWIN__)
        buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll"));
#else
        buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so"));
#endif

使用dlopen接口动态加载so文件:

if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) {
    log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:",
        srv->tmp_buf, dlerror());

plugin_free(p);

return -1;
}

构造需要执行的 mod_init函数:  mod_fastcgi_plugin_init

buffer_reset(srv->tmp_buf);
buffer_copy_string(srv->tmp_buf, module);
buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init"));

找到初始化函数指针:

#if 1
        init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr);
#else
        *(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr);
#endif

调用初始化函数:

if ((*init)(p)) {
    log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" );

plugin_free(p);
    return -1;
}

加载完毕的模块注册到插件管理系统中:

plugins_register(srv, p);

static int plugins_register(server *srv, plugin *p) {
    plugin **ps;
    if (0 == srv->plugins.size) {
        srv->plugins.size = 4;
        srv->plugins.ptr  = malloc(srv->plugins.size * sizeof(*ps));
        srv->plugins.used = 0;
    } else if (srv->plugins.used == srv->plugins.size) {
        srv->plugins.size += 4;
        srv->plugins.ptr   = realloc(srv->plugins.ptr, srv->plugins.size * sizeof(*ps));
    }

ps = srv->plugins.ptr;
    ps[srv->plugins.used++] = p;

return 0;
}

安装

lighttpd安装

http://www.lighttpd.net/

http://redmine.lighttpd.net/projects/lighttpd/wiki

1、 先执行

./configure --prefix=/usr/local/lighttpd

缺少pcre库处理方法:

http://*.com/questions/2263404/what-package-i-should-install-for-pcre-devel

apt-cache search pcre | grep -- -dev
apt-get install libpcre3-dev
 
遇到缺少zlib库的情况:
configure: error: zlib-headers and/or libs where not found, install them or build with --without-zlib
 apt-cache search zlib | grep -- -dev
apt-get install  zlib1g-dev
 
遇到bzip2库缺少情况:
configure: error: bzip2-headers and/or libs where not found, install them or build with --without-bzip2
apt-get install libbz2-dev
 
 

2、 编译安装

make

make install

安装wsapi

help报错:

root@fqs:/home/share/wsapi-master/wsapi-master# ./configure --help
./configure: 3: [: --help: unexpected operator
Trying to find where you installed Lua...
Illegal option --
./configure: 22: [: .: unexpected operator
Lua not found, please install Lua 5.1 (and put in your PATH)
root@fqs:/home/share/wsapi-master/wsapi-master#

查看confiure为 需要指定lua路径名

root@fqs:/home/share/wsapi-master/wsapi-master# cat configure
#!/bin/sh

if [ $1 == "--help" ]; then
  echo "Usage: configure lua51"
  echo "where lua51 is the name of your Lua executable"
  exit 0
fi

echo "Trying to find where you installed Lua..."

if [ $1 != "" ]; then
  lua=$1
else
  lua="lua51"
fi

lua_bin=`which $lua`
lua_bin_dir=`dirname $lua_bin`

lua_root=`dirname $lua_bin_dir`

if [ $lua_root != "" ]; then
  echo "Lua is in $lua_root"
  echo "Writing config"
  lua_share=$lua_root/share/lua/5.1
  lua_lib=$lua_root/lib/lua/5.1
  bin_dir=$lua_root/bin
  echo "LIB_OPTION= -shared -fPIC" > config
  echo "LUA_DIR= $lua_share" >> config
  echo "BIN_DIR= $bin_dir" >> config
  echo "LUA_LIBDIR= $lua_lib" >> config
  echo "Now run 'make && sudo make install'"
else
  echo "Lua not found, please install Lua 5.1 (and put in your PATH)"
fi

root@fqs:/home/share/wsapi-master/wsapi-master#

然后,添加本机的lua名称后,提示可以make

root@fqs:/home/share/wsapi-master/wsapi-master# ./configure lua5.1
./configure: 3: [: lua5.1: unexpected operator
Trying to find where you installed Lua...
Lua is in /usr
Writing config
Now run 'make && sudo make install'

make后有报错:

root@fqs:/home/share/wsapi-master/wsapi-master# make
cc   -shared -fPIC -o src/fastcgi/lfcgi.so src/fastcgi/lfcgi.c -lfcgi
src/fastcgi/lfcgi.c:8:24: fatal error: fcgi_stdio.h: No such file or directory
compilation terminated.
Makefile:23: recipe for target 'src/fastcgi/lfcgi.so' failed
make: *** [src/fastcgi/lfcgi.so] Error 1

原因是 fastcgi 没有安装, 安装fastcgi库

root@fqs:/home/share/wsapi-master/wsapi-master# apt-get install libfcgi
Reading package lists... Done
Building dependency tree      
Reading state information... Done
Note, selecting 'libfcgi0ldbl' instead of 'libfcgi'
The following packages were automatically installed and are no longer required:
  linux-headers-4.4.0-21 linux-headers-4.4.0-21-generic linux-image-4.4.0-21-generic linux-image-extra-4.4.0-21-generic
Use 'apt autoremove' to remove them.
The following NEW packages will be installed:
  libfcgi0ldbl
0 upgraded, 1 newly installed, 0 to remove and 71 not upgraded.
Need to get 161 kB of archives.
After this operation, 500 kB of additional disk space will be used.
Get:1 http://us.archive.ubuntu.com/ubuntu xenial/main amd64 libfcgi0ldbl amd64 2.4.0-8.3 [161 kB]
Fetched
161 kB in 2min 17s (1,168
B/s)                                                                                                                                                       

Selecting previously unselected package libfcgi0ldbl.
(Reading database ... 131376 files and directories currently installed.)
Preparing to unpack .../libfcgi0ldbl_2.4.0-8.3_amd64.deb ...
Unpacking libfcgi0ldbl (2.4.0-8.3) ...
Processing triggers for man-db (2.7.5-1) ...
Setting up libfcgi0ldbl (2.4.0-8.3) ...
Processing triggers for libc-bin (2.23-0ubuntu3) ...

make install // 安装 wsapi脚本文件

make install-fcgi // 安装 libfcgi库

cgilua安装

http://luaforge.net/projects/cgilua/

解压直接 make

examples文件夹中的 index.lp文件, 需要将首行申明修改为 cgilua.fcgi

#!/usr/bin/env cgilua.fcgi

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>

访问:

http://192.168.1.108:8081/index.lp

显示:

lighttpd与fastcgi+cgilua原理、代码分析与安装

lighttpd配置

以cgilua安装包中的example为例子运行。

设置web脚本根目录 && 错误日志路径

## a static document-root, for virtual-hosting take look at the
## server.virtual-* options
server.document-root        = "/home/share/cgilua-master/cgilua-master/examples"

## where to send error-messages to
server.errorlog             = "/home/share/cgilua-master/cgilua-master/examples/lighttpd.error.log"

错误日志内容:

root@fqs:/home/share/cgilua-master/cgilua-master/examples# cat lighttpd.error.log
2016-07-03 16:24:22: (log.c.194) server started
2016-07-03
17:35:25: (request.c.311) fd: 7 request-len: 151 \nGET /hello.html
HTTP/1.1\r\nUser-Agent: Wget/1.17.1 (linux-gnu)\r\nAccept:
*/*\r\nAccept-Encoding: identity\r\nHost: localhost:8081\r\nConnection:
Keep-Alive\r\n\r\n
2016-07-03 17:35:25: (response.c.336) -- splitting Request-URI
2016-07-03 17:35:25: (response.c.337) Request-URI     :  /hello.html
2016-07-03 17:35:25: (response.c.338) URI-scheme      :  http
2016-07-03 17:35:25: (response.c.339) URI-authority   :  localhost:8081
2016-07-03 17:35:25: (response.c.340) URI-path (raw)  :  /hello.html
2016-07-03 17:35:25: (response.c.341) URI-path (clean):  /hello.html
2016-07-03 17:35:25: (response.c.342) URI-query       :  
2016-07-03 17:35:25: (mod_access.c.138) -- mod_access_uri_handler called
2016-07-03 17:35:25: (response.c.465) -- before doc_root
2016-07-03 17:35:25: (response.c.466) Doc-Root     : /home/share/cgilua-master/cgilua-master/examples
2016-07-03 17:35:25: (response.c.467) Rel-Path     : /hello.html
2016-07-03 17:35:25: (response.c.468) Path         : 
2016-07-03 17:35:25: (response.c.516) -- after doc_root
2016-07-03 17:35:25: (response.c.517) Doc-Root     : /home/share/cgilua-master/cgilua-master/examples
2016-07-03 17:35:25: (response.c.518) Rel-Path     : /hello.html
2016-07-03 17:35:25: (response.c.519) Path         : /home/share/cgilua-master/cgilua-master/examples/hello.html
2016-07-03 17:35:25: (response.c.536) -- logical -> physical
2016-07-03 17:35:25: (response.c.537) Doc-Root     : /home/share/cgilua-master/cgilua-master/examples
2016-07-03 17:35:25: (response.c.538) Basedir      : /home/share/cgilua-master/cgilua-master/examples
2016-07-03 17:35:25: (response.c.539) Rel-Path     : /hello.html
2016-07-03 17:35:25: (response.c.540) Path         : /home/share/cgilua-master/cgilua-master/examples/hello.html
2016-07-03 17:35:25: (response.c.557) -- handling physical path

将lp中添加到默认显示文件中:

# files to check for if .../ is requested
index-file.names            = ( "index.php", "index.html", "index.lp",
                                "index.htm", "default.htm" )

访问记录日志:

#### accesslog module
accesslog.filename          = "/home/share/cgilua-master/cgilua-master/examples/access.log"

日志内容:

root@fqs:/home/share/cgilua-master/cgilua-master/examples# cat access.log
127.0.0.1 localhost:8081 - [03/Jul/2016:17:35:25 +0800] "GET /hello.html HTTP/1.1" 200 21 "-" "Wget/1.17.1 (linux-gnu)"
127.0.0.1 localhost:8081 - [03/Jul/2016:17:36:24 +0800] "GET /hello.html HTTP/1.1" 200 13 "-" "Wget/1.17.1 (linux-gnu)"
127.0.0.1 localhost:8081 - [03/Jul/2016:17:39:09 +0800] "GET /hello.html HTTP/1.1" 200 13 "-" "Wget/1.17.1 (linux-gnu)"
127.0.0.1 localhost:8081 - [03/Jul/2016:17:40:11 +0800] "GET /hello.html HTTP/1.1" 200 13 "-" "Wget/1.17.1 (linux-gnu)"
192.168.1.104
192.168.1.108:8081 - [03/Jul/2016:17:41:13 +0800] "GET /hello.html
HTTP/1.1" 200 13 "-" "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0)
Gecko/20100101 Firefox/47.0"
192.168.1.104 192.168.1.108:8081 -
[03/Jul/2016:17:41:13 +0800] "GET /favicon.ico HTTP/1.1" 404 345 "-"
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0) Gecko/20100101
Firefox/47.0"
192.168.1.104 192.168.1.108:8081 -
[03/Jul/2016:17:41:13 +0800] "GET /favicon.ico HTTP/1.1" 404 345 "-"
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0) Gecko/20100101
Firefox/47.0"

将lp后缀添加到需要fastcgi处理的列表中:

##
# which extensions should not be handle via static-file transfer
#
# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi", ".lp" )

绑定端口修改:

## bind to port (default: 80)
server.port                = 8081

调试打印功能打开

## enable debugging
debug.log-request-header   = "enable"
debug.log-response-header  = "enable"
debug.log-request-handling = "enable"
debug.log-file-not-found   = "enable"

lp处理句柄描述

fastcgi.server             = ( ".lp" =>
                               ( "localhost" =>
                                 (
                                   "socket" => "/tmp/lp-fastcgi01.socket",
#                                   "bin-path" => "/usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.fcgi"
                                   "bin-path" => "/home/share/cgilua-master/cgilua-master/src/launchers/cgilua.fcgi"
                                 )
                               )
                            )

运行

在 lighttpd.conf 所在目录执行:

/usr/local/lighttpd/sbin/lighttpd  -f lighttpd.conf

查看lighttpd

root@fqs:/home/share/cgilua-master/cgilua-master/examples# ps aux | grep lighttpd
root      2449  0.0  0.2  24084  2260 ?        S    17:38   0:00 /usr/local/lighttpd/sbin/lighttpd -f lighttpd.conf
root      2541  0.0  0.1  14228  1028 pts/1    S+   18:55   0:00 grep --color=auto lighttpd
root@fqs:/home/share/cgilua-master/cgilua-master/examples#
root@fqs:/home/share/cgilua-master/cgilua-master/examples#

查看CGI程序

root@fqs:/home/share/cgilua-master/cgilua-master/examples# ps aux | grep lua
root     
2450  0.0  0.3  22940  2844 ?        S    17:38   0:00 lua
/home/share/cgilua-master/cgilua-master/src/launchers/cgilua.fcgi
root     
2452  0.0  0.3  22940  2884 ?        S    17:38   0:00 lua
/home/share/cgilua-master/cgilua-master/src/launchers/cgilua.fcgi
root     
2454  0.0  0.3  22940  2852 ?        S    17:38   0:00 lua
/home/share/cgilua-master/cgilua-master/src/launchers/cgilua.fcgi
root     
2456  0.0  0.5  23868  3824 ?        S    17:38   0:00 lua
/home/share/cgilua-master/cgilua-master/src/launchers/cgilua.fcgi
root      2539  0.0  0.1  14228  1088 pts/1    S+   18:54   0:00 grep --color=auto lua
root@fqs:/home/share/cgilua-master/cgilua-master/examples#

完整配置

http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ConfigurationOptions

# lighttpd configuration file
#
# use it as a base for lighttpd 1.0.0 and above
#
# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $

############ Options you really have to take care of ####################

## modules to load
# at least mod_access and mod_accesslog should be loaded
# all other module should only be loaded if really neccesary
# - saves some time
# - saves memory
server.modules              = (
#                               "mod_rewrite",
#                               "mod_redirect",
#                               "mod_alias",
                                "mod_access",
#                               "mod_cml",
#                               "mod_trigger_b4_dl",
#                               "mod_auth",
#                               "mod_status",
#                               "mod_setenv",
                                "mod_fastcgi",
#                               "mod_proxy",
#                               "mod_simple_vhost",
#                               "mod_evhost",
#                               "mod_userdir",
#                               "mod_cgi",
#                               "mod_compress",
#                               "mod_ssi",
#                               "mod_usertrack",
#                               "mod_expire",
#                               "mod_secdownload",
#                               "mod_rrdtool",
                                "mod_accesslog" )

## a static document-root, for virtual-hosting take look at the
## server.virtual-* options
server.document-root        = "/home/share/cgilua-master/cgilua-master/examples"

## where to send error-messages to
server.errorlog             = "/home/share/cgilua-master/cgilua-master/examples/lighttpd.error.log"

# files to check for if .../ is requested
index-file.names            = ( "index.php", "index.html", "index.lp",
                                "index.htm", "default.htm" )

## set the event-handler (read the performance section in the manual)
# server.event-handler = "freebsd-kqueue" # needed on OS X

# mimetype mapping
mimetype.assign             = (
  ".pdf"          =>      "application/pdf",
  ".sig"          =>      "application/pgp-signature",
  ".spl"          =>      "application/futuresplash",
  ".class"        =>      "application/octet-stream",
  ".ps"           =>      "application/postscript",
  ".torrent"      =>      "application/x-bittorrent",
  ".dvi"          =>      "application/x-dvi",
  ".gz"           =>      "application/x-gzip",
  ".pac"          =>      "application/x-ns-proxy-autoconfig",
  ".swf"          =>      "application/x-shockwave-flash",
  ".tar.gz"       =>      "application/x-tgz",
  ".tgz"          =>      "application/x-tgz",
  ".tar"          =>      "application/x-tar",
  ".zip"          =>      "application/zip",
  ".mp3"          =>      "audio/mpeg",
  ".m3u"          =>      "audio/x-mpegurl",
  ".wma"          =>      "audio/x-ms-wma",
  ".wax"          =>      "audio/x-ms-wax",
  ".ogg"          =>      "application/ogg",
  ".wav"          =>      "audio/x-wav",
  ".gif"          =>      "image/gif",
  ".jpg"          =>      "image/jpeg",
  ".jpeg"         =>      "image/jpeg",
  ".png"          =>      "image/png",
  ".xbm"          =>      "image/x-xbitmap",
  ".xpm"          =>      "image/x-xpixmap",
  ".xwd"          =>      "image/x-xwindowdump",
  ".css"          =>      "text/css",
  ".html"         =>      "text/html",
  ".htm"          =>      "text/html",
  ".js"           =>      "text/javascript",
  ".asc"          =>      "text/plain",
  ".c"            =>      "text/plain",
  ".cpp"          =>      "text/plain",
  ".log"          =>      "text/plain",
  ".conf"         =>      "text/plain",
  ".text"         =>      "text/plain",
  ".txt"          =>      "text/plain",
  ".dtd"          =>      "text/xml",
  ".xml"          =>      "text/xml",
  ".mpeg"         =>      "video/mpeg",
  ".mpg"          =>      "video/mpeg",
  ".mov"          =>      "video/quicktime",
  ".qt"           =>      "video/quicktime",
  ".avi"          =>      "video/x-msvideo",
  ".asf"          =>      "video/x-ms-asf",
  ".asx"          =>      "video/x-ms-asf",
  ".wmv"          =>      "video/x-ms-wmv",
  ".bz2"          =>      "application/x-bzip",
  ".tbz"          =>      "application/x-bzip-compressed-tar",
  ".tar.bz2"      =>      "application/x-bzip-compressed-tar"
)

# Use the "Content-Type" extended attribute to obtain mime type if possible
#mimetype.use-xattr        = "enable"

## send a different Server: header
## be nice and keep it at lighttpd
server.tag                 = "lighttpd/1.4.11 (Win32)"

#### accesslog module
accesslog.filename          = "/home/share/cgilua-master/cgilua-master/examples/access.log"

## deny access the file-extensions
#
# ~    is for backupfiles from vi, emacs, joe, ...
# .inc is often used for code includes which should in general not be part
#      of the document-root
url.access-deny             = ( "~", ".inc" )

$HTTP["url"] =~ "\.pdf$" {
  server.range-requests = "disable"
}

##
# which extensions should not be handle via static-file transfer
#
# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi", ".lp" )

######### Options that are good to be but not neccesary to be changed #######

## bind to port (default: 80)
server.port                = 8081

## bind to localhost (default: all interfaces)
#server.bind                = "localhost"

## error-handler for status 404
#server.error-handler-404   = "/error-handler.html"
#server.error-handler-404   = "/error-handler.php"

## to help the rc.scripts
#server.pid-file            = "/var/run/lighttpd.pid"

###### virtual hosts
##
##  If you want name-based virtual hosting add the next three settings and load
##  mod_simple_vhost
##
## document-root =
##   virtual-server-root + virtual-server-default-host + virtual-server-docroot
## or
##   virtual-server-root + http-host + virtual-server-docroot
##
#simple-vhost.server-root   = "/home/weigon/wwwroot/servers/"
#simple-vhost.default-host  = "grisu.home.kneschke.de"
#simple-vhost.document-root = "/pages/"

##
## Format: <errorfile-prefix><status-code>.html
## -> ..../status-404.html for 'File not found'
#server.errorfile-prefix    = "/home/weigon/projects/lighttpd/doc/status-"

## virtual directory listings
#dir-listing.activate       = "enable"

## enable debugging
debug.log-request-header   = "enable"
debug.log-response-header  = "enable"
debug.log-request-handling = "enable"
debug.log-file-not-found   = "enable"

### only root can use these options
#
# chroot() to directory (default: no chroot() )
#server.chroot              = "/"

## change uid to <uid> (default: don't care)
#server.username            = "wwwrun"

## change uid to <uid> (default: don't care)
#server.groupname           = "wwwrun"

#### compress module
#compress.cache-dir         = "/tmp/lighttpd/cache/compress/"
#compress.filetype          = ("text/plain", "text/html")

#### proxy module
## read proxy.txt for more info
#proxy.server               = ( ".php" =>
#                               ( "localhost" =>
#                                 (
#                                   "host" => "192.168.0.101",
#                                   "port" => 80
#                                 )
#                               )
#                             )

#### fastcgi module
## read fastcgi.txt for more info
## for PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini
#fastcgi.server             = ( ".php" =>
#                               ( "localhost" =>
#                                 (
#                                   "socket" => "/tmp/php-fastcgi.socket",
#                                   "bin-path" => "/usr/local/bin/php"
#                                 )
#                               )
#                            )

fastcgi.server             = ( ".lp" =>
                               ( "localhost" =>
                                 (
                                   "socket" => "/tmp/lp-fastcgi01.socket",
#                                   "bin-path" => "/usr/local/lib/luarocks/rocks/cgilua/5.1.4-2/bin/cgilua.fcgi"
                                   "bin-path" => "/home/share/cgilua-master/cgilua-master/src/launchers/cgilua.fcgi"
                                 )
                               )
                            )

#### CGI module
#cgi.assign                 = ( ".pl"  => "/usr/bin/perl",
#                               ".cgi" => "/usr/bin/perl" )
#

#### SSL engine
#ssl.engine                 = "enable"
#ssl.pemfile                = "C:/lighttpd/sbin/server.pem"

#### status module
#status.status-url          = "/server-status"
#status.config-url          = "/server-config"

#### auth module
## read authentication.txt for more info
#auth.backend               = "plain"
#auth.backend.plain.userfile = "lighttpd.user"
#auth.backend.plain.groupfile = "lighttpd.group"

#auth.backend.ldap.hostname = "localhost"
#auth.backend.ldap.base-dn  = "dc=my-domain,dc=com"
#auth.backend.ldap.filter   = "(uid=$)"

#auth.require               = ( "/server-status" =>
#                               (
#                                 "method"  => "digest",
#                                 "realm"   => "download archiv",
#                                 "require" => "user=jan"
#                               ),
#                               "/server-config" =>
#                               (
#                                 "method"  => "digest",
#                                 "realm"   => "download archiv",
#                                 "require" => "valid-user"
#                               )
#                             )

#### url handling modules (rewrite, redirect, access)
#url.rewrite                = ( "^/$"             => "/server-status" )
#url.redirect               = ( "^/wishlist/(.+)" => "http://www.123.org/$1" )
#### both rewrite/redirect support back reference to regex conditional using %n
#$HTTP["host"] =~ "^www\.(.*)" {
#  url.redirect            = ( "^/(.*)" => "http://%1/$1" )
#}

#
# define a pattern for the host url finding
# %% => % sign
# %0 => domain name + tld
# %1 => tld
# %2 => domain name without tld
# %3 => subdomain 1 name
# %4 => subdomain 2 name
#
#evhost.path-pattern        = "/home/storage/dev/www/%3/htdocs/"

#### expire module
#expire.url                 = ( "/buggy/" => "access 2 hours", "/asdhas/" => "access plus 1 seconds 2 minutes")

#### ssi
#ssi.extension              = ( ".shtml" )

#### rrdtool
#rrdtool.binary             = "/usr/bin/rrdtool"
#rrdtool.db-name            = "/var/www/lighttpd.rrd"

#### setenv
#setenv.add-request-header  = ( "TRAV_ENV" => "mysql://user@host/db" )
#setenv.add-response-header = ( "X-Secret-Message" => "42" )

## for mod_trigger_b4_dl
# trigger-before-download.gdbm-filename = "/home/weigon/testbase/trigger.db"
# trigger-before-download.memcache-hosts = ( "127.0.0.1:11211" )
# trigger-before-download.trigger-url = "^/trigger/"
# trigger-before-download.download-url = "^/download/"
# trigger-before-download.deny-url = "http://127.0.0.1/index.html"
# trigger-before-download.trigger-timeout = 10

## for mod_cml
## don't forget to add index.cml to server.indexfiles
# cml.extension               = ".cml"
# cml.memcache-hosts          = ( "127.0.0.1:11211" )

#### variable usage:
## variable name without "." is auto prefixed by "var." and becomes "var.bar"
#bar = 1
#var.mystring = "foo"

## integer add
#bar += 1
## string concat, with integer cast as string, result: "www.foo1.com"
#server.name = "www." + mystring + var.bar + ".com"
## array merge
#index-file.names = (foo + ".php") + index-file.names
#index-file.names += (foo + ".php")

#### include
#include /etc/lighttpd/lighttpd-inc.conf
## same as above if you run: "lighttpd -f /etc/lighttpd/lighttpd.conf"
#include "lighttpd-inc.conf"

#### include_shell
#include_shell "echo var.a=1"
## the above is same as:
#var.a=1