//start from the very beginning,and to create greatness
//@author: Chuangwei Lin
//@E-mail:979951191@qq.com
//@brief: SHTTPD支持CGI的实现
#include "lcw_shttpd.h"
/******************************************************
函数名: GenerateDirFile(struct worker_ctl *wctl)
参数:
功能:生成目录下的文件列表
*******************************************************/
int GenerateDirFile(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);
char *arg[ARGNUM];
int num = 0;
char *rpath = wctl->conn.con_req.rpath;
stat *fs = &wctl->conn.con_res.fsate;
//打开目录
DIR *dir = opendir(rpath);
if(dir == NULL)
{
//错误
res->status = 500;
retval = -1;
goto EXITgenerateIndex;
}
//建立临时文件保存目录列表
File *tmpfile;
char tmpbuff[2048];
int filesize = 0;
char *uri = wctl->conn.con_req.uri;
//以wb+形式创建一个临时二进制文件
tmpfile = tmpfile();
//标题部分
sprintf(tmpbuff,
"%s%s%s",
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<HTML><HEAD><TITLE>",
uri,
"</TITLE></HEAD>\n" );
fprintf(tmpfile, "%s", tmpbuff);
filesize += strlen(tmpbuff);
//标识部分
sprintf(tmpbuff,
"%s %s %s",
"<BODY><H1>Index of:",
uri,
" </H1> <HR><P><I>Date: </I> <I>Size: </I></P><HR>");
fprintf(tmpfile, "%s", tmpbuff);
filesize += strlen(tmpbuff);
//读取目录中的文件列表
struct dirent *de;
#define PATHLENGTH 2048
char path[PATHLENGTH];
char tmpath[PATHLENGTH];
char linkname[PATHLENGTH];
struct stat fs;//stat 结构定义于:/usr/include/sys/stat.h 文件中
strcpy(path, rpath);
if(rpath[strlen(rpath)]!='/')
{
rpath[strlen(rpath)]='/';
}
while ((de = readdir(dir)) != NULL)//读取一个文件
{
menset(tmpath, 0, sizeof(tmpath));
menset(linkname, 0, sizeof(linkname));
if(strcmp(de->d_name, "."))//不是当前目录
{
if(strcmp(de->d_name, ".."))//不是父目录
{
strcpy(linkname,de->d_name);//将目录名称作为链接名称
}
else//是父目录
{
strcpy(linkname, "Parent Directory");//将父目录作为链接名称
} sprintf(tmpath, "%s%s",path, de->d_name);//构建当前文件的全路径
stat(tmpath, &fs);//获得文件信息
if(S_ISDIR(fs.st_mode))//是一个目录
{
//打印目录的连接为目录名称
sprintf(tmpbuff, "<A HREF=\"%s/\">%s/</A><BR>\n", de->d_name,tmpath);
}
else//正常文件
{
char size_str[32];
off_t size_int;
size_int = fs.st_size;//文件大小
if (size_int < 1024)//不到1K
sprintf(size_str, "%d bytes", (int) size_int);
else if (size_int < 1024*1024)//不到1M
sprintf(size_str, "%1.2f Kbytes", (float) size_int / 1024);
else//其他
sprintf(size_str, "%1.2f Mbytes", (float) size_int / (1024*1024));
//输出文件大小
sprintf(tmpbuff, "<A HREF=\"%s\">%s</A> (%s)<BR>\n", de->d_name, linkname, size_int);
}
//将形成的字符串写入临时文件
fprintf(tmpfile, "%s", tmpbuff);
filesize += strlen(tmpbuff);
}
}
//生成临时的文件信息,主要是文件大小
fs.st_ctime = time(NULL);
fs.st_mtime = time(NULL);
fs.st_size = filesize;
fseek(tmpfile, (long) 0, SEEK_SET);//移动文件指针到头部
EXITgenerateIndex:
return 0;
} #define CGISTR "/cgi-bin/"//CGI目录的字符串
#define ARGNUM 16 //CGI程序变量的最大个数
#define READIN 0 //读出管道
#define WRITEOUT 1 //写入管道
/******************************************************
函数名:cgiHandler(struct worker_ctl *wctl)
参数:
功能:
*******************************************************/
int cgiHandler(struct worker_ctl *wctl)
{
struct conn_request *req = &wctl->conn.con_req;
struct conn_response *res = &wctl->conn.con_res;
//strstr(str1,str2);str1:被查找目标 str2:要查找对象 
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;
char *pos = command;//查找CGI的命令
for(;*pos != '?' && *pos !='\0';pos++);//找到命令尾
{
*pos = '\0';
}
sprintf(rpath, "%s%s",conf_para.CGIRoot,command);//构建全路径
//查找CGI的参数
pos++;
for(;*pos != '\0' && num < ARGNUM;)
{ //CGI的参数为紧跟CGI命令后的?的字符串,多个变量之间用+连接起来,所以可以根据加号的个数确定参数的个数
arg[num] = pos;//参数头
for(;*pos != '+' && *pos!='\0';pos++);
if(*pos == '+')
{
*pos = '\0';//参数尾
pos++;
num++;
}
}
arg[num] = NULL;
//命令的属性
if(stat(rpath,fs)<0)
{
//错误
res->status = 403;
retval = -1;
goto EXITcgiHandler;
}
else if((fs->st_mode & S_IFDIR) == S_IFDIR)
{
//是一个目录,列出目录下的文件
GenerateDirFile(wctl);
retval = 0;
goto EXITcgiHandler;
}
else if((fs->st_mode & S_IXUSR) != S_IXUSR)
{
//所指文件不能执行
res->status = 403;
retval = -1;
goto EXITcgiHandler;
}
//创建进程间通信的管道
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;
}
//进程分叉
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]);//关闭读端
//主进程从CGI的标准输出读取数据 ,并将数据发送到网络资源请求的客户端
int size = 0;//这里初始化为0,怎么进入while循环?改为下面的情况
int end = 0;
//读取CGI进程数据
size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.dres));
while(size > 0 && !end)
{
if(size > 0)
{//将数据发送给客户端
send(wctl->conn.cs, res->res.ptr, strlen(res->res.ptr));
}
else
{
end = 1;
}
size = read(pipe_out[READIN], res->res.ptr, sizeof(wctl->conn.dres));
}
wait(&end);//等待其子进程全部结束
close(pipe_out[READIN]);//关闭管道
close(pipe_in[WRITEOUT]);
retval = 0;
}
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()会从PATH 环境变量所指的目录中查找符合参数file的文件名,
//找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、
//argv[1]……,最后一个参数必须用空指针(NULL)作结束
execlp(rpath, arg);//执行命令,命令的输出需要为标准输出
}
EXITcgiHandler:
return retval;
}

一个简单的wed服务器SHTTPD(4)————SHTTPD支持CGI的实现的更多相关文章

  1. 一个简单的wed服务器SHTTPD(3)————SHTTPD多客户端支持的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  2. 一个简单的wed服务器SHTTPD(9)————main函数文件,Makefile,头文件

    主函数: #include "lcw_shttpd.h" //初始化时服务器的默认配置 extern struct conf_opts conf_para= { "/us ...

  3. 一个简单的wed服务器SHTTPD(1)————命令行和文件配置解析

    开始学习<LInux网络编程>中的综合案例,虽然代码书上有,还是自己打一下加深理解和印象. 主要有两个函数,完成命令行的解析,另一个实现配置文件的解析,注释还是比较丰富的哦. //star ...

  4. 一个简单的wed服务器SHTTPD(6)———— SHTTPD错误处理的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  5. 一个简单的wed服务器SHTTPD(5)————服务器SHTTPD请求方法解析

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  6. 一个简单的wed服务器SHTTPD(7)———— SHTTPD内容类型的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  7. 一个简单的wed服务器SHTTPD(8)———— URI分析

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  8. 一个简单的wed服务器SHTTPD(2)———— 客户端请求分析

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  9. 自己动手模拟开发一个简单的Web服务器

    开篇:每当我们将开发好的ASP.NET网站部署到IIS服务器中,在浏览器正常浏览页面时,可曾想过Web服务器是怎么工作的,其原理是什么?“纸上得来终觉浅,绝知此事要躬行”,于是我们自己模拟一个简单的W ...

随机推荐

  1. Array(数组)对象-->数组值的修改

    1.修改数组值: 数组对象名[下标] = 新值: 举例:原数组如下: var arr = [1,2,3,4,5] 需求:将arr数组第二个元素的值改为10,代码如下: arr[1] = 10; con ...

  2. 数据结构篇-数组(TypeScript版+Java版)

    1.TypeScript版本 export default class MyArray<E> { public data: E[]; public size: number = 0; /* ...

  3. Java创建线程的三种形式的区别以及优缺点

    1.实现Runnable,Callable Callable接口里定义的方法有返回值,可以声明抛出异常. 继承Callable接口实现线程 class ThreadCall implements Ca ...

  4. CVPR2020|3D-VID:基于LiDar Video信息的3D目标检测框架

    作者:蒋天园 Date:2020-04-18 来源:3D-VID:基于LiDar Video信息的3D目标检测框架|CVPR2020 Brief paper地址:https://arxiv.org/p ...

  5. 【转】解决存储过程执行快,但C#程序调用执行慢的问题

    这两天遇到一个问题令人比较郁闷,一个大概120行左右的存储过程在SQL Server2012的查询分析器里面执行,速度非常理想,1秒不到,即可筛选抓取到大概500条数据记录.但在C#程序代码里调用,就 ...

  6. 植物大战僵尸的代码如何使用python来实现

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:程序IT圈 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...

  7. TCP的分分合合(面试必问)

    TCP连接与断开 目录 TCP连接与断开 前言 握手 挥手 最后 前言 相信面试过的小伙伴对这个话题应该不陌生,算是面试必问了,三次握手,四次挥手,以及其中的一些衍生问题. TCP/IP(Transm ...

  8. vs 基础

    1     写入 读取: 1)  写入:Console.Write("hello china")                                      光标紧跟 ...

  9. 立体匹配-----NCC视差匹配

    目录 一.立体匹配算法 1.立体匹配算法分类 二.NCC 视差匹配方法 1.原理 2.NCC计算公式 3.算法流程 4.代码实现     5.不同场景运行 三.结论 四.遇到的问题及解决方法 一.立体 ...

  10. MySQL之唯一索引、外键的变种、SQL语句数据行操作补充

    0.唯一索引 unique对num进行唯一限制,表示num是独一无二的,uql是唯一索引名称 上面为联合索引:num和xx不能完全一样  1.外键的变种 a. 用户表和部门表 用户: 1 alex 1 ...