18.3.7 SHTTPD支持CGI的实现

18.3.7  SHTTPD支持CGI的实现

CGI支持的实现主要包含CGI命令获取、CGI参数获取、管道进程间连接、主进程从CGI进程读取数据和发送数据、CGI进程执行并发送结果给主进程。

1)实现CGI的过程,先初始化变量,这已经是比较通用的方法。

 

#define CGISTR "/cgi-bin/"              /*CGI目录的字符串*/

#define ARGNUM 16                        /*CGI程序变量的最大个数*/

#define READIN 0                        /*读出管道*/

#define WRITEOUT 1                        /*写入管道*/

int cgiHandler(struct worker_ctl *wctl)

{

    struct conn_request *req = &wctl->conn.con_req;

    struct conn_response *res = &wctl->conn.con_res;

    char *command = strstr(req->uri, CGISTR) + strlen(CGISTR);
                                        /
*获得匹配字符串/cgi-bin/之后的地址*/

    char *arg[ARGNUM];

    int num = 0;

    char *rpath = wctl->conn.con_req.rpath;

    stat *fs = &wctl->conn.con_res.fsate;

    int retval = -1;

 

2)将指针指向字符串CGI命令,并找到之后的“?”或者结束符,作为CGI命令的字符串,在字符串末尾用“/0”填充,构成字符串,并与CGIRoot共同生成一个CGI全路径命令。

 

char *pos = command;                                     /*查找CGI的命令*/

for(;*pos != '?' && *pos !='/0';pos++)                    /*找到命令末尾*/

    ;

*pos = '/0';

sprintf(rpath, "%s%s",conf_para.CGIRoot,command);         /*构建全路径*/

 

3CGI的参数为紧跟CGI命令后“?”的字符串,多个变量之间用“+”连接起来。所以可以根据“+”的数量确定参数的个数,这里假设参数最多有16个。参数放在arg中,参数的个数由变量num确定。

 

/*CGI的参数*/

pos++;

for(;*pos != '/0' && num < ARGNUM;)

{

    arg[num] = pos;

    for(;*pos != '+' && *pos!='/0';pos++)

        ;

    if(*pos == '+')

    {

        *pos = '/0';

        pos++;

        num++;

    }

}

arg[num] = NULL;

 

4)查看CGI命令的属性,确定不是目录并且可以执行。

 

/*命令的属性*/

if(stat(rpath,fs)<0)

{

    /*错误*/

    res->status = 403;

    retval = -1;

    goto EXITcgiHandler;

}

else if((fs->st_mode & S_IFDIR) == S_IFDIR)

{

    /*是一个目录,列出目录下的文件*/

   

}

else if((fs->st_mode & S_IXUSR) != S_IXUSR)

{

    /*所指文件不能执行*/

    res->status = 403;

    retval = -1;

    goto EXITcgiHandler;

}

 

5)创建管道,用于进程间通信。

 

/*创建进程间通信的管道*/

int pipe_in[2];

int pipe_out[2];

if(pipe[pipe_in] < 0)

{

    res->status = 500;

    retval = -1;

    goto EXITcgiHandler;

}

if(pipe[pipe_out] < 0)

{

    res->status = 500;

    retval = -1;

    goto EXITcgiHandler;

}

 

6)将进程分叉,主进程处理客户端的连接,CGI进程处理CGI脚本。在主进程中,关闭管道pipe_out的写和管道pipe_in的读。

 

/*进程分叉*/

int pid = 0;

pid = fork();

if(pid < 0)                            /*错误*/

{

    res->status = 500;

    retval = -1;

    goto EXITcgiHandler;

}

else if(pid > 0)                    /*父进程*/

{

    close(pipe_out[WRITEOUT]);

    close(pipe_in[READIN]);

 

7)主进程从CGI端的标准输出读取数据,并将数据发送到网络资源请求的客户端。当CGI进程端结束后,等待其全部子进程的结束。最后关闭管道。

 

    int size = 0;

    int end = 0;

    while(size > 0 && !end)

    {

        size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.
        dres));                              /
*读取CGI进程端数据*/

        if(size > 0)

        {

            send(wctl->conn.cs, res->res.ptr, strlen(res->res.ptr));
                                            /
*将数据发送给客户端*/

        }

        else

        {

            end = 1;

        }

    }

    wait(&end);                              /*等待其子进程全部结束*/

    close(pipe_out[READIN]);                /*关闭管道*/

    close(pipe_in[WRITEOUT]);

    retval = 0;

}

 

8)在CGI进程中,先将客户端发送过来的CGI脚本及参数形成一个字符串,然后将pipe_out管道的写端和标准输出绑定在一起。这个管道和主进程的读端是连在一起的,当CGI进程的程序向标准输出发送数据的时候,主进程中会收到其发送的数据。最后执行       脚本。

 

    Else                                    /*子进程*/

    {

        char cmdarg[2048];

        char onearg[2048];

        char *pos = NULL;

        int i = 0;

        /*形成执行命令*/

        memset(onearg, 0, 2048];

        for(i = 0;i<num;i++)

            sprintf(cmdarg,"%s %s", onearg, arg[i]);

        /*将写入的管道绑定到标注输出*/

        close(pipe_out[READIN]);            /*关闭无用的读管道*/

        dup2(pipe_out[WRITEOUT], 1);     /*将写管道绑定到标注输出*/

        close(pipe_out[WRITEOUT]);         /*关闭写管道*/

        close(pipe_in[WRITEOUT]);   

        dup2(pipe_in[READIN], 0);  

        close(pipe_in[READIN]);  

       

        execlp(rpath,  arg);           /*执行命令,命令的输出需要为标准输出*/

 

    }

EXITcgiHandler:

    return retval;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值