<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>OurApache &#187; deflate</title>
	<atom:link href="http://ourapache.com/archives/tag/deflate/feed" rel="self" type="application/rss+xml" />
	<link>http://ourapache.com</link>
	<description>我们致力于一个Apache知识的分享网站</description>
	<lastBuildDate>Tue, 13 Apr 2010 05:18:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>gzip 与 deflate</title>
		<link>http://ourapache.com/archives/72</link>
		<comments>http://ourapache.com/archives/72#comments</comments>
		<pubDate>Fri, 06 Feb 2009 06:07:24 +0000</pubDate>
		<dc:creator>OurApache</dc:creator>
				<category><![CDATA[未分类]]></category>
		<category><![CDATA[deflate]]></category>
		<category><![CDATA[gzip]]></category>

		<guid isPermaLink="false">http://www.ourapache.com/?p=72</guid>
		<description><![CDATA[连上网搜资料加读代码，一共花了大约3个小时，到现在，大约清楚了这么几个问题：

deflate 是最基础的算法，在 zlib 里面有实现 
gzip 在 deflate 的 raw data 前增加了 10 个字节的 gzheader，尾部添加了 8 个字节的校验字节（可选 crc32 和 adler32） 和长度标识字节，gzip 的 magic number 是 0x1f, 0x8b 
zlib 自己也有 header 和尾部校验的数据，如果使用 deflateInit 而不是 deflateInit2，或者 windowBits 设置为正数8~15的话 
zlib windowBits 设置为 16 的时候，zlib 自己会产生一个 gzip 的头和尾，这种情况下 OS_CODE 被设置为 255（unknown），尾部校验使用 crc32 。问题是，既然 zlib 本身就提供了这种功能，为什么 apache 和 nginx 不用，反而都选择手工添加呢？ 
为 nginx 添加 deflate 支持，只需要把输出中的头，尾去掉，并把 Content-Encoding 改为 deflate 即可。18 个字节，就这样省下来了。 ]]></description>
			<content:encoded><![CDATA[<p>新东家还没有报道，就安排先做一个小任务：把 nginx 的 <a href="http://ourapache.com/archives/tag/gzip" class="st_tag internal_tag" rel="tag" title="标签 gzip 下的日志">gzip</a> 换成 <a href="http://ourapache.com/archives/tag/deflate" class="st_tag internal_tag" rel="tag" title="标签 deflate 下的日志">deflate</a> ，问为什么，老大说能省 18 个字节。</p>
<p>在baidu上搜了好久，搜到的中文基本上都是讲 apache 的 gzip（apache 1.3） 和 deflate（apache 2.x）的配置的，仅有的几个跟 nginx 相关的，也逃不出配置文件的范畴。至于原理，算法等等，只有去 Google 英文资料了。</p>
<p>换了关键词，直接搜 zlib ，终于找到一些有用的东西，在 http://www.cppblog.com/jinq0123/archive/2007/07/09/HttpCompressConv.html 处看到这样一段话：</p>
<blockquote><p>deflate与gzip解压的代码几乎相同，应该可以合成一块代码。<br />
区别仅有：</p>
<ol>
<li>deflate使用inflateInit()，而gzip使用inflateInit2()进行初始化，比 inflateInit()多一个参数: -MAX_WBITS，表示处理raw deflate数据。因为gzip数据中的zlib压缩数据块没有zlib header的两个字节。使用inflateInit2时要求zlib库忽略zlib header。在zlib手册中要求windowBits为8..15，但是实际上其它范围的数据有特殊作用，见zlib.h中的注释，如负数表示raw deflate。</li>
<li>Apache的deflate变种可能也没有zlib header，需要添加假头后处理。即MS的错误deflate (raw deflate).zlib头第1字节一般是0&#215;78, 第2字节与第一字节合起来的双字节应能被31整除，详见rfc1950。例如Firefox的zlib假头为0&#215;7801，python zlib.compress()结果头部为0x789c。</li>
</ol>
</blockquote>
<p>再去检查 zlib.h 中的注释说明，在 zlib-1.2.3/zlib.h Line 500 的地方发现这样一段话：</p>
<blockquote><p>The windowBits parameter is the base two logarithm of the window size<br />
(the size of the history buffer). It should be in the range 8..15 for this<br />
version of the library. Larger values of this parameter result in better<br />
compression at the expense of memory usage. The default value is 15 if<br />
deflateInit is used instead.</p>
<p>windowBits can also be -8..-15 for raw deflate. In this case, -windowBits<br />
determines the window size. deflate() will then generate raw deflate data<br />
with no zlib header or trailer, and will not compute an adler32 check value.</p>
<p>windowBits can also be greater than 15 for optional gzip encoding. Add<br />
16 to windowBits to write a simple gzip header and trailer around the<br />
compressed data instead of a zlib wrapper. The gzip header will have no<br />
file name, no extra data, no comment, no modification time (set to zero),<br />
no header crc, and the operating system will be set to 255 (unknown).  If a<br />
gzip stream is being written, strm-&gt;adler is a crc32 instead of an adler32.</p></blockquote>
<p>回过头来看 nginx 和 apache 的实现：</p>
<p> nginx-0.6.34/src/http/modules/ngx_http_gzip_filter_module.c Line 335：</p>
<blockquote><p>rc = deflateInit2(&amp;ctx-&gt;zstream, (int) conf-&gt;level, Z_DEFLATED,<br />
-wbits, memlevel, Z_DEFAULT_STRATEGY);</p></blockquote>
<p>httpd-2.0.63/modules/filters/mod_deflate.c Line 374:</p>
<blockquote><p>zRC = deflateInit2(&amp;ctx-&gt;stream, c-&gt;compressionlevel, Z_DEFLATED,<br />
c-&gt;windowSize, c-&gt;memlevel,<br />
Z_DEFAULT_STRATEGY);</p>
<p>(Line 153: c-&gt;windowSize = i * -1; )</p></blockquote>
<p>也就是说，nginx 和 apache 在程序里处理的都是 raw deflate data ，windowBits 都是负数，那为什么 Content-Encoding 都写的是 gzip 而不是 deflate 呢？</p>
<p>在 apache 的 mod_deflate.c 里，首先发现了这样一个写入 gzip header 的动作：</p>
<blockquote><p>       /* RFC 1952 Section 2.3 dictates the gzip header:<br />
*<br />
* +&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+<br />
* |ID1|ID2|CM |FLG|     MTIME     |XFL|OS |<br />
* +&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+&#8212;+<br />
*<br />
* If we wish to populate in MTIME (as hinted in RFC 1952), do:<br />
* putLong(date_array, apr_time_now() / APR_USEC_PER_SEC);<br />
* where date_array is a char[4] and then print date_array in the<br />
* MTIME position.  WARNING: ENDIANNESS ISSUE HERE.<br />
*/<br />
buf = apr_psprintf(r-&gt;pool, “%c%c%c%c%c%c%c%c%c%c”, deflate_magic[0],<br />
deflate_magic[1], Z_DEFLATED, 0 /* flags */,<br />
0, 0, 0, 0 /* 4 chars for mtime */,<br />
0 /* xflags */, OS_CODE);</p></blockquote>
<p>deflate_magic 是这样定义的：</p>
<blockquote><p>/* magic header */<br />
static char deflate_magic[2] = { &#8216; 37&#8242;, &#8217;213&#8242; };</p></blockquote>
<p>而 OS_CODE 是在 zutil.h 里定义的，AMIGA 是 1，VAXC 是 2，OS2 是 6，WIN32 是 11，默认 unix 是 3（从这个顺序也可以看出操作系统的发展历史了）</p>
<p>数一下，10个字节，再联想到老大说的 18 个字节，仔细找找，终于在 Line 462 里发现这样一个附加 tail 的动作：</p>
<blockquote><p>buf = apr_palloc(r-&gt;pool, <img class="wp-smiley" src="http://extra-001.yo2cdn.com/wp-includes/images/smilies/icon_cool.gif" border="0" alt="8)" align="absBottom" /> ;<br />
putLong((unsigned char *)&amp;buf[0], ctx-&gt;crc);<br />
putLong((unsigned char *)&amp;buf[4], ctx-&gt;stream.total_in);</p>
<p>b = apr_bucket_pool_create(buf, 8, r-&gt;pool, f-&gt;c-&gt;bucket_alloc);<br />
APR_BRIGADE_INSERT_TAIL(ctx-&gt;bb, b);</p></blockquote>
<p>不多不少，8个字节。10个字节的头加上 8 个字节的尾巴，就是老大说的多出来的 18 个字节。apache 调用 zlib 的接口产生了 raw defalte 的数据，然后手工的添加了 gzip 头和尾。</p>
<p>同样的，在 nginx 的 ngx_http_gzip_filter_module.c 首先在 Line 179看到 Igor Sysoev 同学很不负责任的定义了这样一个 gzip header：</p>
<blockquote><p>static u_char  gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };</p></blockquote>
<p>仔细看最后一位！居然直接写了一个 3 ！这会不会导致 windows 上编译的 nginx 在输出 gzip 压缩过的页面的时候，客户端解压不正常？回头有空再去看看 zlib 里关于解压的算法代码中，对于这个 OS_CODE 是怎么处理的吧。</p>
<p>继续寻找，在 Line 351 的地方，作者还写了一段注释（虽然我越看越不明白他在试图表达什么意思）：</p>
<blockquote><p>        b-&gt;memory = 1;<br />
b-&gt;pos = gzheader;<br />
b-&gt;last = b-&gt;pos + 10;</p>
<p>out.buf = b;<br />
out.next = NULL;</p>
<p>/*<br />
* We pass the gzheader to the next filter now to avoid its linking<br />
* to the ctx-&gt;busy chain.  zlib does not usually output the compressed<br />
* data in the initial iterations, so the gzheader that was linked<br />
* to the ctx-&gt;busy chain would be flushed by ngx_http_write_filter().<br />
*/</p></blockquote>
<p>大致是说把 gzheader 传给下一个 filter 去处理，这个 filter 只出来 raw deflate 数据，以及附加的 tail 吧。在 Line 605 的地方：</p>
<blockquote><p>#if (NGX_HAVE_LITTLE_ENDIAN &amp;&amp; NGX_HAVE_NONALIGNED)</p>
<p>trailer-&gt;crc32 = ctx-&gt;crc32;<br />
trailer-&gt;zlen = ctx-&gt;zin;</p>
<p>#else<br />
trailer-&gt;crc32[0] = (u_char) (ctx-&gt;crc32 &amp; 0xff);<br />
trailer-&gt;crc32[1] = (u_char) ((ctx-&gt;crc32 &gt;&gt; <img class="wp-smiley" src="http://extra-001.yo2cdn.com/wp-includes/images/smilies/icon_cool.gif" border="0" alt="8)" align="absBottom" /> &amp; 0xff);<br />
trailer-&gt;crc32[2] = (u_char) ((ctx-&gt;crc32 &gt;&gt; 16) &amp; 0xff);<br />
trailer-&gt;crc32[3] = (u_char) ((ctx-&gt;crc32 &gt;&gt; 24) &amp; 0xff);</p>
<p>trailer-&gt;zlen[0] = (u_char) (ctx-&gt;zin &amp; 0xff);<br />
trailer-&gt;zlen[1] = (u_char) ((ctx-&gt;zin &gt;&gt; <img class="wp-smiley" src="http://extra-001.yo2cdn.com/wp-includes/images/smilies/icon_cool.gif" border="0" alt="8)" align="absBottom" /> &amp; 0xff);<br />
trailer-&gt;zlen[2] = (u_char) ((ctx-&gt;zin &gt;&gt; 16) &amp; 0xff);<br />
trailer-&gt;zlen[3] = (u_char) ((ctx-&gt;zin &gt;&gt; 24) &amp; 0xff);<br />
#endif</p></blockquote>
<p>幸亏有 IBM MOTOROLA  们造了 Big Endian 机器，这样一来，这段代码的意思再明白不过了。</p>
<p>连上网搜资料加读代码，一共花了大约3个小时，到现在，大约清楚了这么几个问题：</p>
<ol>
<li>deflate 是最基础的算法，在 zlib 里面有实现</li>
<li>gzip 在 deflate 的 raw data 前增加了 10 个字节的 gzheader，尾部添加了 8 个字节的校验字节（可选 crc32 和 adler32） 和长度标识字节，gzip 的 magic number 是 0x1f, 0x8b</li>
<li>zlib 自己也有 header 和尾部校验的数据，如果使用 deflateInit 而不是 deflateInit2，或者 windowBits 设置为正数8~15的话</li>
<li>zlib windowBits 设置为 16 的时候，zlib 自己会产生一个 gzip 的头和尾，这种情况下 OS_CODE 被设置为 255（unknown），尾部校验使用 crc32 。问题是，既然 zlib 本身就提供了这种功能，为什么 apache 和 nginx 不用，反而都选择手工添加呢？</li>
<li>为 nginx 添加 deflate 支持，只需要把输出中的头，尾去掉，并把 Content-Encoding 改为 deflate 即可。18 个字节，就这样省下来了。</li>
</ol>
<p>参考资料：</p>
<ul>
<li><a title="zlib 手册" href="http://www.zlib.net/manual.html" target="_blank"><span style="color: #44a1d0;">zlib 主页上的手册</span></a>：http://www.zlib.net/manual.html</li>
</ul>
<p>PS，在腾讯最后一天上班，并祝所有爱我的和我爱的人小年快乐！大年也快乐！</p>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年02月7号 -- <a href="http://ourapache.com/archives/94" title="使用gzip压缩来压缩网页之apache的相关配置">使用gzip压缩来压缩网页之apache的相关配置</a></li><li>2008年12月25号 -- <a href="http://ourapache.com/archives/1" title="使用gzip将你的Apache速度提高十倍">使用gzip将你的Apache速度提高十倍</a></li></ul>
	标签：<a href="http://ourapache.com/archives/tag/deflate" title="deflate" rel="tag">deflate</a>, <a href="http://ourapache.com/archives/tag/gzip" title="gzip" rel="tag">gzip</a>, <a href="http://ourapache.com/archives/category/uncategorized" title="未分类" rel="tag">未分类</a><br />
]]></content:encoded>
			<wfw:commentRss>http://ourapache.com/archives/72/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

