欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-19 13:52:47
原发布在ChinaUnix,但未自动搬迁过来:http://blog.chinaunix.net/uid-20682147-id-4895772.html PDF版本:https://files-cdn.cnblogs.com/files/aquester/%E5%BC%80%E6%BA%90C%E ......

原发布在chinaunix,但未自动搬迁过来:

pdf版本:https://files-cdn.cnblogs.com/files/aquester/%e5%bc%80%e6%ba%90c%e5%8a%a0%e5%8a%a0%e7%89%88%e6%9c%accgi%e5%ba%93cgicc%e5%85%a5%e9%97%a8.pdf

目录

目录 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官网:,截止2015/3/14cgicc最新稳定版本为3.2.16,下载地址是:,最新更新时间为2014/12/7(令人惊讶和欣慰的是作为古老的cgicgicc还在不断的更新)。

2. cgicc组成

cgicc由两大部分组成:

1) cgi输入处理子模块

2) html输出子模块

 

本文暂只介绍cgi输入处理子模块,对于html输出,推荐google开源的ctemplatehttps://github.com/olafvdspek/ctemplate)。

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

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

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

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

3.6. formentry

提供访问htmlform中的非被上传文件类的信息和数据接口。取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构造函数触发的,一般可在cgimain函数中定义一个cgicc对象:

 开源C++版本CGI库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.gztar 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. 页面效果

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

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>

 

注意,上传文件时,formenctype属性值必须被设定为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输出子模块类图

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

n htmlbooleanelement

注意sstate是类htmlbooleanelement的静态数据成员,sstate的数据类型为bool

n 标签

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

n eelementtype

枚举类型,定义了两个枚举值:eatomiceboolean,eatomic对应的实现类为htmlatomicelement,eboolean对应的实现为htmlbooleanelement。类似于strong类的为eboolean类型,而类似于hrbr之类的为eatomic类型。对于eatomic类型的html标签,它没有对应的关闭标签(也叫结束标签),观察以下两组的差别:

<br />

<strong>this text is strong</strong>

 

<hr />

<p>this is some text</p>

 

可以看到br和hr均是eatomic类型的标签,而strong和p均是eboolean类型的标签。

 

n fembedded

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

下面这行为无fembedded的eboolean标签:

 

 

下段这段也是含fembedded的eboolean的标签,“<title>cgicc</title>”为标签head的fembedded内容:

<head>

    <title>cgicc</title>

</head>

 

 

n fdataspecified

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

<a href="">一见的技术博客</a>

 

n 代码中的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文件中,定义了htmlbody等类(位于cgicc名字空间内),但是有些隐晦,直接看不出来:

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

 

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

1) 宏boolean_element

#define boolean_element(name, tag) \

    tag(name, tag); typedef htmlbooleanelement<name##tag> name

 

2) 宏tag

// 注意区分下面的tag和tag

#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<htmltag> html; // html是不是就是一个类了?

 

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

7.1. httpcontentheader

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

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;

}

 

其中,子类httphtmlheadergetdata()返回“text/html”,子类httpplainheadergetdata()返回“text/plain”,子类httpxhtmlheadergetdata()返回“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()取值。