HTTP服务器(三)

下面实现处理动态页面的逻辑:

HTTP服务器(三)_第1张图片

  • 创建一对命名管道,fork创建子进程;让父子进程执行不同的任务

值得注意的是,由于管道数据流动是单向的,所以要创建一对;父进程将必要的信息通过管道传递给子进程,子进程将计算的结果通过管道传递给父进程。

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);                                                                                                                     
    }
}
  • 实现一个CGI程序(计算两个数相加)

HTTP服务器(三)_第2张图片

#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); }

 

你可能感兴趣的:(项目)