Apache源代码之自定义函数库

节选自《Apache源代码解析-基于Apache0.6.5》第三章。

网址:http://www.oldapache.org

论坛:http://bbs.oldapache.org

邮箱:tsingien@gmail.com


    本章描述Apache0.6.5的自定义函数库。这些函数供实现Apache功能模块的代码调用。本章代码可以按照功能分成两类,一类实现对文件的读操作 进行缓冲,包括stream.h和stream.c;另外一类由单独的util.c文件组成,实现了一些供其它函数调用的工具函数。

3.1 背景知识

 

    本章涉及到4个相关知识点:url编/解码,时间格式,夏令时以及BASE64编码,了解这些知识点是您能够理解代码的前提,如果您具备了相关知识,可以跳过相关的小节。下面我们对这几个背景知识进行简单说明。

3.1.1 URL编码/解码

 

    发送给服务器的URL请求中,如果含有非数字字母数据的,浏览器会编码后传递,比如您在注册个人信息的时候,需要填写您的中文名和公司英文名两项数据,如果您的中文名字是张三,公司英文名字是Microsoft Corporation,请求方法是GET,表单中中文名字的输入框的name属性是cname,公司英文名输入框的name属性是corpname,服务器端处理表单数据的文件是/user/reg.php ,那么浏览器发送的请求将类似于http://www. server.com/user/reg.php?cname=%D5%C5%C8%FD&corpname=Microsoft%20Corporation

其中%D5%C5%C8%FD就是“张三”两个汉字URL编码后的效果,而公司名称中的%20就是空格字符URL编码后的效果。

    由于浏览器的这个特性,服务器需要在接收数据之后进行URL解码操作,同时服务器提供了编码操作。

    编码和解码操作是通过函数escape_url和unescape_url完成的。

    编码的方法是:不是字母或数字的字符将被替换成百分号(%)后跟其16进制ASCII编码。由于历史原因,将加号(+)编码成%20(这点和RFC1738不兼容)。

    解码就是编码的逆向工程。

3.1.2 时间格式

 

    在HTTP协议里面用到了几个时间格式,在Apache0.6.5里面除了用户自定义时间格式外,还涉及到一下三种:

ctime格式时间:Fri Mar 13 13:58:46 2009

RFC860格式时间:Monday, 15-Aug-05 15:52:01

RFC822格式时间:Mon, 15 Aug 2005 15:52:01

3.1.3 夏令时

 

    夏令时是一种为节约能源而人为规定地方时间的制度。在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间提前一小时,可以 使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。中国曾在1986年到1991年实行过夏令时,80前的同学们应该还有印象。

    在C语言中,结构tm中的域tm_isdst来标识夏令时,实行夏令时的时候,tm_isdst为正;不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst()为负。

    结构tm的详细组成及其含义可以在/usr/include/time.h中找到。在此不再赘述。

3.1.4 BASE64编码

 

    BASE64编码最早使用在邮件传输上,由于历史原因,Email被设计成只允许传送ASCII字符,这样一来,如果您发送了一封带有非ASCII字符的Email通过有“历史原因”的网关就可能出现问题:问题网关有可能将每个字节的最高位置为0。

    此时我们就需要对邮件中传输的信息进行编码,使之全部用ASCII字符表示。

    举例说明,您可以给自己发一封邮件,标题是test,内容简单些,只有两个字“张三”,这时邮箱收到的数据是什么呢?

    我是通过Firefox接收的邮件(OutLook类似),从Firefox里面看起来一切正常,显示发件人,收件人,邮件内容,但是网关上传输的数据并不是您看起来这么简单。

    有一个方法可以知道网关上传输的数据内容都有那些,在Firefox里面选中这封邮件,从菜单中选择“文件”,从子菜单里面选择“导出邮件”,格式选择 “OutLook邮件(*.eml)”,随便起一个名字,比如abc.eml。用文本编辑器打开这个邮件,你会发现内容有点“变态”:

X-Kaspersky: Original server data starting here: +OK 2303 octets

………………

Subject: test

From: Lee tsingien <***@***.com>

To: ***@***.com

Content-Type: text/plain; charset=GB2312

Content-Transfer-Encoding: base64

………………

1cXI/Q==

    从高亮部分可以看到,传输的编码类型是BASE64,那邮件内容呢?就是“1cXI/Q==”,这就是“张三”两个汉字的BASE64编码结果。看到了,全是ASCII字符,这样就跟有“历史问题”的网关兼容了。

    很神奇是不是,您也许要问,它是怎么“编”的呢?

    其实很简单,将需要编码的二进制数据每次取3个byte,顺序放入一个24bit的缓冲区中,不足3byte,将缓冲区中剩余部分补0。然后每次从缓冲区中取出6个bit,根据RFC2045中定义的码表得到对应的字符即可。

    比如我们的张三,16进制数据就是D5C5C8FD,转换成二进制就是11010101110001011100100011111101。每次取6个bit,取出的二进制序列转换成十进制就是53(110101)、28(011100)、23(010111)、8(001000)、63(111111)、16(010000),在表3-1中找到对应的字符就是1cXI/Q

    你也看到了,似乎不对,因为邮件里面的数据比我们操作的结果多出两个等号(=),这是怎么回事呢?

    因为上面的编码规则说的并不完整,相信您也看到了,规则里面是“每次取3个byte”,而需要编码的数据的字节数不一定是3的倍数啊,就像我们的字节数是4字节,不满足条件,这该怎么处理呢?

    补充规则:在编码的字符串后面添加“3-(原文字节数 MOD 3)”个等号(=)。在我们的例子里面,就是添加3-(4mod3)=2个等号。

    所以最终编码结果为1cXI/Q==,也就是您在邮件里面看到的内容。

表3-1 The Base64 Alphabet

  值  编码         值    编码         值 编码         值   编码

   0   A            17   R            34   i            51   z

   1   B            18   S            35   j            52   0

   2   C            19   T            36   k            53   1

   3   D            20   U            37   l            54   2

   4   E            21   V            38   m            55   3

   5   F            22   W            39   n            56   4

   6   G            23   X            40   o            57   5

   7   H            24   Y            41   p            58   6

   8   I            25   Z            42   q            59   7

   9   J            26   a            43   r            60   8

   10  K            27   b            44   s            61   9

   11  L            28   c            45  t             62   +

   12  M            29   d            46   u            63   /

   13  N            30   e            47   v

   14  O            31   f            48   w           (pad)   =

   15  P            32   g            49   x

   16  Q            33   h            50   y

 

    说了这一通,全是和邮件相关的,我们在讨论的是Web服务器啊。别忙,如果您看一下解码函数uudecode调用位置您就明白了,它是为第九章将要讲到的Basic认证方式服务的,这种认证方式从客户端传递过来的认证数据也是BASE64编码的。

    说点题外话,其实在IE浏览器里面还有一个地方用到了BASE64编码了,在IE6之后的版本里面提供了一个功能,就是把网页保存成mht格式。

    您可以用IE打开百度首页http://www.baidu.com , 然后在“文件”菜单中选择“另存为”,在“保存类型”中选择“Web档案,单一文件(*.mht)”选项,然后指定一个文件名,保存成功后您会发现,虽然 百度的首页里面包含的图片,IE只生成了一个.mht文件。然后您可以断开网络连接,在脱机的情况用IE打开这个mht文件,您会发现百度的LOGO图片 也能正常显示,很奇妙?其实您可以用文本编辑器打开这个mht文件,其中有类似这样的一段信息:

------=_NextPart_000_0000_01C9C495.CF9C2DA0

Content-Type: image/gif

Content-Transfer-Encoding: base64

Content-Location: http://www.baidu.com/img/baidu_logo.gif

R0lGODlhDgGBALMAAPHs98zK91RM5JGM7nBq6O1raPfBv+lEQfSdmyMZ3OEGAf///wAAAAAAAAAA

………………

2rIJEQAAOw==

    看到了熟悉的==?再看看上面,注明了Content-Type是image/gif,编码方式也是BASE64,聪明的您一定想到了,原来LOGO文件http://www.baidu.com/img/baidu_logo.gif 的二进制信息已经通过BASE64编码保存在mht文件中了,所以即便是脱机,您也可以看到这个图片。

    如果您有兴趣,可以把这部分内容解码成二进制,然后把二进制信息写入到一个文件中,并把这个命名为baidu_logo.gif,使用图片查看工具查看,看看是不是您当初保存的百度的LOGO图片。

3.2代码注释


3.2.1 stream.h、stream.c

 

    您可能听说过包裹函数(或者叫包装函数),实际上就是对常用操作的一个封装,比如原子日志,在多进程或多线程同时操作一个日志文件的时候,可能会有竞争问 题存在,为了避免竞争,可以设计一个原子日志系统,这个系统提供给调用者几个简单的接口(函数)来完成原子日志的记录,而竞争问题的解决、文件读写的错 误、信号的处理等等都在原子日志系统里面完成,对调用者来说是透明的。

    同样stream.h和stream.c就提供了几个包裹函数,主要用来对文件读操作进行缓冲,这对socket这种特殊文件来说是很有意义的。

限于篇幅,注释代码部分请参看本书官网。


节选自《Apache源代码解析-基于Apache0.6.5》第三章。

网址:http://www.oldapache.org

论坛:http://bbs.oldapache.org

邮箱:tsingien@gmail.com

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值