Peter J. Cranstone wrote:
> Just a heads up for this forum. We have finished porting mod_gzip to
> Apace 2.0 but are holding off on releasing it until Apache goes solid
> beta. There are still a lot of unresolved issues and would rather see
> the 2.x more stable before releasing.
>
> Mod_gzip for Apache 2.0 is based on mod_gzip for Apache 1.x and allows
> those users with existing configs to use them in 2.x
>
I'd welcome a comparision of both methods Peter, (you could mail me your code
or you could do it yourself)
The reason I wrote this in the first place was due to you not supporting
2.0 in mod_gzip, and when I started looking at the problem it was easily solved
by using zlib.
I think a GZip filter (whichever one) should be in the core.
3rd party modules are invisible to most people, and this would save
people $$$ in bandwidth challenged countries like Oz and most of Asia.
> Regards
>
>
> Peter J. Cranstone
>
> -----Original Message-----
> From: Justin Erenkrantz [mailto:[EMAIL PROTECTED]]
> Sent: Saturday, September 01, 2001 3:57 PM
> To: [EMAIL PROTECTED]
> Subject: [PATCH] Add mod_gz to httpd-2.0
>
>
> Ian has posted his mod_gz filter before, now I'd like to give it a +1.
>
> I told him I'd look at it a while ago, but never got a chance to do
> so. So, I spent this morning cleaning up the configuration and a bit
> of the code to fit our style (nothing major).
>
> I'd like to add this to the modules/filters directory (which seems like
> the most appropriate place).
>
> Can I get two other +1s? I've reviewed the code and can get it
> confirmed working with Netscape 4.77 and Mozilla 0.9.3 by adding the
> following to httpd.conf:
>
> <IfModule mod_gz.c>
> GZFilter On
> AddOutputFilter GZ html
> </IfModule>
>
> We could remove GZFilter as it really serves no purpose as well as the
> text/html check in mod_gz. I'd like to commit something that is close
> to what Ian originally submitted and then tweak it slightly.
>
> (Interesting to note that Netscape 4.77 does not allow you to view the
> source of a gzipped'd entity while Mozilla shows you the decompressed
> entity. Mozilla is getting cool...)
>
> I'm sure we can do more analysis of its performance (what the
> appropriate deflation settings should be), but I'd really to get this in
> first. =-) Please test and report back... -- justin
>
> Index: config.m4
> ===================================================================
> RCS file: /home/cvs/httpd-2.0/modules/filters/config.m4,v
> retrieving revision 1.6
> diff -u -r1.6 config.m4
> --- config.m4 2001/05/12 03:48:31 1.6
> +++ config.m4 2001/09/01 21:38:16
> @@ -6,6 +6,55 @@
>
> APACHE_MODULE(include, Server Side Includes, , , yes)
>
> +APACHE_MODULE(gz, GZip encoding support, "mod_gz.lo", , most, [
> + AC_ARG_WITH(z, [ --with-z=DIR use a specific zlib library],
> + [
> + if test "x$withval" != "xyes" && test "x$withval" != "x"; then
> + ap_zlib_base="$withval"
> + fi
> + ])
> + if test "x$ap_zlib_base" = "x"; then
> + AC_MSG_CHECKING([for zlib location])
> + AC_CACHE_VAL(ap_cv_zlib,[
> + for dir in /usr/local /usr ; do
> + if test -d $dir && test -f $dir/include/zlib.h; then
> + ap_cv_zlib=$dir
> + break
> + fi
> + done
> + ])
> + ap_zlib_base=$ap_cv_zlib
> + if test "x$ap_zlib_base" = "x"; then
> + enable_gz=no
> + AC_MSG_RESULT([not found])
> + else
> + AC_MSG_RESULT([$ap_zlib_base])
> + fi
> + fi
> + if test "$enable_gz" != "no"; then
> + ap_save_includes=$INCLUDE
> + ap_save_ldflags=$LDFLAGS
> + ap_save_libs=$LIBS
> + if test "$ap_zlib_base" != "/usr"; then
> + APR_ADDTO(INCLUDES, [-I${ap_zlib_base}/include])
> + APR_ADDTO(LDFLAGS, [-L${ap_zlib_base}/lib])
> + if test "x$ap_platform_runtime_link_flag" != "x"; then
> + APR_ADDTO(LDFLAGS,
> [$ap_platform_runtime_link_flag${ap_zlib_Base}/lib])
> + fi
> + fi
> + APR_ADDTO(LIBS, [-lz])
> + AC_MSG_CHECKING([for zlib library])
> + AC_TRY_LINK([#include <zlib.h>], [return Z_OK;],
> + [AC_MSG_RESULT(found)
> + AC_CHECK_HEADERS(zutil.h)],
> + [AC_MSG_RESULT(not found)
> + enable_gz=no
> + INCLUDES=$ap_save_includes
> + LDFLAGS=$ap_save_ldflags
> + LIBS=$ap_save_libs])
> + fi
> +])
> +
> APR_ADDTO(LT_LDFLAGS,-export-dynamic)
>
> APACHE_MODPATH_FINISH
>
> Index: mod_gz.c
> ===================================================================
> /* ====================================================================
> * The Apache Software License, Version 1.1
> *
> * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
> * reserved.
> *
> * Redistribution and use in source and binary forms, with or without
> * modification, are permitted provided that the following conditions
> * are met:
> *
> * 1. Redistributions of source code must retain the above copyright
> * notice, this list of conditions and the following disclaimer.
> *
> * 2. Redistributions in binary form must reproduce the above copyright
> * notice, this list of conditions and the following disclaimer in
> * the documentation and/or other materials provided with the
> * distribution.
> *
> * 3. The end-user documentation included with the redistribution,
> * if any, must include the following acknowledgment:
> * "This product includes software developed by the
> * Apache Software Foundation (http://www.apache.org/)."
> * Alternately, this acknowledgment may appear in the software
> itself,
> * if and wherever such third-party acknowledgments normally appear.
> *
> * 4. The names "Apache" and "Apache Software Foundation" must
> * not be used to endorse or promote products derived from this
> * software without prior written permission. For written
> * permission, please contact [EMAIL PROTECTED]
> *
> * 5. Products derived from this software may not be called "Apache",
> * nor may "Apache" appear in their name, without prior written
> * permission of the Apache Software Foundation.
> *
> * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
> * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
> * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
> * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
> * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
> * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
> * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
> * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
> * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> * SUCH DAMAGE.
> * ====================================================================
> *
> * This software consists of voluntary contributions made by many
> * individuals on behalf of the Apache Software Foundation. For more
> * information on the Apache Software Foundation, please see
> * <http://www.apache.org/>.
> *
> * Portions of this software are based upon public domain software
> * (zlib functions gz_open and gzwrite)
> */
>
> /*
> * mod_gz.c: GZip's HTML content on the fly
> *
> * Written by Ian Holsman ([EMAIL PROTECTED])
> *
> */
>
> #include "httpd.h"
> #include "http_config.h"
> #include "http_log.h"
> #include "apr_strings.h"
> #include "apr_general.h"
> #include "util_filter.h"
> #include "apr_buckets.h"
> #include "http_request.h"
>
> #include "zlib.h"
> #ifdef HAVE_ZUTIL_H
> #include "zutil.h"
> #else
> /* As part of the encoding process, we must send what our OS_CODE is
> * (or so it seems based on what I can tell of how gzip encoding works).
> *
> * zutil.h is not always included with zlib distributions (it is a
> private
> * header), so this is straight from zlib 1.1.3's zutil.h. - JRE */
> #ifdef OS2 # define OS_CODE 0x06 #endif
>
> #ifdef WIN32 /* Window 95 & Windows NT */
> # define OS_CODE 0x0b
> #endif
>
> #if defined(VAXC) || defined(VMS)
> # define OS_CODE 0x02
> #endif
>
> #ifdef AMIGA
> # define OS_CODE 0x01
> #endif
>
> #if defined(ATARI) || defined(atarist)
> # define OS_CODE 0x05
> #endif
>
> #if defined(MACOS) || defined(TARGET_OS_MAC)
> # define OS_CODE 0x07
> #endif
>
> #ifdef __50SERIES /* Prime/PRIMOS */
> # define OS_CODE 0x0F
> #endif
>
> #ifdef TOPS20
> # define OS_CODE 0x0a
> #endif
>
> #ifndef OS_CODE
> # define OS_CODE 0x03 /* assume Unix */
> #endif
> #endif
>
> static const char s_szGZFilterName[] = "GZ";
> module AP_MODULE_DECLARE_DATA gz_module;
>
> typedef struct
> {
> int bEnabled;
> int windowSize;
> int memlevel;
> char*noteName;
> } GZFilterConfig;
> /* windowsize is negative to suppress Zlib header */
> #define GZFILTER_DEFAULT_WINDOWSIZE -15
> #define GZFILTER_DEFAULT_MEMLEVEL 9
> #define GZFILTER_BUFFERSIZE 8096
> /*#define GZFILTER_COMPRESSION "GZCOMPRESSION" */
>
> typedef struct gz_ctx
> {
> z_stream strm;
> char buffer[GZFILTER_BUFFERSIZE];
> unsigned long crc;
> }
> gz_ctx;
>
> /* Outputs a long in LSB order to the given file
> * only the bottom 4 bits are required for the GZ file format. */
> static void putLong(char *string, unsigned long x) {
> int n;
> for (n = 0; n < 4; n++) {
> string[n] = (int) (x & 0xff);
> x >>= 8;
> }
> }
>
> static void *GZFilterCreateServerConfig(apr_pool_t * p, server_rec * s)
> {
> GZFilterConfig *pConfig = apr_pcalloc(p, sizeof *pConfig);
>
> pConfig->bEnabled = 0;
> pConfig->memlevel = GZFILTER_DEFAULT_MEMLEVEL;
> pConfig->windowSize = GZFILTER_DEFAULT_WINDOWSIZE;
>
> return pConfig;
> }
> static const char *GZSetWindowSize(cmd_parms * cmd, void *dummy, const
> char* arg) {
> GZFilterConfig *pConfig =
> ap_get_module_config(cmd->server->module_config,
> &gz_module);
> int iWindowSize;
> iWindowSize = atoi(arg);
> if (iWindowSize <1 || iWindowSize >15)
> return "GZWindowSize must be between 1 and 15";
> pConfig->windowSize = iWindowSize *-1;
>
> return NULL;
> }
>
> static const char *GZSetNote(cmd_parms * cmd, void *dummy, const char*
> arg) {
> GZFilterConfig *pConfig =
> ap_get_module_config(cmd->server->module_config,
> &gz_module);
> pConfig->noteName=apr_pstrdup(cmd->pool,arg);
>
> return NULL;
> }
>
> static const char *GZSetMemLevel(cmd_parms * cmd, void *dummy, const
> char* arg) {
> GZFilterConfig *pConfig =
> ap_get_module_config(cmd->server->module_config,
> &gz_module);
> int iMemLevel;
> iMemLevel = atoi(arg);
> if (iMemLevel <1 || iMemLevel >9)
> return "GZMemLevel must be between 1 and 9";
> pConfig->memlevel = iMemLevel;
>
> return NULL;
> }
>
> static const char *GZFilterEnable(cmd_parms * cmd, void *dummy, int arg)
> {
> GZFilterConfig *pConfig =
> ap_get_module_config(cmd->server->module_config,
> &gz_module);
> pConfig->bEnabled = arg;
>
> return NULL;
> }
>
> static void GZFilterInsertFilter(request_rec * r)
> {
> GZFilterConfig *pConfig =
> ap_get_module_config(r->server->module_config,
> &gz_module);
>
> if (!pConfig->bEnabled)
> return;
>
> ap_add_output_filter(s_szGZFilterName, NULL, r, r->connection); }
>
> static int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
>
> static apr_status_t GZFilterOutFilter(ap_filter_t * f,
> apr_bucket_brigade * pbbIn) {
> apr_bucket *pbktIn;
> apr_bucket_brigade *pbbOut;
> apr_bucket *pbktOut;
> char *buf;
> char *b;
> const char *accepts;
> request_rec *r = f->r;
> gz_ctx *ctx;
> char *token = NULL;
> int zRC;
> GZFilterConfig *pConfig =
> ap_get_module_config(r->server->module_config,
> &gz_module);
>
> if ( pConfig->bEnabled != 1 )
> return ap_pass_brigade(f->next, pbbIn);
>
> /* only work on main request/no subrequests */
> if (r->main)
> return ap_pass_brigade(f->next, pbbIn);
>
> /* GETs only (for the moment) */
> if (r->method_number != M_GET) {
> return ap_pass_brigade(f->next, pbbIn);
> }
>
> /* only compress text/html files */
> if (strncmp(r->content_type, "text/html", 9))
> return ap_pass_brigade(f->next, pbbIn);
>
> /* some browsers might have problems, so set no-gzip (with
> browsermatch)
> * for them */
> if (apr_table_get(r->subprocess_env, "no-gzip")) {
> return ap_pass_brigade(f->next, pbbIn);
> }
>
> /* if they don't have the line, then they can't play */
> accepts = apr_table_get(r->headers_in, "Accept-Encoding");
> if (accepts == NULL) {
> return ap_pass_brigade(f->next, pbbIn);
> }
>
> token = ap_get_token(r->pool, &accepts, 0);
> while (token && token[0] && strcmp(token, "gzip")) {
> accepts++; /* skip token */
> token = ap_get_token(r->pool, &accepts, 0);
> }
>
> if (token == NULL || token[0] == '\0') {
> return ap_pass_brigade(f->next, pbbIn);
> }
>
> pbbOut = apr_brigade_create(f->r->pool);
> if (!f->ctx) {
> f->ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
> ctx = f->ctx;
> /*
> ctx->strm.zalloc = (alloc_func) 0;
> ctx->strm.zfree = (free_func) 0;
> ctx->strm.opaque = (voidpf) 0;
> ctx->crc = 0L;
> */
> zRC = deflateInit2(&ctx->strm, Z_BEST_SPEED, Z_DEFLATED,
> pConfig->windowSize, pConfig->memlevel,
> Z_DEFAULT_STRATEGY);
> if (zRC != Z_OK) {
> f->ctx = NULL;
> ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
> "unable to init Zlib: deflateInit2 returned %d:
> URL %s",
> zRC, r->uri);
> return ap_pass_brigade(f->next, pbbIn);
> }
> buf = apr_psprintf(r->pool, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0],
> gz_magic[1], Z_DEFLATED, 0 /*flags */ , 0, 0,
> 0,
> 0 /*time */ , 0 /*xflags */ , OS_CODE);
> pbktOut = apr_bucket_pool_create(buf, 10, r->pool);
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
>
> apr_table_setn(r->headers_out, "Content-Encoding", "gzip");
> apr_table_setn(r->headers_out, "Vary", "Accept-Encoding");
> }
> else {
> ctx = f->ctx;
> }
>
> APR_BRIGADE_FOREACH(pbktIn, pbbIn) {
> const char *data;
> apr_size_t lenin;
> unsigned int len;
> char *buf;
> char szCRC[4];
> char szLen[4];
>
> apr_size_t e_wrt;
> int done = 0;
>
> if (APR_BUCKET_IS_EOS(pbktIn)) {
> apr_bucket *pbktEOS = apr_bucket_eos_create();
>
> ctx->strm.avail_in = 0; /* should be zero already
> anyway */
> for (;;) {
> len = GZFILTER_BUFFERSIZE - ctx->strm.avail_out;
> if (len != 0) {
> pbktOut =
> apr_bucket_heap_create(ctx->buffer, len, 1,
> &e_wrt);
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
> ctx->strm.next_out = ctx->buffer;
> ctx->strm.avail_out = GZFILTER_BUFFERSIZE;
> }
> if (done)
> break;
>
> zRC = deflate(&ctx->strm, Z_FINISH);
> if (len == 0 && zRC == Z_BUF_ERROR)
> zRC = Z_OK;
> done = (ctx->strm.avail_out != 0 || zRC ==
> Z_STREAM_END);
> if (zRC != Z_OK && zRC != Z_STREAM_END)
> break;
> }
> putLong(szCRC, ctx->crc);
> putLong(szLen, ctx->strm.total_in);
> buf = apr_palloc(r->pool, 8);
> b=buf;
> *b++=szCRC[0];
> *b++=szCRC[1];
> *b++=szCRC[2];
> *b++=szCRC[3];
> *b++=szLen[0];
> *b++=szLen[1];
> *b++=szLen[2];
> *b++=szLen[3];
>
> pbktOut = apr_bucket_pool_create(buf, 8, r->pool);
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
> ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
> "Zlib: Compressed %ld to %ld : URL %s",
> ctx->strm.total_in, ctx->strm.total_out,
> r->uri);
> if (pConfig->noteName) {
> if (ctx->strm.total_in >0) {
> apr_table_setn(r->notes, pConfig->noteName,
> apr_itoa(r->pool,
>
> (ctx->strm.total_out*100/ctx->strm.total_in)));
> } else {
> apr_table_setn(r->notes,pConfig->noteName,"-");
> }
> }
>
> deflateEnd(&ctx->strm);
>
> f->ctx = NULL;
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktEOS);
> break;
> }
>
> if (APR_BUCKET_IS_FLUSH(pbktIn)) {
> /* XXX FIX: do we need the Content-Size set, or can we
> stream?
> * we should be able to stream */
> /* ignore flush buckets for the moment.. we can't stream as
> we
> * need the size ;( */
> continue;
> }
> /* read */
> apr_bucket_read(pbktIn, &data, &lenin, APR_BLOCK_READ);
> ctx->crc = crc32(ctx->crc, (const Bytef *) data, lenin);
>
> /* write */
> ctx->strm.next_in = (char *) data;
> ctx->strm.avail_in = lenin;
> ctx->strm.next_out = ctx->buffer;
> ctx->strm.avail_out = GZFILTER_BUFFERSIZE;
>
> while (ctx->strm.avail_in != 0) {
> if (ctx->strm.avail_out == 0) {
> ctx->strm.next_out = ctx->buffer;
> len = GZFILTER_BUFFERSIZE - ctx->strm.avail_out;
>
> pbktOut = apr_bucket_heap_create(ctx->buffer, len, 1,
> &e_wrt);
> APR_BRIGADE_INSERT_TAIL(pbbOut, pbktOut);
> ctx->strm.avail_out = GZFILTER_BUFFERSIZE;
> }
> zRC = deflate(&(ctx->strm), Z_NO_FLUSH);
> if (zRC != Z_OK)
> break;
> }
> }
> return ap_pass_brigade(f->next, pbbOut);
> }
>
> static void GZFilterRegisterHooks(apr_pool_t * p)
> {
> /* static const char * const aszPost[] = { "CHUNK", NULL };
> * ap_hook_insert_filter(GZFilterInsertFilter, NULL, aszPost,
> APR_HOOK_LAST); */
> ap_register_output_filter(s_szGZFilterName, GZFilterOutFilter,
> AP_FTYPE_HTTP_HEADER);
> }
>
> static const command_rec GZFilterCmds[] = {
> AP_INIT_FLAG("GZFilter", GZFilterEnable, NULL, RSRC_CONF,
> "Run a GZ filter on this host"),
> AP_INIT_TAKE1("GZFilterNote", GZSetNote, NULL, RSRC_CONF,
> "Set a note to report on compression ratio"),
>
> AP_INIT_TAKE1("GZWindowSize", GZSetWindowSize, NULL, RSRC_CONF,
> "Set the Zlib window size (1-15)"),
> AP_INIT_TAKE1("GZMemLevel", GZSetMemLevel, NULL, RSRC_CONF,
> "Set the Zlib Memory Level (1-9)"),
>
> {NULL}
> };
>
> module AP_MODULE_DECLARE_DATA gz_module = {
> STANDARD20_MODULE_STUFF,
> NULL,
> NULL,
> GZFilterCreateServerConfig,
> NULL,
> GZFilterCmds,
> GZFilterRegisterHooks
> };
>