📄 mod_deflate.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * mod_deflate.c: Perform deflate content-encoding on the fly * * Written by Ian Holsman, Justin Erenkrantz, and Nick Kew *//* * Portions of this software are based upon zlib code by Jean-loup Gailly * (zlib functions gz_open and gzwrite, check_header) *//* zlib flags */#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */#define ORIG_NAME 0x08 /* bit 3 set: original file name present */#define COMMENT 0x10 /* bit 4 set: file comment present */#define RESERVED 0xE0 /* bits 5..7: reserved */#include "httpd.h"#include "http_config.h"#include "http_log.h"#include "apr_lib.h"#include "apr_strings.h"#include "apr_general.h"#include "util_filter.h"#include "apr_buckets.h"#include "http_request.h"#define APR_WANT_STRFUNC#include "apr_want.h"#include "zlib.h"static const char deflateFilterName[] = "DEFLATE";module AP_MODULE_DECLARE_DATA deflate_module;typedef struct deflate_filter_config_t{ int windowSize; int memlevel; int compressionlevel; apr_size_t bufferSize; char *note_ratio_name; char *note_input_name; char *note_output_name;} deflate_filter_config;/* RFC 1952 Section 2.3 defines the gzip header: * * +---+---+---+---+---+---+---+---+---+---+ * |ID1|ID2|CM |FLG| MTIME |XFL|OS | * +---+---+---+---+---+---+---+---+---+---+ */static const char gzip_header[10] ={ '\037', '\213', Z_DEFLATED, 0, 0, 0, 0, 0, /* mtime */ 0, 0x03 /* Unix OS_CODE */};/* magic header */static const char deflate_magic[2] = { '\037', '\213' };/* windowsize is negative to suppress Zlib header */#define DEFAULT_COMPRESSION Z_DEFAULT_COMPRESSION#define DEFAULT_WINDOWSIZE -15#define DEFAULT_MEMLEVEL 9#define DEFAULT_BUFFERSIZE 8096/* Check whether a request is gzipped, so we can un-gzip it. * If a request has multiple encodings, we need the gzip * to be the outermost non-identity encoding. */static int check_gzip(request_rec *r, apr_table_t *hdrs1, apr_table_t *hdrs2){ int found = 0; apr_table_t *hdrs = hdrs1; const char *encoding = apr_table_get(hdrs, "Content-Encoding"); if (!encoding && (hdrs2 != NULL)) { /* the output filter has two tables and a content_encoding to check */ encoding = apr_table_get(hdrs2, "Content-Encoding"); hdrs = hdrs2; if (!encoding) { encoding = r->content_encoding; hdrs = NULL; } } if (encoding && *encoding) { /* check the usual/simple case first */ if (!strcasecmp(encoding, "gzip") || !strcasecmp(encoding, "x-gzip")) { found = 1; if (hdrs) { apr_table_unset(hdrs, "Content-Encoding"); } else { r->content_encoding = NULL; } } else if (ap_strchr_c(encoding, ',') != NULL) { /* If the outermost encoding isn't gzip, there's nowt * we can do. So only check the last non-identity token */ char *new_encoding = apr_pstrdup(r->pool, encoding); char *ptr; for(;;) { char *token = ap_strrchr(new_encoding, ','); if (!token) { /* gzip:identity or other:identity */ if (!strcasecmp(new_encoding, "gzip") || !strcasecmp(new_encoding, "x-gzip")) { found = 1; if (hdrs) { apr_table_unset(hdrs, "Content-Encoding"); } else { r->content_encoding = NULL; } } break; /* seen all tokens */ } for (ptr=token+1; apr_isspace(*ptr); ++ptr); if (!strcasecmp(ptr, "gzip") || !strcasecmp(ptr, "x-gzip")) { *token = '\0'; if (hdrs) { apr_table_setn(hdrs, "Content-Encoding", new_encoding); } else { r->content_encoding = new_encoding; } found = 1; } else if (!ptr[0] || !strcasecmp(ptr, "identity")) { *token = '\0'; continue; /* strip the token and find the next one */ } break; /* found a non-identity token */ } } } return found;}/* Outputs a long in LSB order to the given file * only the bottom 4 bits are required for the deflate file format. */static void putLong(unsigned char *string, unsigned long x){ string[0] = (unsigned char)(x & 0xff); string[1] = (unsigned char)((x & 0xff00) >> 8); string[2] = (unsigned char)((x & 0xff0000) >> 16); string[3] = (unsigned char)((x & 0xff000000) >> 24);}/* Inputs a string and returns a long. */static unsigned long getLong(unsigned char *string){ return ((unsigned long)string[0]) | (((unsigned long)string[1]) << 8) | (((unsigned long)string[2]) << 16) | (((unsigned long)string[3]) << 24);}static void *create_deflate_server_config(apr_pool_t *p, server_rec *s){ deflate_filter_config *c = apr_pcalloc(p, sizeof *c); c->memlevel = DEFAULT_MEMLEVEL; c->windowSize = DEFAULT_WINDOWSIZE; c->bufferSize = DEFAULT_BUFFERSIZE; c->compressionlevel = DEFAULT_COMPRESSION; return c;}static const char *deflate_set_window_size(cmd_parms *cmd, void *dummy, const char *arg){ deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, &deflate_module); int i; i = atoi(arg); if (i < 1 || i > 15) return "DeflateWindowSize must be between 1 and 15"; c->windowSize = i * -1; return NULL;}static const char *deflate_set_buffer_size(cmd_parms *cmd, void *dummy, const char *arg){ deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, &deflate_module); int n = atoi(arg); if (n <= 0) { return "DeflateBufferSize should be positive"; } c->bufferSize = (apr_size_t)n; return NULL;}static const char *deflate_set_note(cmd_parms *cmd, void *dummy, const char *arg1, const char *arg2){ deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, &deflate_module); if (arg2 == NULL) { c->note_ratio_name = apr_pstrdup(cmd->pool, arg1); } else if (!strcasecmp(arg1, "ratio")) { c->note_ratio_name = apr_pstrdup(cmd->pool, arg2); } else if (!strcasecmp(arg1, "input")) { c->note_input_name = apr_pstrdup(cmd->pool, arg2); } else if (!strcasecmp(arg1, "output")) { c->note_output_name = apr_pstrdup(cmd->pool, arg2); } else { return apr_psprintf(cmd->pool, "Unknown note type %s", arg1); } return NULL;}static const char *deflate_set_memlevel(cmd_parms *cmd, void *dummy, const char *arg){ deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, &deflate_module); int i; i = atoi(arg); if (i < 1 || i > 9) return "DeflateMemLevel must be between 1 and 9"; c->memlevel = i; return NULL;}static const char *deflate_set_compressionlevel(cmd_parms *cmd, void *dummy, const char *arg){ deflate_filter_config *c = ap_get_module_config(cmd->server->module_config, &deflate_module); int i; i = atoi(arg); if (i < 1 || i > 9) return "Compression Level must be between 1 and 9"; c->compressionlevel = i; return NULL;}typedef struct deflate_ctx_t{ z_stream stream; unsigned char *buffer; unsigned long crc; apr_bucket_brigade *bb, *proc_bb; int (*libz_end_func)(z_streamp); unsigned char *validation_buffer; apr_size_t validation_buffer_length; int inflate_init;} deflate_ctx;/* Number of validation bytes (CRC and length) after the compressed data */#define VALIDATION_SIZE 8/* Do not update ctx->crc, see comment in flush_libz_buffer */#define NO_UPDATE_CRC 0/* Do update ctx->crc, see comment in flush_libz_buffer */#define UPDATE_CRC 1static 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 crc){ 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) { /* * Do we need to update ctx->crc? Usually this is the case for * inflate action where we need to do a crc on the output, whereas * in the deflate case we need to do a crc on the input */ if (crc) { ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, deflate_len); } 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); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -