⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mini_httpd.c

📁 red had Linux上的迷你http服务器
💻 C
📖 第 1 页 / 共 2 页
字号:

				state = HDR;
			}
			break;

			/* 后两个case是用来处理消息头的,将得到的消息名和值分别存入request结构的nv数组中 */
		case HDR:
			/* 此case是用来得到头名字的 */
			if (*p == ':')
			{
				v.ptr = p + 1;
				state = HDRVAL;
			}
			else if (*p == '\n')
			{
				/* 单独一个回车换行代表所有消息头结束 */
				reqptr->totlen = p - buffer + 1;

				/* 若请求方法是POST,则将跟在请求头后面的实体体存入request结构的argstr中 */
				p++;
				if(vcasecmp(&tmpv, &reqptr->method) == 1)
				{
					reqptr->argstr.ptr = p;
					reqptr->argstr.len = buffer+buflen-p;
				}
				return reqptr;
			}
			else if (h.len == 0 && *p == ' ')
			{
				/* 过滤前置空格 */
				h.ptr = p + 1;
			}
			else
			{
				h.len++;
			}
			break;
		
		case HDRVAL:
			/* 此case是用来得到头名字所对应的值 */
			if (*p == '\n')
			{
				if (v.len && p[-1] == '\r')
				{
					v.len--;
				}
				/* Save header */
				hdr = &reqptr->header[reqptr->numOfHeader++];

				if (reqptr->numOfHeader < MAXHEADER)
				{
					hdr->name = h;
					hdr->value = v;
					/* 调用cb回调函数,设置conn的相应的成员变量 */
					if (cb)
					{
						cb(hdr, reqptr);
					}
				}
				h.ptr = p + 1;
				v.ptr = NULL;
				h.len = v.len = 0;
				state = HDR;
			}
			else if (v.len == 0 && *p == ' ')
			{
				/* 过滤前置空格 */
				v.ptr = p + 1;
			}
			else
			{
				v.len++;
			}
			break;
		}
	}
	return NULL;
}

/*
 * parseURL():分析request结构中的url,将其分为uri和argstr(参数)两部分,
 *			  并解析argstr中的内容,将参数分别存入arg[]数组中.
 */
int
parseURL(struct request *reqp)
{
	char *p;
	char *q;
	char *r;
	char *end;
	
	/* 将url以‘?’为界分割开 */
	p = strchr(reqp->url.ptr, '?');

	/* 无参数并且argstr为空 */
	if(p == NULL && reqp->argstr.len == 0)
	{

		reqp->numOfArg = 0;
		reqp->uri = reqp->url;
	}

	/* 有参数 */
	else
	{
		/* 有‘?’,说明请求方法是GET,参数序列在url中给出 */
		if(p != NULL)
		{
			end = (char*)(reqp->url.ptr+reqp->url.len);
			reqp->uri.ptr = reqp->url.ptr;
			reqp->uri.len = p - (char *)(reqp->url.ptr);
			reqp->argstr.ptr = p+1;
			reqp->argstr.len = end-p-1;
			p++;
		}
		/* 没有‘?’,但argstr不为空,说明请求方法是POST,参数序列已经由parseRequest()函数存入argstr中 */
		else if(p == NULL)
		{
			reqp->uri = reqp->url;
			p = (char *)reqp->argstr.ptr;
			end = p+reqp->argstr.len;
		}
		

		/* 解析参数序列,将参数分别存入argVal和argName数组中 */
		for(q = strchr(p, '='); q != NULL && p<end && q<end; p=r+1, q = strchr(r, '='))
		{
			if(reqp->numOfArg < MAXARG)
			{
				reqp->arg[reqp->numOfArg].name.ptr = p;
				reqp->arg[reqp->numOfArg].name.len = q-p;
				q++;
				reqp->arg[reqp->numOfArg].value.ptr = q;

				r = strchr(q, '&');
				if(r == NULL)
				{
					reqp->arg[reqp->numOfArg++].value.len = end-q;
					return 0;				
				}
				else
				{
					reqp->arg[reqp->numOfArg++].value.len = r-q;
				}
			}
			else
			{
				showError("parseURL : numOfArg out of MAXARG");
				return -1;
			}
		}
	}
}

/*
 * cgi处理
 */
int
dealCgi(struct request *reqp, int fdclient)
{
	char *p,					/* 临时存储环境变量的内容 */
		 qs[BUFSIZ],			/* url中的?后面的参数列表 */
		 gi[BUFSIZ],			/* cgi版本号 */
		 sn[BUFSIZ],			/* 被请求的脚本文件名 */
		 path[BUFSIZ],			/* 环境变量PATH的内容 */
		 ct[BUFSIZ],			/* 存储CONTENT_TYPE */
		 rm[BUFSIZ],			/* 请求方法(GET、HEAD、POST等) */
		 pl[BUFSIZ],			/* perl库 */
		 user[BUFSIZ],			/* 存储REMOTE_USER */
		 cookie[BUFSIZ],		/* 存储HTTP_COOKIE */
	     cl[BUFSIZ],			/* 存储CONTENT_LENGTH */
		 *env[20];				/* 用来存储环境变量,以便向execve()函数传送参数 */
	char *argv[MAXARG];			/* 用来存储url中的cgi参数,以便向execve()函数传送参数 */
	int i;						/* 循环变量 */

	/* 设置环境变量 */
	env[0] = gi;
	(void) snprintf(gi, sizeof(gi), "GATEWAY_INTERFACE=CGI/1.1");

	env[1] = sn;
	(void) snprintf(sn, sizeof(sn), "SCRIPT_NAME=%.*s", reqp->uri.len, (char *)reqp->uri.ptr);

	env[2] = path;
	/* getenv():用来取出以参数为名字的环境变量内容 */
	if ((p = getenv("PATH")) == NULL)
	{
		p = "/bin:/sbin:/usr/bin:/usr/sbin";
	}
	(void) snprintf(path, sizeof(path), "PATH=%s", p);
	
	env[3] = qs;
	(void) snprintf(qs, sizeof(qs), "QUERY_STRING=%.*s", reqp->argstr.len, (char *)reqp->argstr.ptr);

	env[4] = ct;
	(void) snprintf(ct, sizeof(ct), "CONTENT_TYPE=%s", "text/html");

	env[5] = rm;
	(void) snprintf(rm, sizeof(rm), "REQUEST_METHOD=%.*s", reqp->method.len, (char *)reqp->method.ptr);
	
	env[6] = pl;
	if ((p = getenv("PERLLIB")) == NULL)
	{
		p = "/perl";
	}
	(void) snprintf(pl, sizeof(pl), "PERLLIB=%s", p);
	
	env[7] = user;
	(void) snprintf(user, sizeof(user), "REMOTE_USER=%s", "Mr. Bin");

	env[8] = cookie;
	(void) snprintf(cookie, sizeof(cookie), "HTTP_COOKIE=%.*s", reqp->cookie.len, (char *)reqp->cookie.ptr);

	env[9] = cl;
	(void) snprintf(cl, sizeof(cl), "CONTENT_LENGTH=%d", vtoint(&reqp->clength));
	
	env[10] = ct;
	(void) snprintf(ct, sizeof(ct), "CONTENT_TYPE=%.*s", reqp->ctype.len, (char *)reqp->ctype.ptr);
	
	env[11] = NULL;

	/* 设置参数 */
	argv[0] = (char *)malloc(sizeof(char)*(reqp->uri.len+1));
	strcpy(argv[0],docroot);
	sprintf(argv[0]+strlen(docroot), "%.*s", reqp->uri.len, reqp->uri.ptr);
	for(i = 1; i <= reqp->numOfArg; i++)
	{
		argv[i] = (char *)malloc(sizeof(char)*(reqp->arg[i-1].value.len+1));
		sprintf(argv[i], "%.*s", reqp->arg[i-1].value.len, reqp->arg[i-1].value.ptr);
	}

	/* 发送响应头 */
	sendheaders(reqp, fdclient);

	/* 
	 * execve(const char *filename, char *const argv[],char *const envp[]):
	 * 用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指
	 * 针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组.
	 * 如果执行成功则函数不会返回,执行失败则直接返回-1.
	 */
	if(execve(argv[0], argv, env) == -1)
	{
		showError("dealCgi : execve");
		return -1;
	}
}

/*
 * redirect():将子进程的标准输入重定向为pipe1的读出端,标准输出端重定向
 *			  为pipe2的输入端,父进程不需要pipe1的读出端和pipe2的写入端.
 */
int
redirect(int *pipe1, int *pipe2, int flag)
{
	/* 子进程的调用 */
	if(flag == 0)
	{
		/* 子进程不需要pipe1的写入端和pipe2的读出端 */
		close(pipe1[1]);
		close(pipe2[0]);

		/* 将子进程的标准输入重定向为pipe1的读出端,标准输出端重定向为pipe2的输入端 */
		if(dup2(pipe1[0], 0) == -1 || dup2(pipe2[1], 1) == -1)
		{
			showError("redirect : son : dup2");
			return -1;
		}
		close(pipe1[0]);
		close(pipe2[1]);
	}
	
	/* 父进程的调用 */
	else
	{
		/* 父进程不需要pipe1的读出端和pipe2的写入端 */
		close(pipe1[0]);
		close(pipe2[1]);

		/* 将父进程的标准输入重定向为pipe2的读出端 */
		if(dup2(pipe2[0], 0) == -1)
		{
			showError("redirect : father : dup2");
			printf("%s\n",strerror(errno));			return -1;
		}
		close(pipe2[0]);
	}

	return 0;
}

/*
 * reply():对cgi程序发来的结果进行处理(待扩展),之后发送给客户端
 */
intreply(int fdclient)
{
	char replystr[BUFSIZ];
	int i = 0;
	
	while((replystr[i++] = fgetc(stdin)) != EOF)
	{
		if(i >= BUFSIZE)
		{
			showError("reply : replystr out of BUFSIZE");
			return -1;
		}
	}
	i--;
	replystr[i] = '0';

	if(send(fdclient, replystr, i, 0) == -1)
	{
		showError("reply : send");
		return -1;
	}

	return 0;
}

/*
 * htoi():将url编码汉字的%后的十六位数转换成int型的
 */
int
htoi(char *data)
{
	char *digits="0123456789ABCDEF";
	
	if(islower(data[0]))
	{
		data[0] = toupper(data[0]);
	}
	if(islower(data[1]))
	{
		data[1] = toupper(data[1]);
	}
		
	return 16*(strchr(digits,data[0])-strchr(digits,'0')) + (strchr(digits,data[1])-strchr(digits,'0'));
}
	
/*
 * filter():过滤url编码的汉字,解码
 */
int
filter(char *str)
{
	int ischinese = 0;
	int i;
	int j;
	int n = strlen(str);
	char *tmp;
	char *p;
	int result;
	
	tmp = (char*)malloc(n+1);
	strcpy(tmp,str);
	if(strchr(tmp,'%')!=NULL)
	{
		ischinese = 1;
	}
	
	for(i = 0,j = 0; i < n; i++)
	{
		if(*(str+i) != '%')
		{
			*(tmp+j) = *(str+i);
			j++;
		}	
	}	
	*(tmp+j)=0;

	if(ischinese)
	{
		printf("缂栫爜锛?s\n",tmp);
		p = strchr(tmp,'=');
		p++;
		strncpy(str,tmp,p-tmp);
		printf("*****%d******\n",strlen(p));
		for(i = 0,j = p-tmp; i < strlen(p); i+=2)
		{
			result = htoi(p+i);
			printf("杞

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -