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


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

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

    2. CGICC组成

    CGICC由两大部分组成:

    1) CGI输入处理子模块

    2) HTML输出子模块

     

    本文暂只介绍CGI输入处理子模块,对于HTML输出,推荐Google开源的ctemplatehttps://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

    提供访问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对象:

     

    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. 页面效果

     

    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输出子模块类图

     

    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="http://aquester.cublog.cn">一见的技术博客</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名字空间内),但是有些隐晦,直接看不出来:

     

     

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

    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

     

    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()取值。

  • 相关阅读:
    淘淘整理相关链接
    Ajax跨域请求
    tomcat启动(六)Catalina分析-StandardServer.start()
    Tomcat中组件的生命周期管理公共接口Lifecycle
    Catalina.createDigester方法详细理解
    SpringMVC访问WEB-INF下的jsp的方法
    tomcat启动(五)Catalina分析-service.init
    tomcat启动(四)Catalina分析-server的init()方法
    tomcat启动(三)Catalina分析-load方法分析
    tomcat启动(三)Catalina简要分析
  • 原文地址:https://www.cnblogs.com/aquester/p/10064335.html
Copyright © 2020-2023  润新知