}
}
+/* Inputs a string and returns a long.
+ */
+static unsigned long getLong(char *string)
+{
+ int n = 3;
+ unsigned long x = 0;
+
+ while (n) {
+ x |= (unsigned long)((unsigned char)string[n--]) & 0xff;
+ x <<= 8;
+ }
+
+ x |= (unsigned long)((unsigned char)string[0]) & 0xff;
+ return x;
+}
+
static void *create_deflate_server_config(apr_pool_t *p, server_rec *s)
{
deflate_filter_config *c = apr_pcalloc(p, sizeof *c);
}
/* magic header */
-static int deflate_magic[2] = { 0x1f, 0x8b };
+static char deflate_magic[2] = { '\037', '\213' };
typedef struct deflate_ctx_t
{
z_stream stream;
unsigned char *buffer;
unsigned long crc;
- apr_bucket_brigade *bb;
+ apr_bucket_brigade *bb, *proc_bb;
} deflate_ctx;
static apr_status_t deflate_out_filter(ap_filter_t *f,
return APR_SUCCESS;
}
+/* This is the deflate input filter (inflates). */
+static apr_status_t deflate_in_filter(ap_filter_t *f,
+ apr_bucket_brigade *bb,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ apr_bucket *bkt;
+ request_rec *r = f->r;
+ deflate_ctx *ctx = f->ctx;
+ int zRC;
+ apr_status_t rv;
+ deflate_filter_config *c;
+
+ /* just get out of the way of things we don't want. */
+ if (mode != AP_MODE_READBYTES) {
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ c = ap_get_module_config(r->server->module_config, &deflate_module);
+
+ if (!ctx) {
+ int found = 0;
+ char *token, deflate_hdr[10];
+ const char *encoding;
+ apr_size_t len;
+
+ /* only work on main request/no subrequests */
+ if (r->main) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* Let's see what our current Content-Encoding is.
+ * If gzip is present, don't gzip again. (We could, but let's not.)
+ */
+ encoding = apr_table_get(r->headers_in, "Content-Encoding");
+ if (encoding) {
+ const char *tmp = encoding;
+
+ token = ap_get_token(r->pool, &tmp, 0);
+ while (token && token[0]) {
+ if (!strcasecmp(token, "gzip")) {
+ found = 1;
+ break;
+ }
+ /* Otherwise, skip token */
+ tmp++;
+ token = ap_get_token(r->pool, &tmp, 0);
+ }
+ }
+
+ if (found == 0) {
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
+ ctx->buffer = apr_palloc(r->pool, c->bufferSize);
+
+ rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ len = 10;
+ rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* We didn't get the magic bytes. */
+ if (len != 10 ||
+ deflate_hdr[0] != deflate_magic[0] ||
+ deflate_hdr[1] != deflate_magic[1]) {
+ return APR_EGENERAL;
+ }
+
+ /* We can't handle flags for now. */
+ if (deflate_hdr[3] != 0) {
+ return APR_EGENERAL;
+ }
+
+ zRC = inflateInit2(&ctx->stream, c->windowSize);
+
+ if (zRC != Z_OK) {
+ f->ctx = NULL;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "unable to init Zlib: "
+ "inflateInit2 returned %d: URL %s",
+ zRC, r->uri);
+ ap_remove_input_filter(f);
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ /* initialize deflate output buffer */
+ ctx->stream.next_out = ctx->buffer;
+ ctx->stream.avail_out = c->bufferSize;
+
+ apr_brigade_cleanup(ctx->bb);
+ }
+
+ if (APR_BRIGADE_EMPTY(ctx->proc_bb)) {
+ rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ APR_BRIGADE_FOREACH(bkt, ctx->bb) {
+ const char *data;
+ apr_size_t len;
+
+ /* If we actually see the EOS, that means we screwed up! */
+ if (APR_BUCKET_IS_EOS(bkt)) {
+ return APR_EGENERAL;
+ }
+
+ if (APR_BUCKET_IS_FLUSH(bkt)) {
+ apr_bucket *tmp_heap;
+ zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH);
+ if (zRC != Z_OK) {
+ return APR_EGENERAL;
+ }
+
+ ctx->stream.next_out = ctx->buffer;
+ len = c->bufferSize - ctx->stream.avail_out;
+
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+ tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+ NULL, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+ ctx->stream.avail_out = c->bufferSize;
+
+ /* Move everything to the returning brigade. */
+ APR_BUCKET_REMOVE(bkt);
+ APR_BRIGADE_CONCAT(bb, ctx->bb);
+ break;
+ }
+
+ /* read */
+ apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ);
+
+ /* pass through zlib inflate. */
+ ctx->stream.next_in = (unsigned char *)data;
+ ctx->stream.avail_in = len;
+
+ while (ctx->stream.avail_in != 0) {
+ if (ctx->stream.avail_out == 0) {
+ apr_bucket *tmp_heap;
+ ctx->stream.next_out = ctx->buffer;
+ len = c->bufferSize - ctx->stream.avail_out;
+
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+ tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+ NULL, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+ ctx->stream.avail_out = c->bufferSize;
+ }
+
+ zRC = inflate(&ctx->stream, Z_NO_FLUSH);
+
+ if (zRC == Z_STREAM_END) {
+ break;
+ }
+
+ if (zRC != Z_OK) {
+ return APR_EGENERAL;
+ }
+ }
+ if (zRC == Z_STREAM_END) {
+ apr_bucket *tmp_heap, *eos;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "Zlib: Inflated %ld to %ld : URL %s",
+ ctx->stream.total_in, ctx->stream.total_out,
+ r->uri);
+
+ len = c->bufferSize - ctx->stream.avail_out;
+
+ ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len);
+ tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len,
+ NULL, f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap);
+ ctx->stream.avail_out = c->bufferSize;
+
+ /* Is the remaining 8 bytes already in the avail stream? */
+ if (ctx->stream.avail_in >= 8) {
+ unsigned long compCRC, compLen;
+ compCRC = getLong(ctx->stream.next_in);
+ if (ctx->crc != compCRC) {
+ return APR_EGENERAL;
+ }
+ ctx->stream.next_in += 4;
+ compLen = getLong(ctx->stream.next_in);
+ if (ctx->stream.total_out != compLen) {
+ return APR_EGENERAL;
+ }
+ }
+ else {
+ /* FIXME: We need to grab the 8 verification bytes
+ * from the wire! */
+ return APR_EGENERAL;
+ }
+
+ inflateEnd(&ctx->stream);
+ apr_brigade_cleanup(ctx->bb);
+
+ eos = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos);
+ break;
+ }
+ }
+ }
+
+ if (!APR_BRIGADE_EMPTY(ctx->proc_bb)) {
+ apr_bucket_brigade *newbb;
+
+ /* May return APR_INCOMPLETE which is fine by us. */
+ apr_brigade_partition(ctx->proc_bb, readbytes, &bkt);
+
+ newbb = apr_brigade_split(ctx->proc_bb, bkt);
+ APR_BRIGADE_CONCAT(bb, ctx->proc_bb);
+ APR_BRIGADE_CONCAT(ctx->proc_bb, newbb);
+ }
+
+
+
+ return APR_SUCCESS;
+}
+
static void register_hooks(apr_pool_t *p)
{
ap_register_output_filter(deflateFilterName, deflate_out_filter,
AP_FTYPE_CONTENT_SET);
+ ap_register_input_filter(deflateFilterName, deflate_in_filter,
+ AP_FTYPE_CONTENT_SET);
}
static const command_rec deflate_filter_cmds[] = {