执行CGI脚本,动态页面申请:execute_cgi
GET 方法用来请求访问已被 URI 识别的资源。指定的资源进服务器端解析后返回响应内容。换言之,如果请求的资源是文本(静态页面请求),那就维持原样返回;如果是像CGI 那样的程序(动态页面请求),则返回经过执行后的输出结果。
结合程序工作步骤看下面代码,更容易理解
/*execute_cgi函数负责将请求传递给cgi程序处理,
服务器与cgi之间通过管道pipe通信,首先初始化两个管道,并创建子进程去执行cgi函数
子进程执行cgi程序,获取cgi的标准输出通过管道传给父进程,由父进程发送给客户端
*/
//参数path指向执行的CGI脚本路径 ,method指向http的请求方法
void execute_cgi(int client, const char *path,
const char *method, const char *query_string)
{
char buf[1024];
int cgi_output[2];
int cgi_input[2];
//cgi_out 是指CGI脚本输出数据的管道
//cgi_input是指向CGI脚本输入数据的管道
pid_t pid;
int status;
int i;
char c;
int numchars = 1;
int content_length = -1;
buf[0] = 'A'; buf[1] = '\0';
//如果是get请求,读取并丢弃头部
if (strcasecmp(method, "GET") == 0)//
while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */
numchars = get_line(client, buf, sizeof(buf));
else /* POST */
{
//如果是post请求
numchars = get_line(client, buf, sizeof(buf));
while ((numchars > 0) && strcmp("\n", buf))
{
buf[15] = '\0';
//读取头信息找到Content-Length字段的值
if (strcasecmp(buf, "Content-Length:") == 0)
//Content-Length:15
content_length = atoi(&(buf[16]));
numchars = get_line(client, buf, sizeof(buf));
}
if (content_length == -1) {
bad_request(client);
return;
}
}
//正确返回200
sprintf(buf, "HTTP/1.0 200 OK\r\n");
send(client, buf, strlen(buf), 0);
//pipe(int filep[2]) 管道函数,f[0]读,f[1]写
//必须在fork()中调用pipe(),否则子进程不会继承文件描述符。
//两个进程必须有血缘关系才能使用pipe。但是可以使用命名管道。
if (pipe(cgi_output) < 0) {
cannot_execute(client);
return;
}
if (pipe(cgi_input) < 0) {
cannot_execute(client);
return;
}
if ( (pid = fork()) < 0 ) {
cannot_execute(client);
return;
}
/*子进程继承了父进程的pipe,然后通过关闭子进程output管道的输出端,input管道的写入端;
关闭父进程output管道的写入端,input管道的输出端*/
if (pid == 0) /* child: CGI script */
{
char meth_env[255];
char query_env[255];
char length_env[255];
//把stdout 重定向到cgi_output[1]
dup2(cgi_output[1], 1);
dup2(cgi_input[0], 0);
close(cgi_output[0]);//关闭cgi_output读端
close(cgi_input[1]); //关闭cgi_input 写端
sprintf(meth_env, "REQUEST_METHOD=%s", method);
putenv(meth_env);
if (strcasecmp(method, "GET") == 0) {
/*设置 query_string 的环境变量*/
sprintf(query_env, "QUERY_STRING=%s", query_string);
putenv(query_env);
}
else { /* POST */
/*设置 content_length 的环境变量*/
sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
putenv(length_env);
}
//exec函数簇,执行CGI脚本,获取cgi的标准输出作为相应内容发送给客户端
execl(path, path, NULL);
exit(0);
}
else
{ /* parent */
close(cgi_output[1]);
close(cgi_input[0]);
/*通过关闭对应管道的通道,然后重定向子进程的管道某端,这样就在父子进程之间构建一条单双工通道
如果不重定向,将是一条典型的全双工管道通信机制
*/
if (strcasecmp(method, "POST") == 0)
for (i = 0; i < content_length; i++) {
recv(client, &c, 1, 0);//从客户端接收单个字符
write(cgi_input[1], &c, 1);
//数据传送过程:input[1](父进程) ——> input[0](子进程)[执行cgi函数] ——> STDIN ——> STDOUT
// ——> output[1](子进程) ——> output[0](父进程)[将结果发送给客户端]
}
while (read(cgi_output[0], &c, 1) > 0)//读取output的管道输出到客户端,output输出端为cgi脚本执行后的内容
send(client, &c, 1, 0);//即将cgi执行结果发送给客户端,即send到浏览器,如果不是POST则只有这一处理
close(cgi_output[0]);//关闭剩下的管道端,子进程在执行dup2之后,就已经关闭了管道一端通道
close(cgi_input[1]);
waitpid(pid, &status, 0);//等待子进程终止
}
}
整体代码在GitHub上面:
如果错误,欢迎指出,交流进步,谢谢
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shumachanpin/article-121791-3.html
即使是后面一条