下面实现处理动态页面的逻辑:
值得注意的是,由于管道数据流动是单向的,所以要创建一对;父进程将必要的信息通过管道传递给子进程,子进程将计算的结果通过管道传递给父进程。
int HanndlerCGI(int sock, Req* req)
{
err_code = 200;
//1、创建一对匿名管道
int fd1[2], fd2[2];
pipe(fd1);
pipe(fd2);
int father_read = fd1[0];
int child_write = fd1[1];
int father_write = fd2[1];
int child_read = fd2[0];
//2、创建子进程
int ret = fork();
if(ret > 0)
{
// 1)父进程流程
close(child_write);
close(child_read);
HandlerCGIFather(sock, father_read, father_write, req, ret);
}
else if(ret == 0)
{
// 2)子进程流程
close(father_read);
close(father_write);
HandlerCGIChild(sock, child_read, child_write, req);
}
else
{
perror("fork");
err_code = 404;
}
return err_code;
}
传递信息给子进程:
如果是post方法,需要将body中的内容通过管道发送给子进程;
如果是get方法,query_string已经在之前获取到了,不需要作什么了。
构造响应报文:
由于将生成动态页面的任务交给了子进程,所以父进程只需要构造响应报文,将子进程通过管道传递给父进程的内容,发送给客户端。
void HandlerCGIFather(int sock, int father_read, int father_write, Req* req, int pid)
{
printf("in CGIFather\n");
//1.如果是post请求,将body中的数据写到管道中,由子进程完成动态页面的生成
if(strcmp(req->method, "POST") == 0)
{
char buf[1024] = {0};
read(sock, buf, req->content_length);
write(father_write, buf, req->content_length);
}
//2.构造HTTP响应
const char* first_line = "HTTP/1.1 200 OK\n";
const char* blank_line = "\n";
send(sock, first_line, strlen(first_line), 0);
send(sock, blank_line, strlen(blank_line), 0);
//3.从管道中读取数据(子进程动态生成的页面),写到socket中
char c = '\0';
while(read(father_read, &c, 1) > 0)
{
write(sock, &c, 1);
printf("%c\n", c);
}
printf("write to sock\n");
//4.进行进程等待,回收进程资源
waitpid(pid, NULL, 0);
}
设置环境变量:
由于,进行程序替换后,就无法知道父进程发送给子进程的数据了,于是我们就要想办法让程序替换后的程序仍旧可以获得这些数;又因为我们发现环境变量是进行程序替换后,仍旧可以访问到的,所以,我们要做的第一步就是将必要的信息设置成环境变量。
把标准输入和标准输出重定向到管道上:
这样一来,我们实现的CGI程序往标准输出上写,就是往管道上写;于是只要是能够访问标准输入输出,以及环境变量的编程语言就都可以实现CGI程序了。
获取CGI程序的真实路径,进行程序替换,让替换后的程序完成具体的计算过程。
void HandlerCGIChild(int sock, int child_read, int child_write, Req* req)
{
printf("in CGIchild\n");
//1、设置环境变量
char method[1024], query_string[1024], content_length[1024];
sprintf(method, "REQUEST_METHOD=%s", req->method);
putenv(method);
printf("method putenv:%s\n", method);
if(strcmp(req->method, "GET") == 0)
{
sprintf(query_string, "QUERY_STRING=%s", req->query_string);
putenv(query_string);
}
else
{
printf("put content_length : %d\n", req->content_length);
sprintf(content_length, "CONTENT_LENGTH=%d", req->content_length);
putenv(content_length);
}
printf("before dup\n");
//2、把标准输入和输出重定向到管道上
dup2(child_read, 0);
dup2(child_write, 1);
//3、进行程序替换
// a)拼装url为一个完整的文件路径
char file_path[1024*4] = {0};
HandlerFilePath(req->url_path, file_path);
// b)进行程序替换
int err = execl(file_path, file_path, NULL);
//4、如果exec失败,进行错误处理
if(err < 0)
{
perror("execl error");
exit(0);
}
}
#include
#include
#include
int GetQueryString(char buf[])
{
char* method = getenv("REQUEST_METHOD");
if(strcmp(method, "GET") == 0)
{
char* query_string = getenv("QUERY_STRING");
if(query_string == NULL)
{
fprintf(stderr, "get QUERY_STRING error\n");
}
strcpy(buf, query_string);
return 0;
}
else if(strcmp("POST", method) == 0)
{
fprintf(stderr, "in post get string\n");
char* content_len = getenv("CONTENT_LENGTH");
fprintf(stderr, "cont—len:%s\n", content_len);
int content_length = atoi(content_len);
fprintf(stderr, "cont—length:%d\n", content_length);
int i = 0;
char c = '\0';
while(i < content_length)
{
read(0, &c, 1);
buf[i++] = c;
}
fprintf(stderr, "string:%s\n", buf);
return 0;
}
return -1;
}
int main()
{
fprintf(stderr, "in math\n");
char query_string[1024*4] = {0};
int ret = GetQueryString(query_string);
if(ret < 0)
{
fprintf(stderr, "GetQueryString error\n");
return 0;
}
fprintf(stderr, "query_string:%s\n", query_string);
int a,b;
sscanf(query_string, "a=%d&b=%d", &a, &b);
fprintf(stderr, "after anylaze string\n");
int sum = a+b;
printf("sum = %d
", sum);
fprintf(stderr, "%d+%d=%d\n", a, b, sum);
}