- ### 运行CGI程序
- void exec_cgi(int fd, char *method, int content_length, char *filename, char *cgiargs)
-
{
-
char buf[MAXLINE], *emptylist[] = { NULL };
-
-
#### 发送 HTTP相依的第一部分
-
sprintf(buf, "HTTP/1.1 200 OK\r\n");
-
write(fd, buf, strlen(buf));
-
sprintf(buf, "Server: Mini Web Server\r\n");
-
write(fd, buf, strlen(buf));
-
-
if (fork() == 0) { ##返回是0,表示是在子进程中运行,处理POST 方式
- ### 子进程 处理POST 方法
-
if (strcasecmp(method, "POST") == 0) {
-
int pfd[2];
-
int rc = pipe(pfd);
-
if (rc < 0) {
-
perror("pipe in POST failed");
-
exit(1);
-
}
-
int post_pid = fork();
-
if (post_pid == 0) {
-
close(pfd[0]);
-
int n = 0, bytes_read = 0;
-
/*only read length of "content_length"*/
-
while (n < content_length) {
-
bytes_read = read(fd, buf, sizeof(buf)-1);
-
printf("content read: %s \n", buf);
-
if (bytes_read > 0) {
-
write(pfd[1], buf, bytes_read);
-
n += bytes_read;
-
}
-
}
-
exit(0);
-
}
-
-
close(pfd[1]);
-
/*redirect to STDIN*/
-
dup2(pfd[0],STDIN_FILENO);
- ### POST 处理结束
-
}
-
-
### 设置 CGI 环境变量,本程序中仅支持 “QUERY_STRING”和“content_length”
-
setenv("QUERY_STRING", cgiargs, 1);
-
sprintf(buf, "%d", content_length);
-
setenv("CONTENT_LENGTH", buf);
-
dup2(fd, STDOUT_FILENO); # 重定向stdout 到客户端
-
execve(filename, emptylist, environ); # 运行 CGI 程序
-
}
-
wait(NULL); #父进程等待 子进程结束并收回
-
}
函数体中比较长的部分是处理POST方法,我们先忽略这段逻辑,来看看相对简单的GET方法怎么处理。
- sprintf(buf, "HTTP/1.1 200 OK\r\n");
-
write(fd, buf, strlen(buf));
-
sprintf(buf, "Server: Mini Web Server\r\n");
-
write(fd, buf, strlen(buf));
按照上面的代码按照HTTP规范返回HTTP响应行即部分响应报头之后,在下面通过fork 调用来复制一个子进程。在子进程的处理中,忽略POST处理逻辑。
- setenv("QUERY_STRING", cgiargs, 1);
-
sprintf(buf, "%d", content_length);
-
setenv("CONTENT_LENGTH", buf);
- 设置环境变量“QUERY_STRING” 和“CONTENT_LENGTH”,这是 CGI规范中规定的WEB 服务器向CGI程序传递参数的方式之一,CGI规范所定义的环境变量除了“QUERY_STRING”和“CONTENT_LENGTH”之外,还有“SERVER_PORT”,“REMOTE_HOST”
- 等很多,这里我们仅仅支持了最常用的两个,实际应用中可能是需要增加的。
dup2
(fd
, STDOUT_FILENO
); # 重定向stdout 到客户端
execve
(filename
, emptylist
, environ
); # 运行 CGI 程序通过dup2函数将子进程的标准输出stdout重定向到fd,接着通过execve加载并运行CGI程序,并通过glibc中的全局变量 environ 来传递环境变量,这样程序运行时,就可以通过环境变量来接收服务器传递过来的参数,并将结果通过标准输出stdout返回给客户端浏览器。
wait
(NULL); #父进程等待 子进程结束并收回
我们可以利用后面的测试程序来理解服务器对 CGI的支持。在测试程序中我们提供了用来测试GET 和 POST 的HTML表单,分别对应 get.html 和 post.html 两个文件。
get.html 的 内容如下:
test of "GET"
Test GET Form