[转]w3c-libwww入门教程

【libwww介绍】

官方网站:http://www.w3.org/Library/
更多信息:http://www.w3.org/Library/User/
运行平台:Unix/Linux,Windows
以下资料来源:http://zh.wikipedia.org/wiki/Libwww

简介:
Libwww 是一个高度模组化用户端的网页存取API ,用C语言写成,可在 Unix 和 Windows 上运行。 It can be used for both large and small applications including: browsers/editors, robots and batch tools. There are pluggable modules provided with Libwww which include complete HTTP/1.1 with caching, pipelining, POST, Digest Authentication, deflate, etc. The purpose of libwww is to serve as a testbed for protocol experiments. 蒂姆·伯纳斯-李 在 1992 年十一月创造出了 Libwww,用於展示网际网路的潜能。使用 Libwww 的应用程式,如被广泛使用的命令列文字浏览器 Lynx 及 Mosaic web browser 即是用 Libwww 所写成的。 Libwww 目前为一开放原始码程式,并於日前移至 W3C 管理。基於其为开放原始码的特性,任何人都能为 Libwww 付出一点心力,这也确保了 Libwww 能一直进步,成为更有用的软体。

以下文章来源:
http://bbs.nju.edu.cn/bbsanc?path=/groups/GROUP_3/CPlusPlus/D74D7D299/M.1090339010.A


w3c-libwww入门教程
丁建华 <[email protected]> 2004.7.20

如果你 google 一下 libwww, 会发现三个不同的软件包: w3c-libwww,
perl-libwww, glibwww. 它们都是用来处理与 www 有关的各种协议的, 比如
http, ftp, news, gopher 等等. 其中 perl-libwww 是用于 perl 语言环境的,
glibwww 是 gnome 对 w3c-libwww 的一个简单的包装. w3c-libwww 是最古老的,
而且也可以说是最权威的, (假如存在所谓的权威的话 :)) 因为 w3c-libwww
的开发目标就是为 w3.org 发布的各种协议提供一个测试平台, 以评估各种
协议的可行性, 合理性, 兼容性, 可扩充性, 效率与安全等各方面的情况的.
因此其代码绝对值得一读. 但是初学者常常觉得这个函数库太庞杂, 抓任何一点
都会带出一大片, 找不到重点, 理不清头绪. 笔者希望本文能够对他们有所帮助.

===================================================================
??? w3c-libwww 是什么?
>>> Please visit http://www.w3.org/Library/

===================================================================
??? 如何搭建一个 w3c-libwww 的调试平台?
>>> 最好在 Linux 系统上, 把软件包解压, 然后
<cmd> ./configure --enable-shared=no --prefix=/home/me --with-ssl --with-zlib
--with-regex </cmd>
<cmd> make </cmd>
<cmd> make install </cmd>
这样会建立 /home/me/bin, /home/me/lib, /home/me/include 等目录. 你需要把
/home/me/bin 加入你的搜索路径, 以便系统能够找到 libwww-config. 这个配置
程序可能需要手工修改, 好在它非常简单, 对任何想学习 w3c-libwww 的人来说,
改这个 sh 程序不成问题.

===================================================================
??? 如何编译一个程序呢?
>>> 假如你的程序是 prog.c, 你可以这样编译:
<cmd> gcc -g -Wall -c -o prog.o `libwww-config --cflags` prog.c </cmd>
<cmd> gcc -g -o prog prog.o `libwww-config --libs` </cmd>
这样编译出来的程序是包含调试信息的 static 连接, 你可以对 w3c-libwww 的
库函数源代码进行调试.

===================================================================
??? 我能在 Win32 上建立这样的环境吗?
>>> 如果你有足够的钱购买 M$ 的开发工具, 我建议你关掉电脑出去好好享受生活.
如果你只能安装免费的 Cygwin, 那么恐怕不能对 w3c-libwww 的源代码进行调试了.
不过你还是可以用 dll 来做开发: 先用与 Linux 同样的方法在 Cygwin 上装一遍,
尽管这样装出来的 static 库问题很多, 几乎无法使用, 但是我们还是得到了
必要的头文件. 然后我们到源代码的 Library/src/windows 目录下面, 用
<cmd> dlltool -D wwwapp.dll -d wwwapp.def -l libwwwapp.dll.a </cmd>
生成所有必要 dll 的 import 库, (有几个 export 符号重复, 发现后注释掉.)
复制到目标 lib 下就可以了. 当然还要修改 libwww-config, 把所有的 wwwapp
之类的名字都改成 wwwapp.dll 这样的, 也就是把 static 连接都改成 shared
连接. 就可以了.

===================================================================
??? 那些 wwwapp.dll 在哪里啊?
>>> 从 http://www.w3.org 下载 w3c-libwww 的 Win32 发行包并安装. 然后把
其中的 dll 复制到你的某个搜索路径下面. 我就放在 gtk\2.0\bin 下面.

===================================================================
??? gtk+ 有一个 pkg-config 可以拿来用吗?
>>> 可以. 把下面这个文件放到 /lib/pkgconfig 下面就行了.
<file "/lib/pkgconfig/w3c-libwww.pc">
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
target=win32

Name: w3c-libwww
Description: w3c libwww (${target} target)
Version: 5.4.0
Requires:
Libs: -L${libdir} -lwwwxml.dll -lxmltok.dll -lxmlparse.dll -lwwwzip.dll -lwww
init.dll -lwwwapp.dll -lwwwhtml.dll -lwwwtelnt.dll -lwwwnews.dll -lwwwhttp.dl
l -lwwwmime.dll -lwwwgophe.dll -lwwwftp.dll -lwwwfile.dll -lwwwdir.dll -lwwwc
ache.dll
-lwwwstream.dll -lwwwmux -lwwwtrans.dll -lwwwcore.dll -lwwwutils.dll -lmd5 -L
/usr/lib -lz -lssl -lcrypto
Cflags: -I${includedir} -I${includedir}/w3c-libwww
</file>
你可以用 <cmd> pkg-config --list-all </cmd> 试试, 看能否列出 w3c-libwww.
当然, 如果你的 prefix 不同, 你可以修改.

===================================================================
??? 为什么 gcc 顽固地提示某些符号无法解决呢?
>>> 修改 libwww-config 或者 w3c-libwww.pc , 把其中的库列表用
-Wl,--start-group 和 -Wl,--end-group 括起来.

===================================================================
??? Cygwin 下的 gtk+ 开发需要使用 -mms-bitfields -mno-cygwin 选项,
w3c-libwww 需要吗?
>>> 为保持良好的可移植性, w3c-libwww 没有使用位域, 因此与 -mms-bitfields
选项无关, 也不强制要求使用 -mno-cygwin 选项, 但是考虑到与 gtk+ 的集成问题,
笔者推荐使用 -mno-cygwin .

===================================================================
??? Linux 下 <cmd> libwww-config --cflags </cmd> 还包含一个 -DHAVE_CONFIG_H,
这是必要的吗? 在 Cygwin 下情况如何?
>>> 在 Linux 下是必要的. 在 Cygwin 下则是*不能*要的. 而且你必须从源代码的
Library/src/windows 目录下面复制一份儿 config.h 到安装目录的
include/w3c-libwww/windows 下. 可能还需要注释掉下面这个配置项目:
<code "include/w3c-libwww/windows/config.h">
/* Define if you have the <direct.h> header file. */
/*#define HAVE_DIRECT_H 1*/
</code>
然后, 你*必须*使用 -D_CONSOLE 或者 -D_WINDOWS 中的一个. 原因请阅读
include/w3c-libwww/wwwsys.h . 你可能还需要注释掉这个文件中这行代码:
<code "include/w3c-libwww/wwwsys.h">
/* #define MKDIR(a,b) mkdir(a) */
</code>
你可能还需要从系统 include 目录下面复制一份儿 regex.h 到你的安装
include/w3c-libwww 目录下.

===================================================================
??? 如何知道我的程序使用了正确的 dll 呢?
>>> <cmd> cygcheck prog.exe </cmd>

===================================================================
??? 听说 w3c-libwww 使用 C 语言实现了许多 OO 风格的编程, 是吗?
>>> http://www.w3.org/Library/User/Style/

===================================================================
??? w3c-libwww 的文档不提供打包下载, 我必须在线阅读吗?
>>>
<cmd> wget -x -r -nc -nH -p -np --cut-dirs=2 http://www.w3.org/Library/User <
/cmd>

===================================================================
??? 给个程序试一试, 看看开发调试环境是否已经配置好.
>>>
<file "libinit.c">
/*
On Linux:
gcc -g -Wall -o libinit libinit.c `pkg-config --cflags --libs w3c-libwww`

On Cygwin:
gcc -c -g -Wall -mno-cygwin -D_CONSOLE -o libinit.o \
`pkg-config --cflags w3c-libwww` libinit.c
gcc -g -mno-cygwin -o libinit.exe libinit.o `pkg-config --libs w3c-libwww`
*/
#include <WWWLib.h>

/* w3c-libwww 没有提供 Win32 下的缺省 Print 函数, 因此我们必须自己做 */
int printer(const char *fmt, va_list args)
{
return vfprintf(stdout, fmt, args);
}

int main(int argc, char *argv[])
{
/* Win32 下要使用 HTPrint() 就必须自己设置 callback */
HTPrint_setCallback(printer);
HTPrint("Hello w3c-libwww\n");

HTLibInit("TestApp", "0.0.1");
HTLibTerminate();
return 0;
}
</file>
另外, Win32 下 HTTRACE() 无效.

===================================================================
??? 开发调试环境配置好了. 一个典型的 w3c-libwww 应用是个什么样子?
>>> 实现某个协议, 按照这个协议发送请求, 或者响应请求.
<file "req.c">
#include <WWWLib.h>
#include <WWWInit.h>

int printer(const char *fmt, va_list args)
{
return vfprintf(stdout, fmt, args);
}

/* 实现某个协议的客户端代码, 通常要实现一个状态机 */
int my_client(SOCKET sock, HTRequest *req)
{
HTNet *net = HTRequest_net(req);

HTPrint("This's my_client\n");
HTNet_delete(net, HT_OK);
return HT_OK;
}

/* 实现某个协议的服务器端代码, 通常要实现一个状态机 */
int my_server(SOCKET sock, HTRequest *req)
{
HTNet *net = HTRequest_net(req);

HTPrint("This's my_server\n");
HTNet_delete(net, HT_OK);
return HT_OK;
}

int main(int argc, char *argv[])
{
HTRequest *req;

HTLibInit("ProtTest", "0.0.1");
HTPrint_setCallback(printer);

HTTransport_add("mytp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
/* 注册我们的协议 */
HTProtocol_add("myprot", "mytp", 2008, NO, my_client, my_server);

req = HTRequest_new();

/* 发出一个按照我们协议的请求 */
HTLoadAbsolute("myprot://localhost", req);
HTPrint("\n");
/* 响应一个按照我们协议的请求 */
HTServeAbsolute("myprot://localhost", req);

HTRequest_delete(req);

HTLibTerminate();
return 0;
}
</file>

===================================================================
??? 文档上说, 过滤器很重要. 过滤器是如何设置和被调用的?
>>>
<file "filter.c">
#include <WWWLib.h>
#include <WWWInit.h>

int printer(const char *fmt, va_list args)
{
return vfprintf(stdout, fmt, args);
}

int my_client(SOCKET sock, HTRequest *req)
{
HTNet *net = HTRequest_net(req);

HTPrint("This's my_client\n");
HTNet_delete(net, HT_OK); /* 造成后置过滤器被调用 */
return HT_OK;
}

int my_server(SOCKET sock, HTRequest *req)
{
HTNet *net = HTRequest_net(req);

HTPrint("This's my_server\n");
HTNet_delete(net, HT_OK); /* 造成后置过滤器被调用 */
return HT_OK;
}

int my_before(HTRequest *req, void *param, int mode)
{
HTPrint("This's my_before(%s)\n", (char *)param);
return HT_OK;
}

int my_after(HTRequest *req, HTResponse *resp, void *param, int status)
{
HTPrint("This's my_after(%s, %d)\n", (char *)param, status);
return HT_OK;
}

int my_req_before(HTRequest *req, void *param, int mode)
{
HTPrint("This's my_req_before(%s)\n", (char *)param);
return HT_OK;
}

int my_req_after(HTRequest *req, HTResponse *resp, void *param, int status)
{
HTPrint("This's my_req_after(%s, %d)\n", (char *)param, status);
return HT_OK;
}

int main(int argc, char *argv[])
{
HTRequest *req;

HTLibInit("ProtTest", "0.0.1");
HTPrint_setCallback(printer);

HTTransport_add("mytp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
HTProtocol_add("myprot", "mytp", 2008, NO, my_client, my_server);
/* 注册全局前置过滤器和后置过滤器 */
HTNet_addBefore(my_before, NULL, "before-param", HT_FILTER_LAST);
HTNet_addAfter(my_after, NULL, "after-param", HT_ALL, HT_FILTER_LAST);

req = HTRequest_new();
/* 注册请求前置过滤器和后置过滤器 */
HTRequest_addBefore(req, my_req_before, NULL, "req_before_param",
HT_FILTER_LAST, NO);
HTRequest_addAfter(req, my_req_after, NULL, "req_after_param", HT_ALL,
HT_FILTER_LAST, NO);

HTLoadAbsolute("myprot://localhost", req);
HTPrint("\n");
HTServeAbsolute("myprot://localhost", req);

HTRequest_delete(req);

HTLibTerminate();
return 0;
}
</file>

===================================================================
??? 任何具有 OO 风格的, 都必须处理事件. 请给个事件的例子好吗?
>>>
<file "timer.c">
#include <WWWLib.h>
#include <WWWInit.h>

int printer(const char *fmt, va_list args)
{
return vfprintf(stdout, fmt, args);
}

int my_timer(HTTimer *tmr, void *param, HTEventType type)
{
static int count = 0;

HTPrint("I'm my_timer(%s) %d\n", (char *)param, count++);
if (count >= 3)
HTEventList_stopLoop();
return HT_OK;
}

int main(int argc, char *argv[])
{
HTTimer *timer;

HTPrint_setCallback(printer);
HTLibInit("TestApp", "0.0.1");
HTEventInit();

timer = HTTimer_new(NULL, my_timer, "my-timer", 2000, YES, YES);
HTEventList_newLoop();
HTPrint("app stopped\n");

HTEventTerminate();
HTLibTerminate();
return 0;
}
</file>

===================================================================
??? 输入输出无疑是非常重要的. 请给个输出流的例子好吗?
>>>
<file "out_strm.c">
#include <WWWLib.h>
#include <WWWInit.h>
#include <WWWStream.h>

struct _HTStream {
HTStreamClass *isa;
};

static int printer(const char *fmt, va_list pArgs)
{
return vfprintf(stderr, fmt, pArgs);
}

int main(int argc, char *argv[])
{
HTStream *out;

HTPrint_setCallback(printer);
HTLibInit("Test", "0.0.1");

out = HTFWriter_new(NULL, stdout, YES);
HTPrint("I'm %s\n", out->isa->name);
(out->isa->put_string)(out, "Hello, stream\n");
(out->isa->flush)(out);
(out->isa->_free)(out);

HTLibTerminate();
return 0;
}
</file>

===================================================================
??? 请给个输入流的例子.
>>>
<file "in_strm.c">
#include <WWWLib.h>
#include <WWWInit.h>
#include <WWWStream.h>

struct _HTInputStream {
HTInputStreamClass *isa;
};

int main(int argc, char *argv[])
{
HTStream *out;
HTInputStream *in;
HTChannel *chnl;
HTNet *innet;
HTHost *host;
FILE *fp;

if (NULL == (fp = fopen(__FILE__, "rb")))
return (fprintf(stderr, "Can't open source file\n"), 1);
HTLibInit("Test", "0.0.1");

host = HTHost_new("localhost", 0);
innet = HTNet_new(host);
out = HTFWriter_new(NULL, stdout, YES);
HTNet_setReadStream(innet, out);
HTHost_addNet(host, innet);

chnl = HTChannel_new(INVSOC, fp, YES);

in = HTANSIReader_new(host, chnl, NULL, 0);
(in->isa->read)(in);
(in->isa->close)(in);

HTChannel_delete(chnl, HT_OK);

HTLibTerminate();
fclose(fp);
return 0;
}
</file>

===================================================================
??? 我怎么没看到 write 啊? 读入的东西是怎么跑到输出上去的呢?
>>> w3c-libwww 认为, 没有无缘无故的读, 也没有无缘无故的写. 因此你必须
在读之前设置好输出, 然后在 (in->isa->read)(in) 的时候, 会找到你所设置
的输出, 并写到这个输出里面. 调用方只管事前设置就行了, 无需事中干预.
参见 Library/src/HTANSI.c!HTANSIReader_read .

===================================================================
??? 这些事前设置好像很乱啊.
>>> 那是为了循序渐进, 每次引入几个概念以便读者逐步认识. 如果不是自己
开发新协议, 而只是应用 w3c-ilbwww 的话, 可以简单一些.
<file "sload.c">
/* CAUTION: For Cygwin Win32 ONLY !!! */
#include <WWWLib.h>
#include <WWWInit.h>

int terminate_handler(HTRequest * request, HTResponse * response,
void * param, int status)
{
return (HTEventList_stopLoop(), HT_OK);
}

int main(int argc, char *argv[])
{
char * url = NULL;
HTRequest * request = HTRequest_new();

HTLibInit("Simple load file", "0.0.1");
HTTransport_add("local", HT_TP_SINGLE, HTANSIReader_new,
HTANSIWriter_new);
HTProtocol_add("file", "local", 0, YES, HTLoadFile, NULL);
HTEventInit();
HTNet_addAfter(terminate_handler, NULL, NULL, HT_ALL, HT_FILTER_LAST);

url = HTGetCurrentDirectoryURL();
HTSACat(&url, __FILE__);
if (HTLoadToFile/*!HTLoadFile*/(url, request, "CON") == YES)
HTEventList_loop(request);
HT_FREE(url);

HTRequest_delete(request);
HTEventTerminate();
HTLibTerminate();
return 0;
}
</file>

===================================================================
??? 这回连 read 也看不到了.
>>> 参见 Library/src/HTFile.c!HTLoadFile 和 HTHost.c!HTHost_read .

===================================================================
??? 从 HTLoadToFile 到 HTLoadFile 又发生了什么呢?
>>> 按照下列顺序去读 Library/src 下的源代码:
HTAccess.c!HTLoadToFile(url, request, filename)
HTAccess.c!HTLoadAbsolute(url, request)
HTReqMan.c!HTLoad(request, recursive)
HTNet.c!HTNet_newClient(request)
HTProt.c!HTProtocol_find(request, access)
HTProt.c!HTProtocol_client(protocol) 就是 HTLoadFile 了.
最后的调用语句是这样的: (*(cbf))(INVSOC, request) .

===================================================================
??? HTNet_newServer(request) 和 HTNet_newClient(request) 主要干什么?
>>> 首先 HTNet_executeBeforeAll(request), 然后 create_object() 创建一个
空的 HTNet, 设置好 preemptive, protocol, transport, request, 同时也
HTRequest_setNet(request, me), 建立 request 和 net 的双向互指关系.
然后就 (*(cbf))(INVSOC, request), 调用服务(或客户)回调函数.

===================================================================
??? 为什么要做的这么麻烦啊?
>>> 比如小学生做应用题, 通常会列出一排算式, 都是具体数字, 每一步得一个
结果, 实实在在. 但这只是算术, 很初级的东西. 最明显的局限是"可移植性"
和"可重用性"太低. 高级一点的就是代数. 用常数和未知数取代具体数字,
在不知道其具体内容的情况下进行运算. 写程序也是这样. 初学者总是习惯于调用
具体的函数, 比如为 file:// 写一个 HTNet_newFileClient, 为 http:// 写一个
HTNet_newHttpClient, 或者把协议类型作为参数传进来, 然后用 switch 分别
调用 HTLoadFile, HTLoadHttp. 如果有一天要增加一个 newprot:// , 那么这些
代码就都要作相应改动. 这就相当于"算术"的水平. 高级一点的就使用函数指针.
不管这个函数的具体内容, HTNet_newClient 只管在适当的环境下以适当的方式
进行调用就行了. 就像对未知数进行"代数"运算一样. 这样的程序抽象层次更高,
适用性更广. 增改协议的时候, 不需要改动 HTNet_newClient. 类似的技术今天
已经应用得相当普遍. 比如 C++ 的抽象类, Java 和 Gtk+ 的 Interface 等等.

===================================================================
??? 我如何建立一个服务呢?
>>> 有一个 Library/Examples/listen.c 改写如下:
<file "listen.c">
#include <WWWLib.h>
#include <WWWInit.h>

int global_after(HTRequest *req, HTResponse *resp, void *param, int status)
{
return (HTEventList_stopLoop(), HT_OK);
}

int main(int argc, char *argv[])
{
HTRequest *req = HTRequest_new();
HTStream *out;

HTLibInit("Simple Listener", "0.0.1");
HTEventInit();
HTNet_addAfter(global_after, NULL, NULL, HT_ALL, HT_FILTER_LAST);
HTTransport_add("tcp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
/* 注意端口号是 2004 (任意), 服务回调函数是 HTLoadSocket */
HTProtocol_add("noop", "tcp", 2004, NO, NULL, HTLoadSocket);

HTRequest_setOutputFormat(req, WWW_RAW);
out = HTFWriter_new(req, stdout, YES);
HTRequest_setOutputStream(req, out);

HTServeAbsolute("noop://localhost", req);
HTEventList_newLoop();

HTRequest_delete(req);
HTEventTerminate();
HTLibTerminate();
return 0;
}
</file>
执行 listen.exe , 然后从浏览器上访问 http://localhost:2004 , 就可以从
控制台看到浏览器发给服务器的请求的全部内容. 用 telnet://localhost:2004
连接, 则可以看到从 telnet 终端输入的内容都显示在控制台上.

===================================================================
??? 看起来关键是服务回调函数 HTLoadSocket , 它是干什么的?
>>> 参见 Library/src/HTSocket.c . 先做一个 HTHost_listen, 然后进入
SocketEvent, 若是 RAW_BEGIN, 则 HTHost_accept, 进入 RAW_NEED_STREAM.
如是 RAW_NEED_STREAM, 则把 request 的 outputStream 设置为 net 的
readStream , 并设置已连接, 进入 RAW_READ. 若是 RAW_READ, 则进行
HTHost_read.

===================================================================
??? 我能做一个 echo 吗?
>>>
<file "secho.c">
#include <WWWLib.h>
#include <WWWInit.h>

int global_after(HTRequest *req, HTResponse *resp, void *param, int status)
{
HTEventList_stopLoop();
return HT_OK;
}

typedef enum _EchoState {
ECHO_ERROR = -2,
ECHO_OK = -1,
ECHO_BEGIN = 0,
ECHO_NEED_STREAM,
ECHO_READ
} EchoState;

typedef struct _echo_info {
EchoState state;
HTNet *net;
HTRequest *request;
} echo_info;

static int EchoCleanup(HTRequest *request, int status)
{
HTNet *net = HTRequest_net(request);
echo_info *echo = (echo_info *)HTNet_context(net);

if (status == HT_INTERRUPTED) {
(void)0;
} else if (status == HT_TIMEOUT) {
(void)0;
}
HTNet_delete(net, HT_ERROR);
HT_FREE(echo);
return YES;
}

static int EchoEvent(SOCKET soc, void *pVoid, HTEventType type);

int HTServEcho(SOCKET soc, HTRequest *request)
{
echo_info *echo;
HTNet *net = HTRequest_net(request);
char *url;

if (NULL == (echo = (echo_info *)HT_CALLOC(1, sizeof(echo_info))))
HT_OUTOFMEM("HTServEcho");
echo->state = ECHO_BEGIN;
echo->net = net;
echo->request = request;
HTNet_setContext(net, echo);
HTNet_setEventCallback(net, EchoEvent);
HTNet_setEventParam(net, echo);

url = HTAnchor_physical(HTRequest_anchor(request));
if (HTHost_listen(NULL, net, url) == HT_ERROR)
/* HTNet_execute(net, HTEvent_CLOSE) */
return EchoEvent(soc, echo, HTEvent_CLOSE);
/* HTNet_start(net) */
return EchoEvent(soc, echo, HTEvent_BEGIN);
}

static int EchoEvent(SOCKET soc, void *pVoid, HTEventType type)
{
echo_info *echo = (echo_info *)pVoid;
int status = HT_ERROR;
HTNet *net = echo->net;
HTRequest *request = echo->request;
HTHost *host = HTNet_host(net);

if (type == HTEvent_BEGIN) {
echo->state = ECHO_BEGIN;
} else if (type == HTEvent_CLOSE) {
EchoCleanup(request, HT_INTERRUPTED);
return HT_OK;
} else if (type == HTEvent_TIMEOUT) {
HTRequest_addError(request, ERR_FATAL, NO, HTERR_TIME_OUT,
NULL, 0, "HTServEcho");
EchoCleanup(request, HT_TIMEOUT);
return HT_OK;
} else if (type == HTEvent_END) {
EchoCleanup(request, HT_OK);
return HT_OK;
}

while (1) {
switch (echo->state) {
case ECHO_BEGIN:
status = HTHost_accept(host, net, NULL);
host = HTNet_host(net);
if (status == HT_OK) {
echo->state = ECHO_NEED_STREAM;
} else if (status == HT_WOULD_BLOCK
|| status == HT_PENDING) {
return HT_OK;
} else {
echo->state = ECHO_ERROR;
}
break;
case ECHO_NEED_STREAM:
HTNet_setReadStream(net,
(HTStream *)HTNet_getOutput(net, NULL, 0));
HTRequest_setOutputConnected(request, YES);
echo->state = ECHO_READ;
break;
case ECHO_READ:
status = HTHost_read(host, net);
if (status == HT_WOULD_BLOCK)
return HT_OK;
else if (status == HT_CLOSED)
echo->state = ECHO_OK;
else
echo->state = ECHO_ERROR;
break;
case ECHO_OK:
EchoCleanup(request, HT_OK);
return HT_OK;
break;
case ECHO_ERROR:
EchoCleanup(request, HT_ERROR);
return HT_OK;
break;
default:
HTDEBUGBREAK("Bad echo state %d\n" _ echo->state);
}
}
return HT_OK;
}

int main(int argc, char *argv[])
{
HTRequest *req;

HTLibInit("Simply Echo", "0.0.1");
HTEventInit();
HTNet_addAfter(global_after, NULL, NULL, HT_ALL, HT_FILTER_LAST);
HTTransport_add("tcp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
HTProtocol_add("noop", "tcp", 2004, NO, NULL, HTServEcho);

req = HTRequest_new();
HTRequest_setOutputFormat(req, WWW_RAW);

HTServeAbsolute("noop://localhost", req);
HTEventList_newLoop();

HTRequest_delete(req);
HTEventTerminate();
HTLibTerminate();
return 0;
}
</file>
这个程序在 Win32/Cygwin 上编译后, 运行. 用 telnet 连接本机端口 2004,
就可以看到 echo 的效果了. 但是在 linux 上编译后, 只能 echo 一下,
包括其本身的源码 Library/Example/listen.c 也是这样. 原因不详.

===================================================================
??? 事件究竟是在什么地方被执行的?
>>>
<code "HTEvtLst.c!AsyncWindowProc(hwnd, wMsg, wParam, lParam)">
if (uMsg == WM_TIMER) {
HTTimer_dispatch((HTTimer *)wParam);
return (0);
}
/* dispatch() means (*event->cbf)(s, event->param, type); */
if (HTEventList_dispatch((int)sock, type, now) != HT_OK)
HTEndLoop = -1;
</code>
另一方面:
<code "HTEvtLst.c!HTEventList_loop(NULL)">
/* Don't leave this loop until we leave the application */
while (!HTEndLoop) {
if (active_sockets == 0)
continue;

/* There were active sockets. Determine which fd sets they were in */
for (s = 0 ; s <= maxfds ; s++) {
if (FD_ISSET(s, &texceptset))
if ((status = EventOrder_add(s, HTEvent_OOB, now)) != HT_OK)
goto stop_loop;
if (FD_ISSET(s, &twriteset))
if ((status = EventOrder_add(s, HTEvent_WRITE, now)) != HT_OK)
goto stop_loop;
if (FD_ISSET(s, &treadset))
if ((status = EventOrder_add(s, HTEvent_READ, now)) != HT_OK)
goto stop_loop;
}
/* execute() means (*event->cbf)(s, event->param, type); */
if ((status = EventOrder_executeAndDelete()) != HT_OK) break;
};
</code>

===================================================================
??? secho 只能接受一个连接, 我如何做一个真正的服务器呢?
>>> 从 4.0 版到 5.0 版, w3c-libwww 曾经有个短命的 MiniServer 子项目.
到 5.1 版就被放弃了. 由于现在的 5.4 版与 5.0 版在 API 上已经发生了巨大的
变化, 因此这个 MiniServer 的源代码已经没有什么参考价值了. w3c-libwww
可能是出于 "只做一件事情, 力图做到最好" 的考虑, 放弃了对服务器框架的支持.

===================================================================
??? 用 w3c-libwww 来开发客户端, 有哪些典型的应用呢?
>>> 你自己去看文档吧, 有很多好的例子. 我能说的就到此为止了.
本文附录有一些随手摘录的笔记, 既不正式, 也不完全, 聊作参考吧.

===================================================================
附: 本文档例子程序在 Win32/Cygwin 平台上的 Makefile,
<file "Makefile">
all: libinit.exe req.exe filter.exe timer.exe out_strm.exe in_strm.exe \
sload.exe listen.exe secho.exe

.SUFFIXES:
.SUFFIXES: .exe .o .c

.o.exe:
gcc -g -mno-cygwin -o $@ $< `pkg-config --libs w3c-libwww`

.c.o:
gcc -c -g -Wall -mno-cygwin -D_CONSOLE -o $@ \
`pkg-config --cflags w3c-libwww` $<

clean:
@rm -f *.exe *.o

.PHONY: all
</file>

===================================================================
??? WWWLib.h 包括哪些内容?
>>>
<code "WWWLib.h">
#include "wwwsys.h"
#include "WWWUtil.h"
#include "WWWCore.h"
</code>

===================================================================
??? WWWUtil.h 包括哪些内容?
>>>
<code "WWWUtil.h">
#include "wwwsys.h"
#include "HTUtils.h"
#include "HTArray.h"
#include "HTAssoc.h"
#include "HTAtom.h"
#include "HTChunk.h"
#include "HTList.h"
#include "HTMemory.h"
#include "HTString.h"
#include "HTUU.h"
</code>

===================================================================
??? libwww 是如何处理 HTPrint 的?
>>> 用户可以设置自己的方式。对 Win32 平台,则是“必须”设置自己的方式。
<code "HTUtils.h">
typedef int HTPrintCallback(const char * fmt, va_list pArgs);

extern void HTPrint_setCallback(HTPrintCallback * pCall);
extern HTPrintCallback * HTPrint_getCallback(void);

extern int HTPrint(const char * fmt, ...);
</code>

<code "HTTrace.c">
PRIVATE HTPrintCallback * PHTPrintCallback = NULL;

PUBLIC void HTPrint_setCallback (HTPrintCallback * pCall)
{
PHTPrintCallback = pCall;
}

PUBLIC HTPrintCallback * HTPrint_getCallback (void)
{
return PHTPrintCallback;
}

PUBLIC int HTPrint (const char * fmt, ...)
{
va_list pArgs;
va_start(pArgs, fmt);
if (PHTPrintCallback)
return (*PHTPrintCallback)(fmt, pArgs);
#ifdef WWW_WIN_WINDOW
return (0); /* 对于 Win32 平台,必须由用户设置回调函数。*/
#else
return (vfprintf(stdout, fmt, pArgs));
#endif
}
</code>

===================================================================
??? libwww 是如何处理 HTTRACE 的?
>>> 注意 HTTRACE 与 HTPrint 有本质的不同。HTTRACE 是一个宏,可以被定义为
像 HTPrint 一样的函数 HTTrace,也可以被定义为根本什么也没有。而且 HTTRACE
在 Win32 平台上实际没有实现,虽然理论上并没有障碍。
在其他平台上,函数 HTTrace 的实现与 HTPrint 类似,只是增加了一个
WWW_TraceFlag。以选择不同的 trace 范畴。
<code "HTUtils.h">
#ifdef HTDEBUG
#ifdef WWW_WIN_DLL
extern int * WWW_TraceFlag; /* In DLLs, we need the indirection*/
#define WWWTRACE (*WWW_TraceFlag) /* 但实际上,这个变量既没有被实例化*/
/* 也没有被 export,因此根本没用。*/
#else
extern unsigned int WWW_TraceFlag; /* Global flag for all W3 trace*/
#define WWWTRACE (WWW_TraceFlag) /* 在 HTTrace.c 中被实例化。*/
#endif /* WWW_WIN_DLL */
#else
#define WWWTRACE 0 /* 非调试版屏蔽一切 trace。*/
#endif /* HTDEBUG */

typedef enum _HTTraceFlags {
SHOW_UTIL_TRACE = 0x1,
SHOW_APP_TRACE = 0x2,
....
SHOW_ALL_TRACE = (int) 0xFFFFFFFF
} HTTraceFlags;

#define UTIL_TRACE (WWWTRACE & SHOW_UTIL_TRACE)
#define APP_TRACE (WWWTRACE & SHOW_APP_TRACE)
....
#define ALL_TRACE (WWWTRACE & SHOW_ALL_TRACE)

typedef int HTTraceCallback(const char * fmt, va_list pArgs);
extern void HTTrace_setCallback(HTTraceCallback * pCall);
extern HTTraceCallback * HTTrace_getCallback(void);

#ifdef HTDEBUG
#undef _
#define _ ,
/* 这里的 TYPE 就是 APP_TRACE 之类,即 (WWWTRACE & SHOW_APP_TRACE) 之类。*/
#define HTTRACE(TYPE, FMT) \
do { if (TYPE) HTTrace(FMT); } while (0);
extern int HTTrace(const char * fmt, ...);
#else
#define HTTRACE(TYPE, FMT) /* empty */
#endif /* HTDEBUG */

typedef int HTTraceDataCallback(char * data, size_t len, char * fmt,
va_list pArgs);
extern void HTTraceData_setCallback(HTTraceDataCallback * pCall);
extern HTTraceDataCallback * HTTraceData_getCallback(void);

#ifdef HTDEBUG
#define HTTRACEDATA(DATA, LEN, FMT) HTTraceData((DATA), (LEN), FMT)
extern int HTTraceData(char * data, size_t len, char * fmt, ...);
#else
#define HTTRACEDATA(DATA, LEN, FMT) /* empty */
#endif /* HTDEBUG */
</code>

<code "HTTrace.c">
#if WWWTRACE_MODE == WWWTRACE_FILE
PUBLIC FILE *WWWTrace = NULL;
#endif

#ifndef WWW_WIN_DLL
PUBLIC unsigned int WWW_TraceFlag = 0; /* Global trace flag for ALL W3 code */
#endif

PRIVATE HTTraceCallback * PHTTraceCallback = NULL;
PRIVATE HTTraceDataCallback * PHTTraceDataCallback = NULL;

/* ---------------------------------------------------------------------- */

PUBLIC void HTTrace_setCallback (HTTraceCallback * pCall)
{
PHTTraceCallback = pCall;
}

PUBLIC HTTraceCallback * HTTrace_getCallback (void)
{
return PHTTraceCallback;
}

PUBLIC int HTTrace (const char * fmt, ...)
{
va_list pArgs;
va_start(pArgs, fmt);
if (PHTTraceCallback)
return (*PHTTraceCallback)(fmt, pArgs);
#ifdef WWW_WIN_WINDOW
return (0);
#else
return (vfprintf(stderr, fmt, pArgs));
#endif
}

PUBLIC void HTTraceData_setCallback (HTTraceDataCallback * pCall)
{
PHTTraceDataCallback = pCall;
}

PUBLIC HTTraceDataCallback * HTTraceData_getCallback (void)
{
return PHTTraceDataCallback;
}

PUBLIC int HTTraceData (char * data, size_t len, char * fmt, ...)
{
va_list pArgs;
va_start(pArgs, fmt);
if (PHTTraceDataCallback)
return (*PHTTraceDataCallback)(data, len, fmt, pArgs);
return (0); /* 必须由用户定义回调函数 */
}
</code>

===================================================================
??? libwww 是如何处理 HTArray 的?
>>> HTArray 是个存储 (void *) 的数组,可以自动扩展。
<code "HTArray.h" impl="HTArray.c">
/* 这个结构也可以放在 HTArray.c 当中,做成 private。
相应的几个宏改成函数。参见 HTChunk。
*/
typedef struct {
int size; /* In numbers of elements */
int growby; /* Allocation unit in elements */
int allocated; /* Current size of *data */
void ** data; /* Pointer to malloced area or 0 */
} HTArray;

extern HTArray * HTArray_new (int grow);
extern BOOL HTArray_delete (HTArray * array);
extern BOOL HTArray_clear (HTArray * array);
extern BOOL HTArray_addObject (HTArray * array, void * object);

/* 如果有数据,则返回该数据,并准备访问下一个数据 */
#define HTArray_firstObject(me, dp) \
((me) && ((dp)=(me)->data) ? *(dp)++ : NULL)
#define HTArray_nextObject(me, dp) \
((me) && (dp) ? *(dp)++ : NULL)

typedef int HTComparer (const void * a, const void * b);
extern BOOL HTArray_sort (HTArray * array, HTComparer * comp);
#define HTArray_data(me) ((me) ? (me)->data : NULL)
#define HTArray_size(me) ((me) ? (me)->size : -1)
</code>

===================================================================
??? libwww 如何处理 HTList?
>>> HTList 是个单向链表,其数据域为 void * object。
<code "HTList.h" impl="HTList.c">
typedef struct _HTList HTList;

struct _HTList {
void * object;
HTList * next;
};

extern HTList * HTList_new (void);
extern BOOL HTList_delete (HTList *me);
extern BOOL HTList_addObject (HTList *me, void *newObject);
extern BOOL HTList_appendObject (HTList * me, void * newObject);
extern HTList * HTList_addList (HTList * me, void * newObject);
extern HTList * HTList_appendList (HTList * me, void * newObject);
extern BOOL HTList_removeObject (HTList * me, void * oldObject);
extern BOOL HTList_quickRemoveElement (HTList * me, HTList * last);
extern BOOL HTList_removeObjectAll (HTList * me, void * oldObject);
extern void * HTList_removeLastObject (HTList * me);
extern void * HTList_removeFirstObject (HTList * me);
/* NULL 和 me->next==NULL 都被视为空链表 */
#define HTList_isEmpty(me) (me ? me->next == NULL : YES)
extern int HTList_count (HTList *me);
extern int HTList_indexOf (HTList * me, void * object);
extern int HTList_indexOfElement (HTList * me, HTList * element);
extern void * HTList_objectAt (HTList * me, int position);
extern HTList * HTList_elementOf (HTList * me, void * object, HTList ** pLast);
#define HTList_objectOf(me) (me ? me->object: NULL)
#define HTList_lastObject(me) \
((me) && (me)->next ? (me)->next->object : NULL)
extern void * HTList_firstObject (HTList * me);
#define HTList_nextObject(me) \
((me) && ((me) = (me)->next) ? (me)->object : NULL)
extern BOOL HTList_insertionSort(HTList * list, HTComparer * comp);
#define HTList_free(x) HT_FREE(x)
</code>

===================================================================
??? libwww 如何处理 HTAssocList ?
>>> HTAssocList 就是 HTList,只是其数据域为 HTAssoc。
<code "HTAssoc.h" impl="HTAssoc.c">
typedef HTList HTAssocList;

typedef struct {
char * name;
char * value;
} HTAssoc;

extern HTAssocList * HTAssocList_new (void);
extern BOOL HTAssocList_delete (HTAssocList * alist);
extern BOOL HTAssocList_addObject (
HTAssocList * alist,
const char * name,
const char * value);
extern BOOL HTAssocList_replaceObject (
HTAssocList * list,
const char * name,
const char * value);
extern BOOL HTAssocList_removeObject (
HTAssocList * list,
const char * name);
extern char * HTAssocList_findObject (
HTAssocList * alist,
const char * name);
extern char * HTAssocList_findObjectExact (
HTAssocList * alist,
const char * name);
extern char * HTAssocList_findObjectCaseSensitive (
HTAssocList * list,
const char * name);
extern char * HTAssocList_findObjectCaseSensitiveExact (
HTAssocList * list,
const char * name);
#define HTAssoc_name(me) ((me) ? (me)->name : NULL)
#define HTAssoc_value(me) ((me) ? (me)->value : NULL)
#define HTAssocList_nextObject(me) \
((me) && ((me) = (me)->next) ? (me)->object : NULL)
</code>

===================================================================
??? libwww 如何处理 HTString?
>>>
<code "HTString.h" impl="HTString.c">
#define StrAllocCopy(dest, src) HTSACopy (&(dest), src)
#define StrAllocCat(dest, src) HTSACat (&(dest), src)
extern char * HTSACopy (char **dest, const char *src);
extern char * HTSACat (char **dest, const char *src);
extern char * StrAllocMCopy (char ** dest, ...);
extern char * StrAllocMCat (char ** dest, ...);
extern int strcasecomp (const char *a, const char *b);
extern int strncasecomp (const char *a, const char *b, int n);
extern int tailcomp(const char * s1, const char * s2);
extern int tailcasecomp(const char * s1, const char * s2);
extern char * HTStrMatch (const char * tmpl, const char * name);
extern char * HTStrCaseMatch (const char * tmpl, const char * name);
extern char * HTStrCaseStr (char * s1, char * s2);
extern char * HTStrip (char * s);
</code>

===================================================================
??? libwww 如何处理 HTAtom?
>>> HTAtom 是个散列表,所谓的 atom 在这里就是单元的内存地址。
<code "HTAtom.h" impl="HTAtom.c">
typedef struct _HTAtom HTAtom;
struct _HTAtom {
HTAtom * next;
char * name;
}; /* struct _HTAtom */

extern HTAtom * HTAtom_for (const char * string);
extern HTAtom * HTAtom_caseFor (const char * string);
#define HTAtom_name(a) ((a) ? (a)->name : NULL)
extern HTList * HTAtom_templateMatches (const char * templ);
extern void HTAtom_deleteAll (void);
</code>

<code "HTAtom.c">
PRIVATE HTAtom * hash_table[HT_XL_HASH_SIZE];
PRIVATE BOOL initialised = NO;
/* 在 HTAtom_for 和 HTAtom_casefor 中, 先计算 hash,然后在
hash_table 中 strcmp,strcasecomp,若有就返回该单元的内存地址,
若没有就加入一个新单元,并返回其地址。
*/
</code>

===================================================================
??? libwww 如何处理 HTChunk?
>>>
<code "HTChunk.h" impl="HTChunk.c">
typedef struct _HTChunk HTChunk;

extern HTChunk * HTChunk_new (int growby);
extern void HTChunk_delete (HTChunk * ch);
extern void HTChunk_clear (HTChunk * ch);
extern void HTChunk_ensure (HTChunk * ch, int extra_size);
extern void HTChunk_putc (HTChunk * ch, char c);
extern void HTChunk_puts (HTChunk * ch, const char *str);
extern void HTChunk_putb (HTChunk * ch, const char *block, int len);
extern char * HTChunk_data (HTChunk * ch);
extern int HTChunk_size (HTChunk * ch);
extern BOOL HTChunk_truncate (HTChunk * ch, int size);
extern BOOL HTChunk_setSize (HTChunk * ch, int size);
extern void HTChunk_terminate (HTChunk * ch);
extern HTChunk * HTChunk_fromCString (char * str, int grow);
extern char * HTChunk_toCString (HTChunk * ch);
extern HTChunk * HTChunk_fromBuffer (char * buf, int buflen, int size, int gr
ow);
</code>

<code "HTChunk.c">
struct _HTChunk {
int size; /* In bytes */
int growby; /* Allocation unit in bytes */
int allocated; /* Current size of *data */
char * data; /* Pointer to malloced area or 0 */
};
/* Free a chunk but keep the data
*/
PUBLIC char * HTChunk_toCString (HTChunk * ch)
{
char * ret = 0;
if (ch) {
ret = ch->data;
HT_FREE(ch);
}
return ret;
}
</code>

===================================================================
??? libwww 如何处理 HTMemory?
>>>
<code "HTMemory.h" impl="HTMemory.c">
extern void* HTMemory_malloc(size_t size);
extern void* HTMemory_calloc(size_t count, size_t size);
extern void* HTMemory_realloc(void * ptr, size_t size);
extern void HTMemory_free(void* ptr);

#define HT_MALLOC(size) HTMemory_malloc((size))
#define HT_CALLOC(count, size) HTMemory_calloc((count), (size))
#define HT_REALLOC(ptr, size) HTMemory_realloc((ptr), (size))
#define HT_FREE(pointer) {HTMemory_free((pointer));((pointer))=NULL;}

/* 可以注册多个内存清理函数,出现短缺就进行清理 */
typedef void HTMemoryCallback(size_t size);
extern BOOL HTMemoryCall_add (HTMemoryCallback * cbf);
extern BOOL HTMemoryCall_delete (HTMemoryCallback * cbf);
extern BOOL HTMemoryCall_deleteAll (void);

/* 可以设置一个内存不足无法继续时的必要清理函数,清理之后就 abort() */
typedef void HTMemory_exitCallback(char *name, char *file, unsigned long line);
extern void HTMemory_setExit(HTMemory_exitCallback * pExit);
extern HTMemory_exitCallback * HTMemory_exit(void);

#define outofmem(file, name) HT_OUTOFMEM(name)
#define HT_OUTOFMEM(name) HTMemory_outofmem((name), __FILE__, __LINE__)
extern void HTMemory_outofmem(char * name, char * file, unsigned long line);
</code>

<code "HTMemory.c">
PRIVATE HTList * HTMemCall = NULL; /* List of memory freers */
PRIVATE HTMemory_exitCallback * PExit = NULL; /* panic and exit function */
PRIVATE size_t LastAllocSize = 0; /* size of last allocation */

PUBLIC void * HTMemory_malloc (size_t size)
{
void * ptr;
ptr = malloc(LastAllocSize = size);
if (ptr != NULL) return ptr;
/* 出现短缺 */
if (HTMemCall) {
HTMemoryCallback * pres;
while ((pres = (HTMemoryCallback *) HTList_nextObject(HTMemCall))) {
HTTRACE(MEM_TRACE, "Mem Calling. %p (size %d)\n" _ (void*)pres _
size);
(*pres)(size); /* 清理 */
if ((ptr = malloc(size)) != NULL) return ptr; /* 重试 */
}
}
HTTRACE(MEM_TRACE, "Memory.... Couldn't allocate %d bytes\n" _ size);
return NULL;
}

PUBLIC void HTMemory_outofmem (char * name, char * file, unsigned long line)
{
if (PExit) /* 如果注册了必要清理函数,就进行必要清理 */
(*PExit)(name, file, line);
HTTRACE(ALL_TRACE, "%s:%ld failed allocation for \"%s\" (%ld bytes).\nPro
gram aborted.\n" _
file _ line _ name _ LastAllocSize);
abort();
}
</code>

===================================================================
??? libwww 如何处理 HTUU?
>>>
<code "HTUU.h" impl="HTUU.c">
extern int HTUU_encode (unsigned char * bufin, unsigned int nbytes,
char * bufcoded);
extern int HTUU_decode (char * bufcoded, unsigned char * bufplain,
int outbufsize);
</code>

===================================================================
??? WWWCore.h 包括哪些内容?
>>>
<code "WWWCore.h">
#include "wwwsys.h"
#include "HTLib.h"
#include "HTReq.h"
#include "HTMethod.h"
#include "HTAnchor.h"
#include "HTLink.h"
#include "HTParse.h"
#include "HTEscape.h"
#include "HTUTree.h"
#include "HTWWWStr.h"
#include "HTUser.h"
#include "HTEvent.h"
#include "HTMemLog.h"
#include "HTError.h"
#include "HTAlert.h"
#include "HTFormat.h"
#include "HTStream.h"
#include "HTStruct.h"
#include "HTNoFree.h"
#include "HTIOStream.h"
#include "HTDNS.h"
#include "HTHost.h"
#include "HTNet.h"
#include "HTInet.h"
#include "HTTrans.h"
#include "HTProt.h"
</code>

===================================================================
??? libwww 如何处理 HTInet?
>>> 获取网络配置的一些函数,在 HTUserProfile 中有运用。
<code "HTInet.h" impl="HTInet.c">
extern char * HTErrnoString (int errnum);
extern int HTInetStatus (int errnum, char * where);
extern unsigned int HTCardinal (int * pstatus,
char ** pp,
unsigned int max_value);
extern const char * HTInetString (struct sockaddr_in * sin);
extern int HTParseInet (HTHost * host, char * hostname, HTRequest * request);
extern time_t HTGetTimeZoneOffset (void);
extern ms_t HTGetTimeInMillis (void);
extern char * HTGetHostName (void);
extern char * HTGetMailAddress (void);
extern char * HTGetNewsServer (void);
extern char * HTGetTmpFileName (const char * dir);
#ifdef WWWLIB_SIG
extern void HTSetSignal (void);
#endif
</code>

===================================================================
??? libwww 如何处理 HTUserProfile?
>>>
<code "HTUser.h" impl="HTUser.c">
typedef struct _HTUserProfile HTUserProfile;

extern HTUserProfile * HTUserProfile_new (const char * name, void * context);
extern BOOL HTUserProfile_localize (HTUserProfile * up); /* 填缺省值 */
extern BOOL HTUserProfile_delete (HTUserProfile * up);
</code>

<code "HTUser.c">
struct _HTUserProfile {
char * user; /* name, set in HTUserProfile_new(name, context) */
/* 但是这个字段实际上没有任何用处! */

char * fqdn; /* Fully qualified domain name */
extern char * HTUserProfile_fqdn (HTUserProfile * up);
extern BOOL HTUserProfile_setFqdn (HTUserProfile * up, const char * fqdn);

char * email; /* Email address of current user */
extern char * HTUserProfile_email (HTUserProfile * up);
extern BOOL HTUserProfile_setEmail (HTUserProfile * up, const char * email);

char * news; /* The news server to use */
extern char * HTUserProfile_news (HTUserProfile * host);
extern BOOL HTUserProfile_setNews (HTUserProfile * host, const char * news);

char * tmp; /* Location for temporary files */
extern char * HTUserProfile_tmp (HTUserProfile * host);
extern BOOL HTUserProfile_setTmp (HTUserProfile * host, const char * tmp);

time_t timezone; /* Time zone in seconds */
extern time_t HTUserProfile_timezone (HTUserProfile * up);
extern BOOL HTUserProfile_setTimezone (HTUserProfile * up, time_t timezone);

void * context; /* Application specific */
extern void * HTUserProfile_context (HTUserProfile * up);
extern BOOL HTUserProfile_setContext (HTUserProfile * up, void * context);
};
</code>

===================================================================
??? libwww 如何处理 HTLib?
>>> 注意,实际上 HTLibInit 干的事儿很少,HTLibTerminate 倒是干的很多。
<code "HTLib.h" impl="HTLib.c">
extern BOOL HTLibInit (const char * AppName, const char * AppVersion);
extern BOOL HTLibTerminate (void);
</code>

<code "HTLib.c">
PRIVATE char * HTAppName = NULL; /* Application name: please supply */
extern const char * HTLib_appName (void);
extern BOOL HTLib_setAppName (const char * name);

PRIVATE char * HTAppVersion = NULL; /* Application version: please supply */
extern const char * HTLib_appVersion (void);
extern BOOL HTLib_setAppVersion (const char * version);

PRIVATE char * HTLibName = "libwww";
extern const char * HTLib_name (void);

PRIVATE char * HTLibVersion = W3C_VERSION;
extern const char * HTLib_version (void);

PRIVATE BOOL HTSecure = NO; /* Can we access local file system? */
extern BOOL HTLib_secure (void);
extern void HTLib_setSecure (BOOL mode);

PRIVATE BOOL initialized = NO;
extern BOOL HTLib_isInitialized (void);

PRIVATE HTUserProfile * UserProfile = NULL; /* Default user profile */
extern HTUserProfile * HTLib_userProfile (void);
extern BOOL HTLib_setUserProfile (HTUserProfile * up);

PUBLIC BOOL HTLibInit (const char * AppName, const char * AppVersion)
{
HTTRACE(CORE_TRACE, "WWWLibInit.. INITIALIZING LIBRARY OF COMMON CODE\n");

/* Set the application name and version */
HTLib_setAppName(AppName);
HTLib_setAppVersion(AppVersion);

/* Initialize the timezone */
#ifdef HAVE_TZSET
tzset();
#endif

/* Create a default user profile and initialize it */
UserProfile = HTUserProfile_new(HT_DEFAULT_USER, NULL);
HTUserProfile_localize(UserProfile);

#ifdef WWWLIB_SIG
/* On Solaris (and others?) we get a BROKEN PIPE signal when connecting
** to a port where we should get `connection refused'. We ignore this
** using the following function call
*/
HTSetSignal(); /* Set signals in library */
#endif

initialized = YES;
return YES;
}

PUBLIC BOOL HTLibTerminate (void)
{
HTTRACE(CORE_TRACE, "WWWLibTerm.. Cleaning up LIBRARY OF COMMON CODE\n");

/* 这八个单元是有全局结构的,应该对它们的初始化予以关注 */
HTNet_killAll();
HTHost_deleteAll(); /* Delete remaining hosts */
HTChannel_deleteAll(); /* Delete remaining channels */
HTAtom_deleteAll(); /* Remove the atoms */
HTDNS_deleteAll(); /* Remove the DNS host cache */
HTAnchor_deleteAll(NULL); /* Delete anchors and drop hyperdocs */
HTProtocol_deleteAll(); /* Remove bindings between access and protocols */
HTUTree_deleteAll(); /* Delete all URL Trees */

HT_FREE(HTAppName); /* Freed thanks to Wade Ogden <[email protected]> */
HT_FREE(HTAppVersion);
HTUserProfile_delete(UserProfile); /* Free our default User profile */

initialized = NO;
return YES;
}
</code>

===================================================================
??? libwww 如何处理 HTRequest?
>>> HTRequest 涉及三个文件,HTReq.h HTReqMan.h HTReqMan.c。
<code "HTReqMan.h" impl="HTReqMan.c">
struct _HTRequest {
BOOL internal; /* Does the app knows about this one? */
extern BOOL HTRequest_setInternal (HTRequest * request, BOOL mode);
extern BOOL HTRequest_internal (HTRequest * request);

time_t date; /* Time stamp when the request was issued */
extern time_t HTRequest_date (HTRequest * request);
extern BOOL HTRequest_setDate (HTRequest * request, time_t date);

HTMethod method;
extern void HTRequest_setMethod (HTRequest *request, HTMethod method);
extern HTMethod HTRequest_method (HTRequest *request);

BOOL flush; /* Should we flush immediately */
extern BOOL HTRequest_setFlush (HTRequest * me, BOOL mode);
extern BOOL HTRequest_flush (HTRequest * me);

HTPriority priority; /* Priority for this request */
extern HTPriority HTRequest_priority (HTRequest * request);
extern BOOL HTRequest_setPriority (HTRequest * request, HTPriority priority);

#ifdef HT_EXT
char * messageBody;
extern BOOL HTRequest_setMessageBody (HTRequest * request, const char * body);
extern BOOL HTRequest_deleteMessageBody (HTRequest * request);
extern char * HTRequest_messageBody (HTRequest * request);

long int messageBodyLength;
extern BOOL HTRequest_setMessageBodyLength (HTRequest * request, long int len
gth);
extern long int HTRequest_messageBodyLength (HTRequest * request);

HTFormat messageBodyFormat;
extern BOOL HTRequest_setMessageBodyFormat (HTRequest * request, HTFormat for
mat);
extern HTFormat HTRequest_messageBodyFormat (HTRequest * request);
#endif
HTUserProfile * userprofile;
extern BOOL HTRequest_setUserProfile (HTRequest * request, HTUserProfile * up);
extern HTUserProfile * HTRequest_userProfile (HTRequest * request);

HTNet * net; /* Information about socket etc. */
extern HTNet * HTRequest_net (HTRequest * request);
extern BOOL HTRequest_setNet (HTRequest * request, HTNet * net);

HTResponse * response;
extern HTResponse * HTRequest_response (HTRequest * request);
extern BOOL HTRequest_setResponse (HTRequest * request, HTResponse * response);

HTList * error_stack; /* List of errors */
extern HTList * HTRequest_error (HTRequest * request);
extern void HTRequest_setError (HTRequest * request, HTList * list);
extern void HTRequest_deleteAllErrors (HTRequest * request);
extern BOOL HTRequest_addError (HTRequest * request,
HTSeverity severity,
BOOL ignore,
int element,
void * par,
unsigned int length,
char * where);
extern BOOL HTRequest_addSystemError (HTRequest * request,
HTSeverity severity,
int errornumber,
BOOL ignore,
char * syscall);

int retrys; /* Number of automatic reloads */
extern int HTRequest_retrys (HTRequest * request);
extern BOOL HTRequest_addRetry (HTRequest * request); /* increase by 1 */
extern BOOL HTRequest_doRetry (HTRequest *request); /* if needed */

int max_forwards;
extern BOOL HTRequest_setMaxForwards (HTRequest * request, int maxforwards);
extern int HTRequest_maxForwards (HTRequest * request);

int AAretrys; /* Number of authentication retries */
extern int HTRequest_AAretrys (HTRequest * request);
extern BOOL HTRequest_addAARetry (HTRequest * request);

BOOL preemptive;
extern void HTRequest_setPreemptive (HTRequest *request, BOOL mode);
extern BOOL HTRequest_preemptive (HTRequest *request);

BOOL ContentNegotiation;
extern void HTRequest_setNegotiation (HTRequest *request, BOOL mode);
extern BOOL HTRequest_negotiation (HTRequest *request);

HTPreconditions preconditions;
extern void HTRequest_setPreconditions (HTRequest * me, HTPreconditions mode);
extern HTPreconditions HTRequest_preconditions (HTRequest * me);

HTGnHd GenMask;
extern void HTRequest_setGnHd (HTRequest *request, HTGnHd gnhd);
extern void HTRequest_addGnHd (HTRequest *request, HTGnHd gnhd);
extern HTGnHd HTRequest_gnHd (HTRequest *request);

HTRsHd ResponseMask;
extern void HTRequest_setRsHd (HTRequest * request, HTRsHd rshd);
extern void HTRequest_addRsHd (HTRequest * request, HTRsHd rshd);
extern HTRsHd HTRequest_rsHd (HTRequest * request);

HTRqHd RequestMask;
extern void HTRequest_setRqHd (HTRequest *request, HTRqHd rqhd);
extern void HTRequest_addRqHd (HTRequest *request, HTRqHd rqhd);
extern HTRqHd HTRequest_rqHd (HTRequest *request);

HTEnHd EntityMask;
extern void HTRequest_setEnHd (HTRequest *request, HTEnHd enhd);
extern void HTRequest_addEnHd (HTRequest *request, HTEnHd enhd);
extern HTEnHd HTRequest_enHd (HTRequest *request);

HTMIMEParseSet * parseSet;
BOOL pars_local;
extern void HTRequest_setMIMEParseSet (HTRequest *request,
HTMIMEParseSet * parseSet, BOOL local);
extern HTMIMEParseSet * HTRequest_MIMEParseSet (HTRequest *request,
BOOL * pLocal);

HTList * conversions;
BOOL conv_local;
extern void HTRequest_setConversion (HTRequest *request, HTList *type, BOOL o
verride);
extern HTList * HTRequest_conversion (HTRequest *request);

HTList * encodings;
BOOL enc_local;
extern void HTRequest_setEncoding (HTRequest *request, HTList *enc, BOOL over
ride);
extern HTList * HTRequest_encoding (HTRequest *request);

HTList * tes;
BOOL te_local;
extern void HTRequest_setTransfer (HTRequest *request, HTList *te, BOOL overr
ide);
extern HTList * HTRequest_transfer (HTRequest *request);

HTList * languages;
BOOL lang_local;
extern void HTRequest_setLanguage (HTRequest *request, HTList *lang, BOOL ove
rride);
extern HTList * HTRequest_language (HTRequest *request);

HTList * charsets;
BOOL char_local;
extern void HTRequest_setCharset (HTRequest *request, HTList *charset, BOOL o
verride);
extern HTList * HTRequest_charset (HTRequest *request);

HTList * befores;
BOOL befores_local;
extern BOOL HTRequest_addBefore (HTRequest * request, HTNetBefore * filter,
const char * tmplate, void * param,
HTFilterOrder order, BOOL override);
extern HTList * HTRequest_before (HTRequest * request, BOOL * override);
extern BOOL HTRequest_deleteBefore (HTRequest * request, HTNetBefore * filter);
extern BOOL HTRequest_deleteBeforeAll (HTRequest * request);

HTList * afters;
BOOL afters_local;
extern BOOL HTRequest_addAfter (HTRequest * request, HTNetAfter * filter,
const char * tmplate, void * param,
int status, HTFilterOrder order,
BOOL override);
extern HTList * HTRequest_after (HTRequest * request, BOOL * override);
extern BOOL HTRequest_deleteAfter (HTRequest * request, HTNetAfter * filter);
extern BOOL HTRequest_deleteAfterStatus (HTRequest * request, int status);
extern BOOL HTRequest_deleteAfterAll (HTRequest * request);

char * proxy;
extern BOOL HTRequest_setProxy (HTRequest * request, const char * proxy);
extern char * HTRequest_proxy (HTRequest * request);
extern BOOL HTRequest_deleteProxy (HTRequest * request); /* free(me->proxy) */

BOOL full_uri;
extern void HTRequest_setFullURI (HTRequest *request, BOOL mode);
extern BOOL HTRequest_fullURI (HTRequest *request);

HTReload reload;
extern void HTRequest_setReloadMode (HTRequest *request, HTReload mode);
extern HTReload HTRequest_reloadMode (HTRequest *request);

HTAssocList * cache_control;
extern BOOL HTRequest_addCacheControl (HTRequest * request,
char * token, char *value);
extern BOOL HTRequest_deleteCacheControlAll (HTRequest * request);
extern HTAssocList * HTRequest_cacheControl (HTRequest * request);

char * default_put_name;
extern char * HTRequest_defaultPutName (HTRequest * me);
extern BOOL HTRequest_setDefaultPutName (HTRequest * me, char * name);
extern BOOL HTRequest_deleteDefaultPutName (HTRequest * me);

HTAssocList * byte_ranges;
extern BOOL HTRequest_addRange (HTRequest * request,
char * unit, char * range);
extern BOOL HTRequest_deleteRangeAll (HTRequest * request);
extern HTAssocList * HTRequest_range (HTRequest * request);

HTAssocList * connection;
extern BOOL HTRequest_addConnection (HTRequest * request,
char * token, char * value);
extern BOOL HTRequest_deleteConnection (HTRequest * request);
extern HTAssocList * HTRequest_connection (HTRequest * request);

HTAssocList * expect;
extern BOOL HTRequest_addExpect (HTRequest * me,
char * token, char * value);
extern BOOL HTRequest_deleteExpect (HTRequest * me);
extern HTAssocList * HTRequest_expect (HTRequest * me);

char * realm; /* Current realm */
extern BOOL HTRequest_setRealm (HTRequest * request, char * realm);
extern const char * HTRequest_realm (HTRequest * request);
extern BOOL HTRequest_deleteRealm (HTRequest * me);

HTAssocList * credentials; /* Credentials received by server */
extern BOOL HTRequest_addCredentials (HTRequest * request,
char * token, char * value);
extern BOOL HTRequest_deleteCredentialsAll (HTRequest * request);
extern HTAssocList * HTRequest_credentials (HTRequest * request);

HTAssocList * extra_headers;
extern BOOL HTRequest_addExtraHeader (HTRequest * request,
char * token, char * value);
extern HTAssocList * HTRequest_extraHeader (HTRequest * request);
extern BOOL HTRequest_deleteExtraHeaderAll (HTRequest * request);

HTList * generators;
BOOL gens_local;
extern void HTRequest_setGenerator (HTRequest *request, HTList *gens,
BOOL override);
extern HTList * HTRequest_generator (HTRequest *request, BOOL *override);

HTAssocList * mandatory; /* 有实现而无声明 */
PUBLIC BOOL HTRequest_addMandatory (HTRequest * me,
char * token, char * value)
PUBLIC HTAssocList * HTRequest_mandatory (HTRequest * me)
PUBLIC BOOL HTRequest_deleteMandatoryAll (HTRequest * me)

HTAssocList * optional; /* 有实现而无声明 */
PUBLIC BOOL HTRequest_addOptional (HTRequest * me,
char * token, char * value)
PUBLIC HTAssocList * HTRequest_optional (HTRequest * me)
PUBLIC BOOL HTRequest_deleteOptionalAll (HTRequest * me)

HTParentAnchor * anchor; /* The Client anchor for this request */
HTChildAnchor * childAnchor; /* For element within the object */
extern void HTRequest_setAnchor (HTRequest *request, HTAnchor *anchor);
extern HTParentAnchor * HTRequest_anchor (HTRequest *request);
extern HTChildAnchor * HTRequest_childAnchor (HTRequest * request);

HTParentAnchor * parentAnchor; /* For referer field */
extern void HTRequest_setParent (HTRequest *request, HTParentAnchor *parent);
extern HTParentAnchor * HTRequest_parent (HTRequest *request);

HTStream * output_stream;
HTStream * orig_output_stream;
extern void HTRequest_setOutputStream (HTRequest *request, HTStream *output);
extern HTStream *HTRequest_outputStream (HTRequest *request);

HTFormat output_format;
extern void HTRequest_setOutputFormat (HTRequest *request, HTFormat fo

你可能感兴趣的:(C++,c,.net,linux,C#)