`

[转]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入门教程
丁建华 <jhding@tom.com> 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 <wade@ebt.com> */
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 format);
extern HTFormat HTRequest_outputFormat (HTRequest *request);

BOOL connected;
extern void HTRequest_setOutputConnected (HTRequest * request, BOOL mode);
extern BOOL HTRequest_outputConnected (HTRequest * request);

HTStream * debug_stream;
HTStream * orig_debug_stream;
extern void HTRequest_setDebugStream (HTRequest *request, HTStream *debug);
extern HTStream *HTRequest_debugStream (HTRequest *request);

HTFormat debug_format;
extern void HTRequest_setDebugFormat (HTRequest *request, HTFormat format);
extern HTFormat HTRequest_debugFormat (HTRequest *request);

HTStream * input_stream;
extern void HTRequest_setInputStream (HTRequest * request, HTStream * input);
extern HTStream *HTRequest_inputStream (HTRequest * request);

HTFormat input_format;
HTPostCallback * PostCallback;
typedef int HTPostCallback (HTRequest * request, HTStream * target);
extern void HTRequest_setPostCallback (HTRequest * request, HTPostCallback *
cbf);
extern HTPostCallback * HTRequest_postCallback (HTRequest * request);

HTRequestCallback * callback;
typedef int HTRequestCallback (HTRequest * request, void *param);
extern void HTRequest_setCallback (HTRequest *request, HTRequestCallback *cb);
extern HTRequestCallback *HTRequest_callback (HTRequest *request);

void * context;
extern void HTRequest_setContext (HTRequest *request, void *context);
extern void *HTRequest_context (HTRequest *request);

HTRequest * source; /* Source for request or itself */
extern BOOL HTRequest_isPostWeb (HTRequest * request);
extern BOOL HTRequest_setSource (HTRequest * request, HTRequest * source);
extern HTRequest * HTRequest_source (HTRequest * request);

HTParentAnchor * source_anchor; /* Source anchor or itself */
extern BOOL HTRequest_setEntityAnchor (HTRequest * request, HTParentAnchor *
anchor);
extern HTParentAnchor * HTRequest_entityAnchor (HTRequest * request);

HTRequest * mainDestination; /* For the typical case */
HTList * destinations; /* List of related requests */
int destRequests; /* Number of destination requests */
int destStreams; /* Number of destination streams */
PUBLIC BOOL HTRequest_addDestination (HTRequest * src, HTRequest * dest)
PUBLIC BOOL HTRequest_removeDestination (HTRequest * dest)
PUBLIC BOOL HTRequest_destinationsReady (HTRequest * me)
PUBLIC BOOL HTRequest_linkDestination (HTRequest *dest)
PUBLIC BOOL HTRequest_unlinkDestination (HTRequest *dest)
PUBLIC BOOL HTRequest_removePostWeb (HTRequest *me)
PUBLIC BOOL HTRequest_killPostWeb (HTRequest *me)
};

extern BOOL HTRequest_addDestination (HTRequest * src, HTRequest * dest);
extern BOOL HTRequest_removeDestination (HTRequest * dest);
extern BOOL HTRequest_destinationsReady (HTRequest * me);

extern BOOL HTRequest_linkDestination (HTRequest * dest);
extern BOOL HTRequest_unlinkDestination (HTRequest * dest);

extern BOOL HTRequest_removePostWeb (HTRequest * me);
extern BOOL HTRequest_killPostWeb (HTRequest * me);

#define HTRequest_mainDestination(me) /
((me) && (me)->source ? (me)->source->mainDestination : NULL)
#define HTRequest_isDestination(me) /
((me) && (me)->source && (me) != (me)->source)
#define HTRequest_isMainDestination(me) /
((me) && (me)->source && /
(me) == (me)->source->mainDestination)
#define HTRequest_isSource(me) /
((me) && (me)->source && (me) == (me)->source)
</code>

<code "HTReq.h" impl="HTReqMan.c">
typedef long HTRequestID;
typedef struct _HTRequest HTRequest;
extern HTRequest * HTRequest_new (void);
extern BOOL HTRequest_clear (HTRequest * me);
extern HTRequest * HTRequest_dup (HTRequest * src);
extern HTRequest * HTRequest_dupInternal (HTRequest * src);
extern void HTRequest_delete (HTRequest * request);

/* 调用 HTNet_newClient(req) / HTNet_newServer(req) */
extern BOOL HTLoad (HTRequest * request, BOOL recursive);
extern BOOL HTServe(HTRequest * request, BOOL recursive);

/* 调用 HTHost_forceFlush(host) */
extern int HTRequest_forceFlush (HTRequest * request);

/* 调用相应的 HTNet_* 函数 */
extern BOOL HTRequest_kill(HTRequest * request);
extern long HTRequest_bodyRead (HTRequest * request);
extern long HTRequest_bodyWritten (HTRequest * request);
extern long HTRequest_bytesRead (HTRequest * request);
extern long HTRequest_bytesWritten (HTRequest * request);
</code>

<code "HTReqMan.c">
PRIVATE int HTMaxRetry = HT_MAX_RELOADS;
extern BOOL HTRequest_setMaxRetry (int newmax);
extern int HTRequest_maxRetry (void);

struct _HTStream {
HTStreamClass * isa;
/* ... */
};

PUBLIC BOOL HTLoad (HTRequest * me, BOOL recursive)
{
if (!me || !me->anchor) {
HTTRACE(CORE_TRACE, "Load Start.. Bad argument/n");
return NO;
}

/* Make sure that we don't carry over any old physical address */
if (!recursive) HTAnchor_clearPhysical(me->anchor);

/* Set the default method if not already done */
if (me->method == METHOD_INVALID) me->method = METHOD_GET;

/* Should we keep the error stack or not? */
if (!recursive && me->error_stack) {
HTError_deleteAll(me->error_stack);
me->error_stack = NULL;
}

/* Delete any old Response Object */
if (me->response) {
HTResponse_delete(me->response);
me->response = NULL;
}

/*
** We set the start point of handling a request to here.
** This time will be used by the cache
*/
HTRequest_setDate(me, time(NULL));

/* Now start the Net Manager */
return HTNet_newClient(me);
}

PUBLIC BOOL HTServe (HTRequest * me, BOOL recursive)
{
if (!me || !me->anchor) {
HTTRACE(CORE_TRACE, "Serve Start. Bad argument/n");
return NO;
}

/* Make sure that we don't carry over any old physical address */
if (!recursive) HTAnchor_clearPhysical(me->anchor);

/* Should we keep the error stack or not? */
if (!recursive && me->error_stack) {
HTError_deleteAll(me->error_stack);
me->error_stack = NULL;
}

/* Delete any old Response Object */
if (me->response) {
HTResponse_delete(me->response);
me->response = NULL;
}

/* Now start the Net Manager */
return HTNet_newServer(me);
}
</code>

===================================================================
??? libwww 如何处理 HTNet?
>>> 有三个文件:HTNetMan.h HTNet.h HTNet.c
<code "HTNetMan.h">
struct _HTNet {
int hash; /* Hash value */

/* Link to other objects */
HTRequest * request; /* Link to request object */
extern BOOL HTNet_setRequest (HTNet * net, HTRequest * request);
extern HTRequest * HTNet_request (HTNet * net);

HTHost * host; /* What we know about the remote host */
extern BOOL HTNet_setHost (HTNet * net, HTHost * host);
extern HTHost * HTNet_host (HTNet * net);
extern BOOL HTNet_setSocket (HTNet * net, SOCKET sockfd);
extern SOCKET HTNet_socket (HTNet * net); /* net->host->channel's socket */
extern BOOL HTNet_persistent (HTNet * net); /* host's isPersistent */
extern BOOL HTNet_setPersistent (HTNet * net,
BOOL persistent,
HTTransportMode mode);
extern BOOL HTNet_setChannel (HTNet * net, HTChannel * channel);
extern HTChannel * HTNet_channel (HTNet * net);
extern BOOL HTNet_setDns (HTNet * net, HTdns * dns); /* host->dns */
extern HTdns * HTNet_dns (HTNet * net);

HTProtocol * protocol; /* Protocol to this request */
extern BOOL HTNet_setProtocol (HTNet * net, HTProtocol * protocol);
extern HTProtocol * HTNet_protocol (HTNet * net);

HTTransport * transport; /* Transport for this request */
extern BOOL HTNet_setTransport (HTNet * net, HTTransport * tp);
extern HTTransport * HTNet_transport (HTNet * net);

int session;

/* For progress notifications */
BOOL countRawBytes; /* If we should count raw bytes */
extern BOOL HTNet_setRawBytesCount (HTNet * net, BOOL mode);
extern BOOL HTNet_rawBytesCount (HTNet * net);

long bytesRead; /* Bytes in body */
long headerBytesRead; /* Bytes read in header */
long bytesWritten; /* Bytes written to network */
long headerBytesWritten; /* Bytes written in header */

time_t connecttime; /* Used on multihomed hosts */
BOOL preemptive; /* Eff result from Request and Protocol */
extern BOOL HTNet_preemptive (HTNet * net);

HTEvent event; /* NOTE: This is *NOT* a pointer */
extern HTEvent * HTNet_event (HTNet * net);
extern BOOL HTNet_setEventParam (HTNet * net, void * eventParam);
extern void * HTNet_eventParam (HTNet * net);
extern BOOL HTNet_setEventCallback(HTNet * net, HTEventCallback * cbf);
extern HTEventCallback * HTNet_eventCallback(HTNet * net);
extern HTPriority HTNet_priority (HTNet * net); /* event.priority */
extern BOOL HTNet_setPriority (HTNet * net, HTPriority priority);
extern BOOL HTNet_execute (HTNet * net, HTEventType type);
/* (*(net->event.cbf))(HTNet_socket(net), net->event.param, type); */
extern BOOL HTNet_start (HTNet * net);
/* (*(me->event.cbf))(HTNet_socket(me), me->event.param, HTEvent_BEGIN); */

HTStream * readStream; /* host's input stream puts data here */
extern HTStream * HTNet_readStream(HTNet * net);
extern BOOL HTNet_setReadStream (HTNet * net, HTStream * stream);

/* User specific stuff */
void * context; /* Protocol Specific context */
extern BOOL HTNet_setContext (HTNet * net, void * context);
extern void * HTNet_context (HTNet * net);

/* Eric's sleezoid cheat - should go to extra pipeline object */
HTEventType registeredFor;
};

extern SOCKET HTNet_socket(HTNet * me);

#define HTNet_setBytesRead(me,l) ((me) ? (me->bytesRead=(l)) : -1)
#define HTNet_bytesRead(me) ((me) ? (me)->bytesRead : -1)
#define HTNet_addBytesRead(me,l) ((me) ? (me->bytesRead+=(l)) : -1)

#define HTNet_setHeaderBytesRead(me,l) ((me) ? (me->headerBytesRead=(l)) :-1)
#define HTNet_headerBytesRead(me) ((me) ? (me)->headerBytesRead : -1)
#define HTNet_addHeaderBytesRead(me,l) ((me) ? (me->headerBytesRead+=(l))
: -1)

#define HTNet_setBytesWritten(me,l) ((me) ? (me->bytesWritten=(l)) :-1)
#define HTNet_bytesWritten(me) ((me) ? (me)->bytesWritten : -1)
#define HTNet_addBytesWritten(me,l) ((me) ? (me->bytesWritten+=(l)) : -1)

#define HTNet_setHeaderBytesWritten(me,l) ((me) ? (me->headerBytesWritten=(l)
) :-1)
#define HTNet_headerBytesWritten(me) ((me) ? /
((me)->headerBytesWritten==0 ? /
HTNet_bytesWritten(me) : /
(me)->headerBytesWritten) : -1)
#define HTNet_addHeaderBytesWritten(me,l) ((me) ? (me->headerBytesWritten+=(l
)) : -1)

extern BOOL HTNet_setEventParam(HTNet * net, void * eventParam);
extern void* HTNet_eventParam(HTNet * net);
extern BOOL HTNet_setEventCallback(HTNet * net, HTEventCallback * cbf);
extern HTEventCallback * HTNet_eventCallback(HTNet * net);
extern BOOL HTNet_setEventPriority(HTNet * net, HTPriority priority);
extern HTPriority HTNet_eventPriority(HTNet * net);
</code>

<code "HTNet.h" impl="HTNet.c">
typedef struct _HTNet HTNet;

typedef int HTNetBefore (HTRequest * request, void * param, int mode);
extern BOOL HTNetCall_addBefore (HTList * list, HTNetBefore * before,
const char * tmplate, void * param,
HTFilterOrder order);
extern BOOL HTNetCall_deleteBefore (HTList * list, HTNetBefore * before);
extern BOOL HTNetCall_deleteBeforeAll (HTList * list);
extern int HTNetCall_executeBefore (HTList * list, HTRequest * request);

typedef int HTNetAfter (HTRequest * request, HTResponse * response,
void * param, int status);
extern BOOL HTNetCall_addAfter (HTList * list, HTNetAfter * after,
const char * tmplate, void * param,
int status, HTFilterOrder order);
extern BOOL HTNetCall_deleteAfter (HTList * list, HTNetAfter * after);
extern BOOL HTNetCall_deleteAfterStatus (HTList * list, int status);
extern BOOL HTNetCall_deleteAfterAll (HTList * list);
extern int HTNetCall_executeAfter (HTList * list, HTRequest * request,
int status);

extern HTList *HTNet_activeQueue (void);
extern BOOL HTNet_idle (void);
extern BOOL HTNet_idle (void);


extern HTList *HTNet_pendingQueue (void);

extern BOOL HTNet_newClient (HTRequest * request);
extern BOOL HTNet_newServer (HTRequest * request);
extern HTNet * HTNet_new (HTHost * host);
extern HTNet * HTNet_dup (HTNet * src);
extern BOOL HTNet_deleteDup (HTNet * dup);

extern BOOL HTNet_delete (HTNet * me, int status);
extern BOOL HTNet_wait (HTNet *net);

extern BOOL HTNet_killPipe (HTNet * net);
extern BOOL HTNet_kill (HTNet * me);
extern BOOL HTNet_killAll (void);

extern HTOutputStream * HTNet_getOutput (HTNet * me, void * param, int mode);
</code>

<code "HTNet.c">
typedef struct _BeforeFilter {
HTNetBefore * before; /* Filter function */
char * tmplate; /* URL template for when to call filter */
int order; /* Relative execution order */
void * param; /* Local context */
} BeforeFilter;

typedef struct _AfterFilter {
HTNetAfter * after; /* Filter function */
char * tmplate; /* URL template for when to call filter */
int order; /* Relative execution order */
void * param; /* Local context */
int status; /* Status of load for when to call filter */
} AfterFilter;

struct _HTStream {
const HTStreamClass * isa;
/* ... */
};

struct _HTInputStream {
const HTInputStreamClass * isa;
/* ... */
};

typedef struct _HTFilterEvent {
HTRequest * request;
int status;
HTTimer * timer;
} HTFilterEvent;

PRIVATE HTList * HTBefore = NULL; /* List of global BEFORE filters */
extern BOOL HTNet_setBefore (HTList * list);
extern HTList * HTNet_before (void);
extern BOOL HTNet_addBefore (HTNetBefore * before, const char * tmplate,
void * param, HTFilterOrder order);
extern BOOL HTNet_deleteBefore (HTNetBefore * before);
extern int HTNet_executeBeforeAll (HTRequest * request);

PRIVATE HTList * HTAfter = NULL; /* List of global AFTER filters */
extern BOOL HTNet_setAfter (HTList * list);
extern HTList * HTNet_after (void);
extern BOOL HTNet_addAfter (HTNetAfter * after, const char * tmplate,
void * param, int status,
HTFilterOrder order);
extern BOOL HTNet_deleteAfter (HTNetAfter * after);
extern BOOL HTNet_deleteAfterStatus (int status);
extern int HTNet_executeAfterAll (HTRequest * request, int status);

PRIVATE int MaxActive = HT_MAX_SOCKETS; /* Max active requests */
extern BOOL HTNet_setMaxSocket (int newmax);
extern int HTNet_maxSocket (void);

PRIVATE int Active = 0; /* Counts open sockets */
extern void HTNet_increaseSocket (void);
extern void HTNet_decreaseSocket (void);
extern int HTNet_availableSockets (void);

PRIVATE int Persistent = 0; /* Counts persistent sockets */
extern void HTNet_increasePersistentSocket (void);
extern void HTNet_decreasePersistentSocket (void);
extern int HTNet_availablePersistentSockets (void);

PRIVATE HTList ** NetTable = NULL; /* List of net objects */
extern BOOL HTNet_deleteAll (void);

PRIVATE int HTNetCount = 0; /* Counting elements in table */
extern BOOL HTNet_isIdle (void); /* HTNetCount > 0 */
extern BOOL HTNet_isEmpty (void); /* HTNetCount <= 0 */
extern int HTNet_count (void);

/* get and call Protocol_server(INVSOC, request) */
PUBLIC BOOL HTNet_newServer (HTRequest * request)
{
HTParentAnchor * anchor = HTRequest_anchor(request);
HTNet * me = NULL;
HTProtocol * protocol;
HTTransport * tp = NULL; /* added JTD:5/28/96 */
char * physical = NULL;
int status;
HTProtCallback * cbf;

if (!request) return NO;

/*
** First we do all the "BEFORE" callbacks in order to see if we are to
** continue with this request or not. If we receive a callback status
** that is NOT HT_OK then jump directly to the after callbacks and return
*/
if ((status = HTNet_executeBeforeAll(request)) != HT_OK) {

/*
** If in non-blocking mode then return here and call AFTER
** filters from a timer event handler. As Olga Antropova
** points out, otherwise, the stack can grow if new requests
** are started directly from the after filters
*/
if (HTEvent_isCallbacksRegistered() && !HTRequest_preemptive(request))
createAfterFilterEvent(request, status);
else
HTNet_executeAfterAll(request, status);
return YES;
}

/*
** If no translation was provided by the filters then use the anchor
** address directly
*/
if (!(physical = HTAnchor_physical(anchor))) {
char * addr = HTAnchor_address((HTAnchor *) anchor);
HTTRACE(CORE_TRACE, "Net Object.. Using default address/n");
HTAnchor_setPhysical(anchor, addr);
physical = HTAnchor_physical(anchor);
HT_FREE(addr);
}

/* Find a protocol object for this access scheme */
{
char * access = HTParse(physical, "", PARSE_ACCESS);
if ((protocol = HTProtocol_find(request, access)) == NULL) {
HTTRACE(CORE_TRACE, "Net Object.. NO PROTOCOL Object found for UR
I scheme `%s/'/n" _ access);
HT_FREE(access);
return NO;
}
if (!(cbf = HTProtocol_server(protocol))) {
HTTRACE(CORE_TRACE, "Net Object.. NO SERVER HANDLER for URI schem
e `%s/'/n" _ access);
HT_FREE(access);
HT_FREE(me);
return NO;
}
HT_FREE(access);
}

/* Find a transport object for this protocol */
if ((tp = HTTransport_find(request, HTProtocol_transport(protocol))) == N
ULL) {
HTTRACE(CORE_TRACE, "Net Object.. NO TRANSPORT found for protocol `%s
/'/n" _ HTProtocol_name(protocol));
return NO;
}

/* Create new net object and bind to request object */
if ((me = create_object()) == NULL) return NO;
me->preemptive = (HTProtocol_preemptive(protocol) || HTRequest_preemptive
(request));
HTNet_setEventPriority(me, HTRequest_priority(request));
me->protocol = protocol;
me->transport = tp; /* added - JTD:5/28/96 */
me->request = request;
HTRequest_setNet(request, me);

/* Start the server request */
HTTRACE(CORE_TRACE, "Net Object.. starting SERVER request %p and net obje
ct %p/n" _ request _ me);
(*(cbf))(INVSOC, request);
return YES;
}

/* HTNet_newClient
** ---------------
** Create a new HTNet object as a new request to be handled. If we have
** more than MaxActive connections already then put this into the
** pending queue, else start the request by calling the call back
** function registered with this access method.
** Returns YES if OK, else NO
*/
/* get and call Protocol_client(INVSOC, request) */
PUBLIC BOOL HTNet_newClient (HTRequest * request)
{
HTParentAnchor * anchor = HTRequest_anchor(request);
HTNet * me = NULL;
HTProtocol * protocol = NULL;
HTTransport * tp = NULL;
char * physical = NULL;
int status;
HTProtCallback * cbf;

if (!request) return NO;

/*
** First we do all the "BEFORE" callbacks in order to see if we are to
** continue with this request or not. If we receive a callback status
** that is NOT HT_OK then jump directly to the after callbacks and return
*/
if ((status = HTNet_executeBeforeAll(request)) != HT_OK) {

/*
** If in non-blocking mode then return here and call AFTER
** filters from a timer event handler. As Olga Antropova
** points out, otherwise, the stack can grow if new requests
** are started directly from the after filters
*/
if (HTEvent_isCallbacksRegistered() && !HTRequest_preemptive(request))
createAfterFilterEvent(request, status);
else
HTNet_executeAfterAll(request, status);
return YES;
}

/*
** If no translation was provided by the filters then use the anchor
** address directly
*/
if (!(physical = HTAnchor_physical(anchor))) {
char * addr = HTAnchor_address((HTAnchor *) anchor);
HTTRACE(CORE_TRACE, "Net Object.. Using default address/n");
HTAnchor_setPhysical(anchor, addr);
physical = HTAnchor_physical(anchor);
HT_FREE(addr);
}

/* Find a protocol object for this access scheme */
{
char * proxy = HTRequest_proxy(request);
char * access = HTParse(proxy ? proxy : physical, "", PARSE_ACCESS);

if ((protocol = HTProtocol_find(request, access)) == NULL) {
HTTRACE(CORE_TRACE, "Net Object.. NO PROTOCOL Object found for UR
I scheme `%s/'/n" _ access);
HT_FREE(access);
return NO;
}
if (!(cbf = HTProtocol_client(protocol))) {
HTTRACE(CORE_TRACE, "Net Object.. NO CLIENT HANDLER for URI schem
e `%s/'/n" _ access);
HT_FREE(access);
HT_FREE(me);
return NO;
}
HT_FREE(access);
}

/* Find a transport object for this protocol */
tp = HTTransport_find(request, HTProtocol_transport(protocol));
if (tp == NULL) {
HTTRACE(CORE_TRACE, "Net Object.. NO TRANSPORT found for protocol `%s
/'/n" _ HTProtocol_name(protocol));
return NO;
}

/* Create new net object and bind it to the request object */
if ((me = create_object()) == NULL) return NO;
me->preemptive = (HTProtocol_preemptive(protocol) || HTRequest_preemptive
(request));
#if 0
me->priority = HTRequest_priority(request);
#endif
HTNet_setEventPriority(me, HTRequest_priority(request));
me->protocol = protocol;
me->transport = tp;
me->request = request;
HTRequest_setNet(request, me);

/* Increase the number of retrys for this download */
HTRequest_addRetry(request);

/*
** Check if we can start the request, else put it into pending queue
** If so then call the call back function associated with the anchor.
** We use the INVSOC as we don't have a valid socket yet!
*/
HTTRACE(CORE_TRACE, "Net Object.. starting request %p (retry=%d) with net
object %p/n" _
request _ HTRequest_retrys(request) _ me);
(*(cbf))(INVSOC, request);
return YES;
}

PUBLIC BOOL HTNet_clear (HTNet * net)
{
if (net) {
net->host->channel = NULL;
net->readStream = NULL;
net->bytesRead = 0;
net->headerBytesRead = 0;
net->bytesWritten = 0;
net->headerBytesWritten = 0;
return YES;
}
return NO;
}

/* Set (*Transport->output_new)(...) to Host's Channel and return it */
PUBLIC HTOutputStream * HTNet_getOutput (HTNet * me, void * param, int mode)
{
if (me && me->host && me->host->channel && me->transport) {
HTTransport * tp = me->transport;
HTChannel * ch = me->host->channel;
HTOutputStream * output = (*tp->output_new)(me->host, ch, param, mode);
HTChannel_setOutput(ch, output);
return output;
}
HTTRACE(CORE_TRACE, "Host Object.. Can't create output stream/n");
return NULL;
}
</code>

===================================================================
??? libwww 如何处理 HTProtocol?
>>>
<code "HTProt.h" impl="HTProt.c">
typedef struct _HTProtocol HTProtocol;
typedef u_short HTProtocolId;

typedef int HTProtCallback (SOCKET, HTRequest *);
extern BOOL HTProtocol_add (const char * name,
const char * transport,
HTProtocolId port,
BOOL preemptive,
HTProtCallback * client,
HTProtCallback * server);
extern BOOL HTProtocol_delete (const char * name);
extern BOOL HTProtocol_deleteAll (void);
extern HTProtocol * HTProtocol_find (HTRequest * request, const char * access);
</code>

<code "HTProt.c">
struct _HTProtocol {
char * name; /* Name of this protocol access scheme */
extern const char * HTProtocol_name (HTProtocol * protocol);

char * transport; /* Name of the transport */
extern BOOL HTProtocol_setTransport (HTProtocol * protoccol,
const char * transport);
extern const char * HTProtocol_transport (HTProtocol * protocol);

HTProtocolId id; /* Default port for this protocol */
extern HTProtocolId HTProtocol_id (HTProtocol * protocol);

BOOL preemptive;
extern BOOL HTProtocol_preemptive (HTProtocol * protocol);

HTProtCallback * client;
extern HTProtCallback * HTProtocol_client (HTProtocol * protocol);

HTProtCallback * server;
extern HTProtCallback * HTProtocol_server (HTProtocol * protocol);
};

PRIVATE HTList * protocols = NULL; /* List of registered protocols */
</code>

<the-end/>

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics