首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开源C++版本CGI库CGICC入门

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

作者头像
一见
发布2019-03-14 15:51:12
2K0
发布2019-03-14 15:51:12
举报
文章被收录于专栏:蓝天蓝天

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使用的):

upload                   upload:                     <form action="/cgi-bin/upload.cgi" method="post" name="formname"                   enctype="multipart/form-data">

注意,上传文件时,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 #include #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 << " accept: " << env. getAccept() << std::endl;         std::cout << " user agent: " << env.getUserAgent() << std::endl;           std::cout << " cookie: " << std::endl;         const std::vector& cookies = env.getCookieList();         for (std::vector::size_type i=0; i         {             const cgicc::HTTPCookie& cookie = cookies[i];             std::cout << "    cookie[" << cookie.getName()                       << "] = " << cookie.getValue() << std::endl;         }           std::cout << " query string: " << env.getQueryString() << std::endl;         std::cout << " remote: " << env.getRemoteAddr() << ":" << env.getServerPort()                   << std::endl;           std::cout << " form: " << std::endl;         const std::vector& form_entries = cgi.getElements();         for (std::vector::size_type i=0; i         {             const cgicc::FormEntry& form_entry = form_entries[i];             std::cout << "    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 << " file: " << cgi.getFiles().size() << std::endl;         }         else         {             const cgicc::FormFile& file = *file_iter;             std::cout << " file: " << std::endl;             std::cout << "    name: "                       << file.getName() << std::endl;             std::cout << "    filename: "                       << file.getFilename() << std::endl;             std::cout << "    type: "                       << file.getDataType() << std::endl;             std::cout << "    size: "                       << file.getDataLength() << std::endl;             std::cout << "    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 标签

对于,前者叫开始标签,后者叫关闭标签。

n 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 textp>

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

n fEmbedded

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

下面这行为无fEmbedded的eBoolean标签:     下段这段也是含fEmbedded的eBoolean的标签,“CGICC”为标签head的fEmbedded内容:     CGICC

n fDataSpecified

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

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

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

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

1) 宏BOOLEAN_ELEMENT

#define BOOLEAN_ELEMENT(name, tag) \     TAG(name, tag); typedef HTMLBooleanElement 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 HTMLBooleanElementTag> html; // html是不是就是一个类了?

html()怎么来的清楚了,还有一个疑问:对于:cout << 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::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();                   // 开始标签是可能包含属性的,                 // 如:,                 // 这里的href即为标签的属性                 if (0 != fAttributes)                 {                     out << ' '; // 属性间使用一个空格分开                     fAttributes->render(out);                 }                   out << '>';             }             else             {                 // 输出关闭标签,如:                 out << "';             }         }         else /* embedded elements present */         {             // 被嵌入的(embedded)的内容总是整体一次性输出,             // 而不是区分其状态值HTMLBooleanElement::sState             out << '<' << getName();               /* render attributes, if present */             if (0 != fAttributes)             {                 out << ' ';                 fAttributes->render(out);             }               out << '>';             fEmbedded->render(out);               // 输出关闭标签,如:             out << "';         }     }     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             {                 // 输出数据,                 // 如CGICC                 // 中的CGICC                 out << getData();             }               // 输出关闭标签,如:             out << "';         }     } }

8. 问题?

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-03-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 简介
  • 2. CGICC组成
  • 3. CGI输入处理子模块类结构
    • 3.1. Cgicc
      • 3.2. CgiEnvironment
        • 3.3. HTTPCookie
          • 3.4. CgiInput
            • 3.5. FormFile
              • 3.6. FormEntry
              • 4. CGI输入处理子模块初始化流程
              • 5. 编译和安装CGICC
              • 6. CGICC使用示例
                • 6.1. 页面效果
                  • 6.2. HTML文件
                    • 6.3. test.txt文件
                      • 6.4. CGI文件
                        • 6.5. 运行效果
                        • 7. HTML输出子模块类图
                          • 7.1. HTTPContentHeader
                            • 7.2. HTMLElement::render()函数
                            • 8. 问题?
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档