boa服务器实现CGI功能

2023-11-04

CGI简介

        CGI 是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据库API 与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。几乎所有服务器都支持CGI,可用任何语言编写CGI,包括流行的C、C ++、VB 和Delphi 等。CGI 分为标准CGI 和间接CGI两种。标准CGI 使用命令行参数或环境变量表示服务器的详细请求,服务器与浏览器通信采用标准输入输出方式。间接CGI 又称缓冲CGI,在CGI 程序和CGI 接口之间插入一个缓冲程序,缓冲程序与CGI 接口间用标准输入输出进行通信。

简而言之:CGI就是web服务器可以调用的其他程序,该程序将输出重定向,在该程序中通过printf生成html,通过管道将数据发给web服务器,完成交互。


boa CGI实现

1.boa获取到数据时,执行process_requests()-->读取http头read_header()--->process_header_end();根据头部信息,获取到该请求为CGI

2.执行init_cgi()

int init_cgi(request * req)
{
    int child_pid;
    int pipes[2];
    int use_pipes = 0;

    SQUASH_KA(req);

    if (req->is_cgi) {
        if (complete_env(req) == 0) {        //设置环境变量
            return 0;
        }
    }
#ifdef FASCIST_LOGGING
    {
        int i;
        for (i = 0; i < req->cgi_env_index; ++i)
            fprintf(stderr, "%s - environment variable for cgi: \"%s\"\n",
                    __FILE__, req->cgi_env[i]);
    }
#endif

    if (req->is_cgi == CGI || 1) {
        use_pipes = 1;                //使用管道
        if (pipe(pipes) == -1) {
            log_error_time();
            perror("pipe");
            return 0;
        }

        /* set the read end of the socket to non-blocking */
        if (set_nonblock_fd(pipes[0]) == -1) {        //设置读管道为非阻塞
            log_error_time();
            perror("cgi-fcntl");
            close(pipes[0]);
            close(pipes[1]);
            return 0;
        }
    }

    child_pid = fork();        //创建子进程,用于启动cgi程序,程序执行完成后退出boa
    switch(child_pid) {
    case -1:
        /* fork unsuccessful */
        log_error_time();
        perror("fork");

        if (use_pipes) {
            close(pipes[0]);
            close(pipes[1]);
        }
        send_r_error(req);
        /* FIXME: There is aproblem here. send_r_error would work
           for NPH and CGI, but not for GUNZIP.  Fix that. */
        /* i'd like to send_r_error, but.... */
        return 0;
        break;
    case 0:
        /* child */
        if (req->is_cgi == CGI || req->is_cgi == NPH) {
            char *foo = strdup(req->pathname);
            char *c;

            if (!foo) {
                WARN("unable to strdup pathname for req->pathname");
                _exit(1);
            }
            c = strrchr(foo, '/');
            if (c) {
                ++c;
                *c = '\0';
            } else {
                /* we have a serious problem */
                log_error_time();
                perror("chdir");
                if (use_pipes)
                    close(pipes[1]);
                _exit(1);
            }
            if (chdir(foo) != 0) {
                log_error_time();
                perror("chdir");
                if (use_pipes)
                    close(pipes[1]);
                _exit(1);
            }
        }
        if (use_pipes) {
            close(pipes[0]);
            /* tie cgi's STDOUT to it's write end of pipe */
            if (dup2(pipes[1], STDOUT_FILENO) == -1) {        //将写管道重定向到标准输出,在cgi中使用printf,数据就会被发送到管道中
                log_error_time();
                perror("dup2 - pipes");
                close(pipes[1]);
                _exit(1);
            }
            close(pipes[1]);
            if (set_block_fd(STDOUT_FILENO) == -1) {        //设置管道为阻塞
                log_error_time();
                perror("cgi-fcntl");
                _exit(1);
            }
        } else {
            /* tie stdout to socket */
            if (dup2(req->fd, STDOUT_FILENO) == -1) {
                log_error_time();
                perror("dup2 - fd");
                _exit(1);
            }
            /* Switch socket flags back to blocking */
            if (set_block_fd(req->fd) == -1) {
                log_error_time();
                perror("cgi-fcntl");
                _exit(1);
            }
        }
        /* tie post_data_fd to POST stdin */
        if (req->method == M_POST) { /* tie stdin to file */
            lseek(req->post_data_fd, SEEK_SET, 0);
            dup2(req->post_data_fd, STDIN_FILENO);
            close(req->post_data_fd);
        }
        /* Close access log, so CGI program can't scribble
         * where it shouldn't
         */
        close_access_log();

        /*
         * tie STDERR to cgi_log_fd
         * cgi_log_fd will automatically close, close-on-exec rocks!
         * if we don't tied STDERR (current log_error) to cgi_log_fd,
         *  then we ought to close it.
         */
        if (!cgi_log_fd)
            dup2(devnullfd, STDERR_FILENO);
        else
            dup2(cgi_log_fd, STDERR_FILENO);

        if (req->is_cgi) {
            char *aargv[CGI_ARGC_MAX + 1];
            create_argv(req, aargv);
            execve(req->pathname, aargv, req->cgi_env);        //启动cgi程序
        } else {
            if (req->pathname[strlen(req->pathname) - 1] == '/')
                execl(dirmaker, dirmaker, req->pathname, req->request_uri,
                      NULL);
#ifdef GUNZIP
            else
                execl(GUNZIP, GUNZIP, "--stdout", "--decompress",
                      req->pathname, NULL);
#endif
        }
        /* execve failed */
        WARN(req->pathname);
        _exit(1);
        break;

    default:
        /* parent */
        /* if here, fork was successful */
        if (verbose_cgi_logs) {
            log_error_time();
            fprintf(stderr, "Forked child \"%s\" pid %d\n",
                    req->pathname, child_pid);
        }

        if (req->method == M_POST) {
            close(req->post_data_fd); /* child closed it too */
            req->post_data_fd = 0;
        }

        /* NPH, GUNZIP, etc... all go straight to the fd */
        if (!use_pipes)
            return 0;

        close(pipes[1]);
        req->data_fd = pipes[0];            //保存读管道的fd

        req->status = PIPE_READ;            //状态置为PIPE_READ
        if (req->is_cgi == CGI) {
            req->cgi_status = CGI_PARSE; /* got to parse cgi header */
            /* for cgi_header... I get half the buffer! */
            req->header_line = req->header_end =
                (req->buffer + BUFFER_SIZE / 2);
        } else {
            req->cgi_status = CGI_BUFFER;
            /* I get all the buffer! */
            req->header_line = req->header_end = req->buffer;
        }

        /* reset req->filepos for logging (it's used in pipe.c) */
        /* still don't know why req->filesize might be reset though */
        req->filepos = 0;
        break;
    }

    return 1;                //more to do
}
3.下一次轮询中,程序回到process_request,状态为 PIPE_READ,准备读取管道中的数据。读取后,继续轮询,直至全部读取完毕,状态置为 PIPE_WRITE,将数据发送给客户端。(正是由于boa的这种轮询机制,使得它可以处理多个请求而不会卡住)


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

boa服务器实现CGI功能 的相关文章

  • Flutter上你需要一个简单实用的验证码输入Widget

    现在很多app都有填写验证码的需求 而这个验证码的UI流行非普通的输入框 毕竟移动开发主要矛盾是设计师日益增长的天马行空同落后的开发之间的矛盾 作为跨平台方案的Flutter自然也会用到这种控件 本着少造轮子 多copy的原则 想去找个验证

随机推荐

  • 《Python进阶系列》六:Python中的文件对象

    文件对象的seek 和tell 打开一个文件 读取内容 是很常见的操作 不过有的时候我们还需要反复读取文件中的内容 如果多次打开文件读取再多次关闭 显然不是特别好的操作 我们可以借助Python文件对象的seek 和tell 函数 来实现反
  • 为什么那么多人用小智ai

    ChatGPT丨小智ai丨chatgpt丨人工智能丨OpenAI丨聊天机器人丨AI语音助手丨GPT 3 5丨OpenAI ChatGPT GPT 4 GPT 3 人机对话 ChatGPT应用 小智ai 小智ai 小智ai 小智ai Chat
  • Shader理论(2):Unity中的各种坐标系

    目录 前言 一 坐标系 1 1 世界坐标系 World Space 1 2 本地坐标系 Local Space 模型坐标系 1 3 屏幕坐标系 Screen Space 1 4 观察坐标系 ViewPort Space 视口坐标系 1 5
  • 如何设计一个短信发送功能

    本文主要分享了如何设计一个发送短信功能 一 总结简述 1 梳理多个平台短信API的发送参数 集成封装提供统一的API 支持多个短信平台 阿里云 腾讯云 百度云 京东云 七牛云 灵活切换 2 提供存储方案 表结构设计 3 提供真实生产项目代码
  • 内核配置中的:Multifunction device drivers

    Multifunction Device Drivers 多功能设备Linux下驱动开发 先来一个英文简介 Multifunction devices embed several functions e g GPIOs touchscree
  • kafka的一些基本命令

    kafka brokers test 8090 zookeeper servers test 2181 1 查看test服务器中的所有topic bin kafka topics sh list zookeeper test 2181 2
  • stable diffusion(Lora的训练)

    以坤坤为例 上网随便找了几个坤坤的人脸图像 作为训练的数据集 1 训练环境搭建 建议看一遍教程 虽然这个up主好像不是很专业的样子 不过流程差不多是这样的 重点关注一下虚拟环境搭建完之后 在终端选择配置的操作 就是一堆yes no 的选项
  • 国产麒麟系统KylinOS Server V10 SP2安装MongoDB6.0版本

    1 下载安装包 安装下载链接 https fastdl mongodb org linux mongodb linux aarch64 rhel82 6 0 6 tgz 2 上传到服务器 3 解压安装包 tar zxvf mongodb l
  • AF Http request 代理形式回调

    AF是以block的方式将请求结果返回 这样的处理思路我感觉很好 每个请求都单独处理自己的回调 AF的请求也可以使代理的形式进行回调 HIHTTPRequest 以代理的形式回调的话需要创建一个请求类 然后将请求结果以代理的形式回调 HIH
  • 毕业论文参考文献格式设置(以GB/T 7714-2015为例)

    Ref https zhuanlan zhihu com p 376138185 下载链接 https www endnote com style download chinese standard gb t7714 numeric 链接
  • 四路服务器选型项目,四路服务器详细

    四路服务器详细 内容精选 换一换 弹性云服务器 Elastic Cloud Server 是一种可随时自动获取 计算能力可弹性伸缩的云服务器 可帮助您打造可靠 安全 灵活 高效的应用环境 确保服务持久稳定运行 提升运维效率 本节介绍REST
  • 【Unity】UnityのTimelineが実機で再生されない件について

    Unity Unity Timeline 実機 再生 件 https qiita com hide gugen items 7fee18be2c789144ed74 経緯 Editor上 普通 再生 日AssetBundle化 iPhone
  • 华为隐藏功能扩大内存代码大全_华为手机绝密功能,在拨号界面就能揭开

    你知道吗 华为手机里的那些隐藏功能 打开方式居然是 拨号界面 只要在拨号界面输入指定代码 就会自动弹出隐藏功能 今天我就来给大家科普科普 那些隐藏在华为手机里的代码大全 测试代码 虽然这个代码叫测试代码 但对咱们普通用户来说却是一个非常实用
  • JS大坑之19位数的Number型精度丢失问题详解

    这篇文章主要介绍了JS大坑之19位数的Number型精度丢失问题 文中通过示例代码介绍的非常详细 对大家的学习或者工作具有一定的参考学习价值 有兴趣的朋友们可以一起来学习探讨 最近在实现一个需求的时候 需要接入第三方的接口 先调用A接口 A
  • 关于ip5306芯片按键关机后仍然有输出

    官方手册中提到 负载自动检测时间 TloadD 负载电流持续小于 45mA 32 s时 芯片会自动进入休眠状态 实际上可能会因为个体差异 芯片空载就60ma 导致无法关机 实际上做这个芯片测试 发现了几个问题 芯片PIN5按键不可抛弃 如果
  • 计算机网络原理选择题

    1 1 电信业一般认为宽带骨干网数据传输速率应达到 d a 640kbps b 640mbps c 1gbps d 2gbps 2 异步时分多路复用 tdm 技术中的时间片分配策略是 c a 预先分配但不固定 b 预先分配固定不变 c 动态
  • 第八章 真实的谎言

    第八章 真实的谎言 我先给大家讲一个石头汤的故事 很久以前 在东欧的一个地方 发生了一次大饥荒 人们都以戒备的心理囤积起他们可以找到的任何食物 并把这些食物都藏了起来 甚至于连他们的朋友和邻居都 不告诉 有一天 一个小商贩驾着他的马车来到一
  • Ant Design与Ant Design pro入门

    一 Ant Design入门 1 什么是Ant Design Ant Design是阿里蚂蚁金服团队基于React开发的ui组件 主要用于中后台系统的使用 官网 https ant design index cn 特性 提炼自企业级中后台产
  • 网易笔试编程题-不要二

    题目描述 二货小易有一个W H的网格盒子 网格的行编号为0 H 1 网格的列编号为0 W 1 每个格子至多可以放一块蛋糕 任意两块蛋糕的欧几里得距离不能等于2 对于两个格子坐标 x1 y1 x2 y2 的欧几里得距离为 x1 x2 x1 x
  • boa服务器实现CGI功能

    CGI简介 CGI 是Web 服务器运行时外部程序的规范 按CGI 编写的程序可以扩展服务器功能 CGI 应用程序能与浏览器进行交互 还可通过数据库API 与数据库服务器等外部数据源进行通信 从数据库服务器中获取数据 格式化为HTML文档后