unsigned char *buffer;
unsigned long crc;
apr_bucket_brigade *bb, *proc_bb;
+ int (*libz_end_func)(z_streamp);
} deflate_ctx;
+static int flush_libz_buffer(deflate_ctx *ctx, deflate_filter_config *c,
+ struct apr_bucket_alloc_t *bucket_alloc,
+ int (*libz_func)(z_streamp, int), int flush)
+{
+ int zRC = Z_OK;
+ int done = 0;
+ unsigned int deflate_len;
+ apr_bucket *b;
+
+ for (;;) {
+ deflate_len = c->bufferSize - ctx->stream.avail_out;
+
+ if (deflate_len != 0) {
+ b = apr_bucket_heap_create((char *)ctx->buffer,
+ deflate_len, NULL,
+ bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+ ctx->stream.next_out = ctx->buffer;
+ ctx->stream.avail_out = c->bufferSize;
+ }
+
+ if (done)
+ break;
+
+ zRC = libz_func(&ctx->stream, flush);
+
+ if (deflate_len == 0 && zRC == Z_BUF_ERROR)
+ zRC = Z_OK;
+
+ done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
+
+ if (zRC != Z_OK && zRC != Z_STREAM_END)
+ break;
+ }
+ return zRC;
+}
+
+static apr_status_t deflate_ctx_cleanup(void *data)
+{
+ deflate_ctx *ctx = (deflate_ctx *)data;
+
+ if (ctx)
+ ctx->libz_end_func(&ctx->stream);
+ return APR_SUCCESS;
+}
+
static apr_status_t deflate_out_filter(ap_filter_t *f,
apr_bucket_brigade *bb)
{
/* Do nothing if asked to filter nothing. */
if (APR_BRIGADE_EMPTY(bb)) {
- return APR_SUCCESS;
+ return ap_pass_brigade(f->next, bb);
}
/* If we don't have a context, we need to ensure that it is okay to send
ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+ ctx->libz_end_func = deflateEnd;
zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
c->windowSize, c->memlevel,
Z_DEFAULT_STRATEGY);
if (zRC != Z_OK) {
- f->ctx = NULL;
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"unable to init Zlib: "
"deflateInit2 returned %d: URL %s",
zRC, r->uri);
+ /*
+ * Remove ourselves as it does not make sense to return:
+ * We are not able to init libz and pass data down the chain
+ * uncompressed.
+ */
+ ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
+ /*
+ * Register a cleanup function to ensure that we cleanup the internal
+ * libz resources.
+ */
+ apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup,
+ apr_pool_cleanup_null);
/* add immortal gzip header */
e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header,
const char *data;
apr_bucket *b;
apr_size_t len;
- int done = 0;
e = APR_BRIGADE_FIRST(bb);
if (APR_BUCKET_IS_EOS(e)) {
char *buf;
- unsigned int deflate_len;
ctx->stream.avail_in = 0; /* should be zero already anyway */
- for (;;) {
- deflate_len = c->bufferSize - ctx->stream.avail_out;
-
- if (deflate_len != 0) {
- b = apr_bucket_heap_create((char *)ctx->buffer,
- deflate_len, NULL,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
- ctx->stream.next_out = ctx->buffer;
- ctx->stream.avail_out = c->bufferSize;
- }
-
- if (done) {
- break;
- }
-
- zRC = deflate(&ctx->stream, Z_FINISH);
-
- if (deflate_len == 0 && zRC == Z_BUF_ERROR) {
- zRC = Z_OK;
- }
-
- done = (ctx->stream.avail_out != 0 || zRC == Z_STREAM_END);
-
- if (zRC != Z_OK && zRC != Z_STREAM_END) {
- break;
- }
- }
+ /* flush the remaining data from the zlib buffers */
+ flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH);
buf = apr_palloc(r->pool, 8);
putLong((unsigned char *)&buf[0], ctx->crc);
}
deflateEnd(&ctx->stream);
+ /* No need for cleanup any longer */
+ apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup);
/* Remove EOS from the old list, and insert into the new. */
APR_BUCKET_REMOVE(e);
}
if (APR_BUCKET_IS_FLUSH(e)) {
- apr_bucket *bkt;
apr_status_t rv;
- apr_bucket_delete(e);
-
- if (ctx->stream.avail_in > 0) {
- zRC = deflate(&(ctx->stream), Z_SYNC_FLUSH);
- if (zRC != Z_OK) {
- return APR_EGENERAL;
- }
+ /* flush the remaining data from the zlib buffers */
+ zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate,
+ Z_SYNC_FLUSH);
+ if (zRC != Z_OK) {
+ return APR_EGENERAL;
}
- ctx->stream.next_out = ctx->buffer;
- len = c->bufferSize - ctx->stream.avail_out;
-
- b = apr_bucket_heap_create((char *)ctx->buffer, len,
- NULL, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
- ctx->stream.avail_out = c->bufferSize;
-
- bkt = apr_bucket_flush_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->bb, bkt);
+ /* Remove flush bucket from old brigade anf insert into the new. */
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
rv = ap_pass_brigade(f->next, ctx->bb);
if (rv != APR_SUCCESS) {
return rv;
zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
- if (zRC != Z_OK)
+ if (zRC != Z_OK) {
return APR_EGENERAL;
+ }
}
apr_bucket_delete(e);
/* Do nothing if asked to filter nothing. */
if (APR_BRIGADE_EMPTY(bb)) {
- return APR_SUCCESS;
+ return ap_pass_brigade(f->next, bb);
}
c = ap_get_module_config(r->server->module_config, &deflate_module);