开源C++版本CGI库CGICC入门

2023-10-27

 

原发布在ChinaUnix,但未被自动搬迁过来:http://blog.chinaunix.net/uid-20682147-id-4895772.html

PDF版本:https://download.csdn.net/download/aquester/10827278

目录

目录 1

1. 简介 1

2. CGICC组成 1

3. CGI输入处理子模块类结构 2

3.1. Cgicc 2

3.2. CgiEnvironment 2

3.3. HTTPCookie 2

3.4. CgiInput 3

3.5. FormFile 3

3.6. FormEntry 3

4. CGI输入处理子模块初始化流程 3

5. 编译和安装CGICC 4

6. CGICC使用示例 5

6.1. 页面效果 5

6.2. HTML文件 5

6.3. test.txt文件 6

6.4. CGI文件 6

6.5. 运行效果 8

7. HTML输出子模块类图 10

7.1. HTTPContentHeader 13

7.2. HTMLElement::render()函数 13

8. 问题? 16

 

1. 简介

CGICC是一个C++语言实现的开源CGI库,采用LGPL授权协议,使用较为简单。

CGICC官网:http://www.gnu.org/software/cgicc/,截止2015/3/14,CGICC最新稳定版本为3.2.16,下载地址是:http://ftp.gnu.org/gnu/cgicc/cgicc-3.2.16.tar.gz,最新更新时间为2014/12/7(令人惊讶和欣慰的是作为古老的CGI,CGICC还在不断的更新)。

2. CGICC组成

CGICC由两大部分组成:

1) CGI输入处理子模块

2) HTML输出子模块

 

本文暂只介绍CGI输入处理子模块,对于HTML输出,推荐Google开源的ctemplate(https://github.com/OlafvdSpek/ctemplate)。

3. CGI输入处理子模块类结构

3.1. Cgicc

CGICC的一类,通常直接在CGI的入口函数,如main函数中定义一个CGICC对象,然后即可使用CGICC提供的各种能力。

3.2. CgiEnvironment

提供get系列方法取各环境变量的值。

3.3. HTTPCookie

提花get系列方法取各Cookie的值,并支持set新增或修改Cookie值。

3.4. CgiInput

CgiEnvironment内部类,仅供CgiEnvironment使用。

3.5. FormFile

提供访问HTML的Form中的被上传文件信息和数据接口。

3.6. FormEntry

提供访问HTML的Form中的非被上传文件类的信息和数据接口。取URL参数值示例:

// http://127.0.0.1/?param_name=param_value

cgicc::form_iterator iter = cgi.getElement("param_name");

if (iter != cgi.getElements().end())

{

    std::string param_value = iter->getValue();

}

 

// 也可以这样做:

std::string param_value = cgi("param_name");

 

// 除此之外,FormEntry还提供了直接取指定数据类型的参数值,如:getIntegerValue、getDoubleValue

4. CGI输入处理子模块初始化流程

初始化流程是由Cgicc构造函数触发的,一般可在CGI的main函数中定义一个Cgicc对象:

5. 编译和安装CGICC

详细编译步骤如下:

1) 将CGICC源代码包(本文下载的是cgicc-3.2.16.tar.gz)上传到Linux某目录(本文将CGICC源代码包cgicc-3.2.16.tar.gz上传到/tmp目录);

2) 登录Linux,并进入目录/tmp;

3) 解压CGICC源代码包cgicc-3.2.16.tar.gz:tar xzf cgicc-3.2.16.tar.gz;

4) 解压后,会在/tmp下产生一个子目录cgicc-3.2.16,进入到这个子目录;

5) 然后执行configure命令(本文指定的安装目录为/usr/local/cgicc-3.2.16,可以根据需要设定为其它目录),以生成Makefile编译文件,如果要在共享库中使用CGICC,请使用下列编译命令:

./configure --prefix=/usr/local/cgicc-3.2.16 CXXFLAGS=-fPIC LDFLAGS=-fPIC

 

否则,可按如下命令编译:

./configure --prefix=/usr/local/cgicc-3.2.16

 

在一些环境上,如果不带-fPIC编译静态库,使用静态库时,就会报链接错误。

6) 执行make编译:make

7) 安装CGICC库:make install

8) 为/usr/local/cgicc-3.2.16建立不带版本号的软链接:

ln -s /usr/local/cgicc-3.2.16 /usr/local/cgicc

 

至此,CGICC库就安装好了!

6. CGICC使用示例

6.1. 页面效果

6.2. HTML文件

页面效果对应的HTML文件内容如下(HTML中的id一般是给前端如js使用的,而name通常是给服务端如CGI使用的):

<html>

    <head>

       <title>upload</title>

    </head>

 

    <body>

        <p>upload:

        <div>

            <form action="/cgi-bin/upload.cgi" method="post" name="formname"

                  enctype="multipart/form-data">

                <input type="text" id="id1" name="name1" />

                <input type="text" id="id2" name="name2" />

 

                <p>

                <input type="file" id="fileid" name="filename" />

                <input type="submit" value="upload" id="upid" name="upname" />

            </form>

        </div>

    </body>

</html>

 

注意,上传文件时,Form的enctype属性值必须被设定为multipart/form-data。

6.3. test.txt文件

test.txt是一个被上传的文件,内容只有一行:0123456789

6.4. CGI文件

// 如果是Exe形式的CGI,则使用如下语句编译:

// g++ -g -o upload.cgi upload.cpp -I/usr/local/cgicc/include /usr/local/cgicc/lib/libcgicc.a

// 如果是共享库(Windows平台叫动态库)形式的CGI,则使用如下语句编译:

// g++ -g -o upload.cgi upload.cpp -shared -fPIC -I/usr/local/cgicc/include /usr/local/cgicc/lib/libcgicc.a

#include <stdio.h>

#include <sstream>

#include "cgicc/Cgicc.h"

#include "cgicc/HTMLClasses.h"

#include "cgicc/HTTPHTMLHeader.h"

 

int main(int argc, char **argv)

{

    try

    {

        cgicc::Cgicc cgi;

 

        // Output the HTTP headers for an HTML document, 

        // and the HTML 4.0 DTD info

        std::cout << cgicc::HTTPHTMLHeader()

                  << cgicc::HTMLDoctype(cgicc::HTMLDoctype::eStrict)

                  << std::endl;

        std::cout << cgicc::html().set("lang", "en").set("dir", "ltr")

                  << std::endl;

 

        // Set up the page's header and title.

        std::cout << cgicc::head() << std::endl;

        std::cout << cgicc::title() << "GNU cgicc v" << cgi.getVersion()

                  << cgicc::title() << std::endl;

        std::cout << cgicc::head() << std::endl;

 

        // Start the HTML body

        std::cout << cgicc::body() << std::endl;

 

        // Print out a message

        std::cout << cgicc::h1("Hello, world from GNU cgicc") << std::endl;

        const cgicc::CgiEnvironment& env = cgi.getEnvironment();

 

        std::cout << "<p>accept: " << env. getAccept() << std::endl;

        std::cout << "<p>user agent: " << env.getUserAgent() << std::endl;

 

        std::cout << "<p>cookie: " << std::endl;

        const std::vector<cgicc::HTTPCookie>& cookies = env.getCookieList();

        for (std::vector<cgicc::HTTPCookie>::size_type i=0; i<cookies.size(); ++i)

        {

            const cgicc::HTTPCookie& cookie = cookies[i];

            std::cout << "<br>    cookie[" << cookie.getName()

                      << "] = " << cookie.getValue() << std::endl; 

        }

 

        std::cout << "<p>query string: " << env.getQueryString() << std::endl;

        std::cout << "<p>remote: " << env.getRemoteAddr() << ":" << env.getServerPort()

                  << std::endl;

 

        std::cout << "<p>form: " << std::endl;

        const std::vector<cgicc::FormEntry>& form_entries = cgi.getElements();

        for (std::vector<cgicc::FormEntry>::size_type i=0; i<form_entries.size(); ++i)

        {

            const cgicc::FormEntry& form_entry = form_entries[i];

            std::cout << "<br>    form["

                      << form_entry.getName() << "] = "

                      << form_entry.getValue() << std::endl;

        }

 

        //

        // 取被上传的文件信息

        //

        

        // 使用getFile取得指定的被上传文件信息

        cgicc::const_file_iterator file_iter = cgi.getFile("file");

        

        // 使用getFiles可以取得所有被上传文件信息

        if (file_iter == cgi.getFiles().end())

        {

            std::cout << "<p>file: " << cgi.getFiles().size() << std::endl;

        }

        else

        {

            const cgicc::FormFile& file = *file_iter;

            std::cout << "<p>file: " << std::endl;

            std::cout << "<br>    name: "

                      << file.getName() << std::endl;

            std::cout << "<br>    filename: "

                      << file.getFilename() << std::endl;

            std::cout << "<br>    type: "

                      << file.getDataType() << std::endl;

            std::cout << "<br>    size: "

                      << file.getDataLength() << std::endl;

            std::cout << "<br>    content: "

                      << file.getData() << std::endl;

        }

 

        // Close the document

        std::cout << cgicc::body() << cgicc::html();

    }

    catch(const std::exception& e)

    {

        // handle error condition

    }

 

    return 0;

}

6.5. 运行效果

点击HTML页面的upload按钮后,页面变成如下:

Hello, world from GNU cgicc

 

accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

 

user agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.36 (KHTML, like Gecko) Chrome/39.0.2172.95 Safari/537.36

 

cookie: 

    cookie[pgv] = 445364884 

    cookie[ku] = f0ab9e006c7f4d5a4e9b394fc44fafc8afd6df6d373f9ff5f2946047974daf0ef9b00c6a1d7c341b 

    cookie[uid] = zhangshan 

    cookie[post-shareto-guide] = 1 

    cookie[si] = s4001534976 

    cookie[info] = ssid=s9175124444 

    cookie[pvid] = 6963827212 

    cookie[code_user_name] = A5C9579BE8B7C0E0 

 

query string:

 

remote: 120.16.82.66:80

 

form: 

    form[name1] = abc 

    form[name2] = xyz 

    form[upname] = upload

 

file: 

    name: filename 

    filename: test.txt 

    type: text/plain 

    size: 10 

    content: 0123456789

7. HTML输出子模块类图

HTMLBooleanElement

注意sState是类HTMLBooleanElement的静态数据成员,sState的数据类型为bool。

标签

对于<html></html>,前者<html>叫开始标签,后者</html>叫关闭标签。

EElementType

枚举类型,定义了两个枚举值:eAtomic和eBoolean,eAtomic对应的实现类为HTMLAtomicElement,eBoolean对应的实现为HTMLBooleanElement。类似于strong类的为eBoolean类型,而类似于hr、br之类的为eAtomic类型。对于eAtomic类型的HTML标签,它没有对应的关闭标签(也叫结束标签),观察以下两组的差别:

<br />

<strong>This text is strong</strong>

 

<hr />

<p>This is some text</p>

 

可以看到brhr均是eAtomic类型的标签,而strongp均是eBoolean类型的标签。

 

fEmbedded

对于eBoolean类型的标签,在HTMLElement::render()函数的实现中,会发现还区分是否有fEmbedded,什么是有fEmbedded的eBoolean类型标签?

下面这行为无fEmbedded的eBoolean标签:

 

 

下段这段也是含fEmbedded的eBoolean的标签,“<title>CGICC</title>”为标签head的fEmbedded内容:

<head>

    <title>CGICC</title>

</head>

 

 

fDataSpecified

也是针对eBoolean类型标签的,同样在HTMLElement::render()函数的实现中,会发现到差别(对应于对HTMLElement::dataSpecified()的调用)。下列的a即为fDataSpecified类型的eBoolean标签,其中“一见的技术博客”为标签a的Data:

<a href="http://aquester.cublog.cn">一见的技术博客</a>

 

代码中的html()究竟是啥?

阅读示例代码,可能会有这样一个疑问:html()是如何被调用的?发现没法直接找到名叫html的函数。

cout << html().set("lang", "en").set("dir", "ltr") << endl;

cout << head() << endl;

cout << title() << "GNU cgicc v" << cgi.getVersion() << title() << endl;

cout << head() << endl;    

cout << body() << endl;

cout << h1("Hello, world from GNU cgicc") << endl;

cout << body() << html();

 

上述调用中的html()、head()、title()、h1()和body()等,实际都是调用类HTMLBooleanElement的构造函数,演变成调用HTMLElement::render(std::ostream& out)。

 

流函数的定义为:

std::ostream& cgicc::operator <<(std::ostream& out, const cgicc::MStreamable& obj)

{

    obj.render(out);

    return out; 

}

 

从流函数的定义不难看出,实际上调用的是render()。

在HTMLClasses.h文件中,定义了html、body等类(位于cgicc名字空间内),但是有些隐晦,直接看不出来:

 

翻译一下,以便容易看懂这个过程,先看相关的宏定义:

1) BOOLEAN_ELEMENT

#define BOOLEAN_ELEMENT(name, tag) \

    TAG(name, tag); typedef HTMLBooleanElement<name##Tag> name

 

2) 宏TAG

// 注意区分下面的tagTag

#define TAG(name, tag) \

    class name##Tag \

    {

    public:

        inline static const char* getName()

        {

            return tag; // 注意不是Tag,而是tag

        }

    } // 注意,这里并没有加分号

 

现在来看HTMLClasses.h文件中定义的BOOLEAN_ELEMENT(html, "html");,宏展开后,变成:

class htmlTag

{

public:

    inline static const char* getName()

    {

        return "html";

    }

};

 

typedef HTMLBooleanElement<htmlTaghtml// html是不是就是一个类了?

 

html()怎么来的清楚了,还有一个疑问:对于:cout << html(),怎么知道是输出<html>还是</html>的?这个逻辑是在函数HTMLElement::render(std::ostream& out)中完成的。

7.1. HTTPContentHeader

HTTPContentHeader负责输出HTTP头中的“Content-Type:”,看它的渲染函数reader()实现:

void cgicc::HTTPContentHeader::render(std::ostream& out) const

{

    out << "Content-Type: " << getData() << std::endl;

 

    std::vector<HTTPCookie>::const_iterator iter;

    for (iter = getCookies().begin(); iter != getCookies().end(); ++iter)

    {

        out << *iter << std::endl;

    }

 

    out << std::endl;

}

 

其中,子类HTTPHTMLHeader的getData()返回“text/html”,子类HTTPPlainHeader的getData()返回“text/plain”,子类HTTPXHTMLHeader的getData()返回“application/xhtml+xml”。

 

7.2. HTMLElement::render()函数

void cgicc::HTMLElement::render(std::ostream& out)  const

{

    if (eBoolean == getType() && false == dataSpecified())

    {

        if (0 == fEmbedded) /* no embedded elements */

        {

            // 切换:用来控制是输入开始标签,还是关闭标签

            // HTMLBooleanElement::sState为类静态数据成员,

            // swapState()的作用就是用来切换它的值。

            swapState();

 

            /* getState() == true ===> element is active */

            if (true == getState())

            {

                // 输出开始标签,

                out << '<' << getName();

 

                // 开始标签是可能包含属性的,

                // 如:<a href="http://aquester.cublog.cn">,

                // 这里的href即为标签<a>的属性

                if (0 != fAttributes)

                {

                    out << ' '; // 属性间使用一个空格分开

                    fAttributes->render(out);

                }

 

                out << '>';

            }

            else

            {

                // 输出关闭标签,如:</head>

                out << "</" << getName() << '>';

            }

        }

        else /* embedded elements present */

        {

            // 被嵌入的(embedded)的内容总是整体一次性输出,

            // 而不是区分其状态值HTMLBooleanElement::sState

            out << '<' << getName();

 

            /* render attributes, if present */

            if (0 != fAttributes)

            {

                out << ' ';

                fAttributes->render(out);

            }

 

            out << '>';

            fEmbedded->render(out);

 

            // 输出关闭标签,如:</head>

            out << "</" << getName() << '>';

        }

    }

    else /* For non-boolean elements */

    {

        if (eAtomic == getType())

        {

            out << '<' << getName();

            if (0 != fAttributes)

            {

                out << ' ';

                fAttributes->render(out);

            }

 

            // eAtomic类型的标签

            out << " />";

        }

        else

        {

            out << '<' << getName();

            if (0 != fAttributes)

            {

                out << ' ';

                fAttributes->render(out);

            }

            out << '>';

      

            if (0 != fEmbedded)

            {

                fEmbedded->render(out);

            }

            else

            {

                // 输出数据,

                // 如<a href="http://www.gnu.org/software/cgicc">CGICC</a>

                // 中的CGICC

                out << getData();

            }

 

            // 输出关闭标签,如:</head>

            out << "</" << getName() << '>';

        }

    }

}

8. 问题?

1) 问题1:怎么取得不在CgiEnvironment支持范围内的环境变量值?

答:可直接调用C库函数getenv()取值。

 

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

开源C++版本CGI库CGICC入门 的相关文章

  • 数字藏品是什么?

    有人说 任何东西都可以成为数字藏品 数字藏品是指通过区块链技术生成具有独特身份凭证的数字作品或艺术品 可以通过数字图片 音乐 视频 3D模型 电子门票 数字纪念品等形式进行展示 阿里巴巴 腾讯 京东 百度等互联网公司都推出了数字藏品平台或产

随机推荐

  • PMD使用与代码质量

    最近项目组要求使用PMD工具 通过自定义规则来检查代码 接录部分文档内容如下 PMD介绍 PMD是一种开源分析Java代码错误的工具 与其他分析工具不同的是 PMD通过静态分析获知代码错误 也就是说 在不运行Java程序的情况下报告错误 P
  • python100以内所有偶数-Python3基础 list 推导式 生成100以内的偶数列表

    Python 3 7 0 OS Ubuntu 18 04 1 LTS IDE PyCharm 2018 2 4 Conda 4 5 11 typesetting Markdown code coder Ubuntu source activ
  • Ubuntu系统预处理、编译、汇编、链接指令

    创建并编辑 c程序文件 gedit 1 c 以1 c为例 在编辑器中输入如下代码并保存 include
  • 基础学习5-centos7调整磁盘大小

    1 建立并查看物理磁盘 fdisk l dev sdb Disk dev sdb 10 7 GB 10737418240 bytes 20971520 sectors Units sectors of 1 512 512 bytes Sec
  • 静态Web服务器-命令行启动动态绑定端口号

    学习目标 能够写出获取终端命令行参数动态绑定端口号的web服务器程序 1 开发命令行启动动态绑定端口号的静态web服务器 实现步骤 获取执行python程序的终端命令行参数 判断参数的类型 设置端口号必须是整型 给Web服务器类的初始化方法
  • 2020-02-26

    请教大家一个AD的问题困扰多少的问题 AD10原理图复制一个器件 比如R1 正常复制粘贴还是R1 通过SHIFT拖动是R2 那如果我原理图中原本就有R2了 还是会有重复的现象 怎样复制粘贴会生成一个原理图中没有的位号呢
  • Splunk HEC 取发送数据 服务器的hostname

    1 背景 最近Client 发送数据到 Splunk HEC 发现对方hostname 没有取到 都是HEC 的VIP 地址 这个就不能发现是那个host 发过来的数据 下面查了下文档 发现Splunk 是可以跟踪发送数据的host 的 主
  • 【计算机网络】UDP协议

    目录 1 UDP协议头部格式 2 UDP协议的特点 2 1 无连接 2 2 不可靠 2 3 面向数据报 2 4 有接收缓冲区 没有发送缓冲区 2 5 大小受限 3 基于UDP的应用层协议 4 UDP协议与TCP协议对比 5 经典面试题 1
  • 基于.NET的企业级软件开发

    企业级开发最好基于一些成熟的框架 从而将主要精力集中到领域模型的设计上 1 UI与业务逻辑的隔离 在web领域可以采用ASP NET MVC框架 2 业务逻辑与DB的隔离 可以采用Entity Framework框架 3 业务逻辑中涉及工作
  • 毕业设计-基于机器视觉的水表读数智能识别系统-OpenCV

    目录 前言 课题背景和意义 实现技术思路 一 系统总体方案设计 二 图像预处理的研究与实现 三 识别区域定位及字符分割的研究与实现 实现效果图样例 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备
  • 分治算法(Java)

    想必大家通过算法的名字就已经明白了 这个算法的过程 一个是分 一个是治 那么我为什么要使用这种算法呢 因为当前的问题是我们使用现有的方法是解决不了的 所以我们需要将一个复杂的问题分成两个或者是更多个相同或相似的子问题 然后再一我们已有的方法
  • 【detectron2】注册、训练、推断自己的数据集

    一 注册自己的数据集 使用detectron2训练自己的数据集 第一步要注册自己的数据集 首先保证自己的数据集标注是coco格式 就可以使用load coco json加载自己的数据集并转化为detectron2的专有数据格式 使用Data
  • C++中关于枚举的使用(enum)

    首先加入头文件 include
  • mysql中字段长度到底是字符数还是字节数?

    这个问题 困惑了很多新学者 今天就给大家来测试测试 首先来给个定长的字段类型 因为这样好看效果 超出的会被截取 create table test id int 10 not null auto increment test name ch
  • 在VMware ESXi服务器上配置NAT上网

    文章目录 前言 一 vSphere Client上操作 1 配置网络环境 2 创建软件路由 3 虚拟机设置 总结 相关文章 前言 在使用VMware workstation的时候 我们经常以NAT的方式配置虚拟机的网络 与桥接方式相比 这样
  • spring之application.yml配置

    server port 18080 context path netty logging config classpath logback xml 文件上传下载大小 速度配置 spring http multipart max file s
  • 15_弹性盒布局

    一 弹性盒子的基本概念 弹性盒属性的使用 概念 在父级元素设置设置弹性盒属性 容器 所有的子级元素会在父级容器的轴向上排列 项目 作用 控制所有的子级元素在父级元素上的排列位置 如何形成弹性盒 属性 display 显示方式 属性值 fle
  • 软件测试-兼容性测试

    1 兼容性测试综述 软件兼容性测试 检查软件之间是否能够正确交互和共享信息 交互可以在运行在同一台计算机上的应用程序之间发生 也可以在距离数千公里的不同计算机上的应用程序之间发生 还可以用软盘在两台计算机之间传输数据 兼容性测试要解决的问题
  • Visual Studio Code调试node.js:无法在PATH上找到运行时的node

    首先 环境变量Path中加入nodejs的路径 验证nodejs是否已经加入环境变量 接着 重新启动Visual Studio Code 试一下 是不是好了 附录 Visual Studio Code 调试 nodeJS launch js
  • 开源C++版本CGI库CGICC入门

    原发布在ChinaUnix 但未被自动搬迁过来 http blog chinaunix net uid 20682147 id 4895772 html PDF版本 https download csdn net download aque