原发布在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/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输出子模块类图

n HTMLBooleanElement

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

n 标签

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

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 text</p>

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

n fEmbedded

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

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

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

<head>

<title>CGICC</title>

</head>

n fDataSpecified

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

<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文件中,定义了html、body等类(位于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;

}

其中,子类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()取值。

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

  1. 最全面的iOS和Mac开源项目和第三方库汇总

    标签: UI 下拉刷新 EGOTableViewPullRefresh – 最早的下拉刷新控件. SVPullToRefresh – 下拉刷新控件. MJRefresh – 仅需一行代码就可以为UIT ...

  2. Ptypes一个开源轻量级的c++库,包括对一些I/O操作、网络通信、多线程和异常处理的封装

    C++开源项目入门级:Ptypes    Ptypes一个开源轻量级的c++库,包括对一些I/O操作.网络通信.多线程和异常处理的封装.虽然代码有限,包括的内容不少,麻雀虽小,五脏俱全.    提高: ...

  3. 其他主流开源硬件简介BeagleBone Black快速入门

    其他主流开源硬件简介BeagleBone Black快速入门 1.3 其他主流开源硬件简介 开源硬件种类繁多,但主要有两款开源硬件常与BeagleBone比较.它们就是Arduino和Raspberr ...

  4. Python 数据处理库 pandas 入门教程

    Python 数据处理库 pandas 入门教程2018/04/17 · 工具与框架 · Pandas, Python 原文出处: 强波的技术博客 pandas是一个Python语言的软件包,在我们使 ...

  5. 开源跨平台声波传输库:Sonic

    简介 [Sonic](https://github.com/linyehui/sonic) 是一个跨平台的声波传输库(iOS & Android),技术上类似于[chirp](http://c ...

  6. 进阶攻略|最全的前端开源JS框架和库

    新的 Javascript 库层出不穷,从而Web 社区愈发活跃.多样.在多方面快速发展.详细去描述每一种主流的 Javascript框架和库近乎不可能,所以在这篇文章中主要介绍一些对前端发展最具影响 ...

  7. 最全Windows版本jemalloc库(5.2.1)及其使用:包含动态库和静态库、x86版本和x64版本、debug版本和release版本

    编写服务器程序时,需要频繁的申请和释放内存,长时间运行会产生大量的内存碎片,这就导致即使当前系统中的闲置内存还足够多,但也无法申请到大的连续可用的内存块,因为此时的物理内存已经千疮百孔像个马蜂窝.此外 ...

  8. Spark之路 --- Scala IDE Maven配置(使用开源中国的Maven库)和使用

    为什么要使用Maven 摘自百度百科的介绍 Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具.Maven 除了以程序构建能力为特色之外,还提 ...

  9. 几款国产开源的Windows界面库

    上次介绍的几款图形界面库http://blog.okbase.net/vchelp/archive/23.html都是国外的开源项目,今天介绍的几款都是国人的开源项目,大部分是采用DirectUI设计 ...

随机推荐

  1. maven中导入包版本冲突的解决

    导入struts包,在struts核心包的ognl包下存在javassist包: 然后再导入hibernate包,在hibernate核心下也存在javassist包: 这样便会存在冲突,ecplis ...

  2. 10.18JS日记

    1.JS的本质就是处理数据,数据来自后台的数据库,所以变量起到了临时存储的作用, ES制定了js的数据类型 2.数据类型有哪些? (1)字符串 String (2)数字  Number (3)布尔 B ...

  3. 织梦替换ueditor百度编辑器,支持图片水印 教程

    1下载ueditor百度编辑器 2 把下载的zip解压得到ueditor文件夹,把解压到的ueditor文件夹扔进你网站的include文件夹去 3 打开 /include/inc/inc_fun_f ...

  4. Maven的conf目录下settings.xml的简单配置

    省略一些其他配置 <?xml version="1.0" encoding="UTF-8"?> <settings xmlns="h ...

  5. Porsche PIWIS TESTER III

    Allscanner VXDIAG Porsche Piwis III with Lenovo T440P Laptop  Porsche Piwis tester III V37.250.020 N ...

  6. python——处理xls表格

    因为工作需要,现有一个运营商导出的xls固定电话话费账单. 账单比较详细,而我最终需要的数据只有那个号码这个月用了多少话费的统计结果. 当年没有好好学office,以致于无从下手.泪奔/(ㄒoㄒ)/~ ...

  7. 八大排序算法的Java代码实现

    简单插入排序 public class QuickSort { private static void quickSort(int [] a, int low, int high){ if (low ...

  8. ExportGrid Aspose.Cells.dll

    using Aspose.Cells; using Aspose.Words; using System; using System.Collections; using System.Collect ...

  9. STL基础3:map

    #include <iostream> #include <map> #include <string> using namespace std; #define ...

  10. 【算法】BFS+哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...