[转]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/>
 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值