一个简单的wed服务器SHTTPD(4)————SHTTPD支持CGI的实现
2015-08-12 13:32
615 查看
[code]//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(3)————SHTTPD多客户端支持的实现
- 一个简单的wed服务器SHTTPD(2)———— 客户端请求分析
- 普通开发者的网络安全必读--网络安全,黑客阅读
- 33 网络相关函数(一)——live555源码阅读(四)网络
- 一个简单的wed服务器SHTTPD(1)————命令行和文件配置解析
- Asp.Net customErrors与httpErrors的区别
- linux 服务器/客户端 tcp通信的简单例子
- NM常用网络命令
- Http 和 Https 访问请求
- HttpURLConnection&HttpClient网络通信
- Dynamic Adaptive Streaming ove HTTP(DASH) Design Principles and Standards
- Dynamic Adaptive Streaming over HTTP Dataset
- HttpWebRequest抓取网页内容与直接输入URL得到的内容不一致!球大神帮忙!!
- POJ 2396 Budget(无源汇网络有上下界的可行流-Dinic)
- 通过端口1433连接到主机127.0.0.1的 TCP/IP 连接失败,错误:“connect timed out”的解决方法
- Http协议学习(1)
- ntp时间服务器以及虚拟机同步网络时间
- Volley发送简单的网络请求(Sending a Simple Request)
- Android 使用 HttpClient 进行网络通信,包括Get方式和Post方式(转)
- http状态码