Mercurial > hg > nginx
view src/http/modules/ngx_http_gzip_filter.c @ 134:d57c6835225c
nginx-0.0.1-2003-09-26-09:45:21 import
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Fri, 26 Sep 2003 05:45:21 +0000 |
parents | b27548f540ad |
children | e29909bd9b8a |
line wrap: on
line source
#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> #include <zlib.h> typedef struct { int enable; int hunk_size; int hunks; int no_buffer; } ngx_http_gzip_conf_t; typedef struct { ngx_chain_t *in; ngx_chain_t *free; ngx_chain_t *busy; ngx_chain_t *out; ngx_chain_t **last_out; ngx_hunk_t *in_hunk; ngx_hunk_t *out_hunk; int hunks; int length; void *alloc; int flush; u_int crc32; z_stream zstream; } ngx_http_gzip_ctx_t; ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); static int ngx_http_gzip_filter_init(ngx_cycle_t *cycle); static void *ngx_http_gzip_create_conf(ngx_conf_t *cf); static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_command_t ngx_http_gzip_filter_commands[] = { {ngx_string("gzip"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, enable), NULL}, {ngx_string("gzip_hunk_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, hunk_size), NULL}, {ngx_string("gzip_hunks"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, hunks), NULL}, {ngx_string("gzip_no_buffer"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_gzip_conf_t, no_buffer), NULL}, ngx_null_command }; static ngx_http_module_t ngx_http_gzip_filter_module_ctx = { NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_gzip_create_conf, /* create location configuration */ ngx_http_gzip_merge_conf, /* merge location configuration */ }; ngx_module_t ngx_http_gzip_filter_module = { NGX_MODULE, &ngx_http_gzip_filter_module_ctx, /* module context */ ngx_http_gzip_filter_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ ngx_http_gzip_filter_init, /* init module */ NULL /* init child */ }; static char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; #if (HAVE_LITTLE_ENDIAN) struct gztrailer { u_int crc32; u_int zlen; }; #else /* HAVE_BIG_ENDIAN */ struct gztrailer { unsigned char crc32[4]; unsigned char zlen[4]; }; #endif static int (*next_header_filter) (ngx_http_request_t *r); static int (*next_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch); static int ngx_http_gzip_header_filter(ngx_http_request_t *r) { ngx_http_gzip_ctx_t *ctx; ngx_http_gzip_conf_t *conf; if (r->headers_out.status != NGX_HTTP_OK || r->header_only /* || r->content_encoding */ /* || r->accept_encoding == NULL */ || r->main) { return next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); if (!conf->enable /* TODO: conf->version */ /* TODO: "text/" -> custom types */ || ngx_strncasecmp(r->headers_out.content_type->value.data, "text/", 5) != 0) { return next_header_filter(r); } ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module, sizeof(ngx_http_gzip_ctx_t), NGX_ERROR); ctx->length = r->headers_out.content_length; r->headers_out.content_length = -1; r->filter |= NGX_HTTP_FILTER_NEED_IN_MEMORY; return next_header_filter(r); } static int ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { int rc, zin, zout; struct gztrailer *trailer; ngx_hunk_t *h; ngx_chain_t *ce; ngx_http_gzip_ctx_t *ctx; ngx_http_gzip_conf_t *conf; ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); if (ctx == NULL) { return next_body_filter(r, in); } conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); if (ctx->alloc == NULL) { #if 0 ngx_test_null(ctx->alloc, ngx_alloc(200K, r->log), NGX_ERROR); #else ctx->alloc = (void *) ~NULL; #endif rc = deflateInit2(&ctx->zstream, /**/ 1, Z_DEFLATED, /**/ -MAX_WBITS, /**/ MAX_MEM_LEVEL - 1, Z_DEFAULT_STRATEGY); if (rc != Z_OK) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "deflateInit2() failed: %d", rc); return ngx_http_gzip_error(ctx); } ngx_test_null(h, ngx_calloc_hunk(r->pool), ngx_http_gzip_error(ctx)); h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY; h->pos = gzheader; h->last = h->pos + 10; ngx_test_null(ce, ngx_alloc_chain_entry(r->pool), ngx_http_gzip_error(ctx)); ce->hunk = h; ce->next = NULL; ctx->out = ce; ctx->last_out = &ce->next; ctx->crc32 = crc32(0L, Z_NULL, 0); ctx->flush = Z_NO_FLUSH; } if (in) { if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { return ngx_http_gzip_error(ctx); } } while (ctx->in || ctx->out || ctx->zstream.avail_in || ctx->zstream.avail_out || ctx->flush != Z_NO_FLUSH) { for ( ;; ) { if (ctx->in && ctx->zstream.avail_in == 0 && ctx->flush == Z_NO_FLUSH) { ctx->in_hunk = ctx->in->hunk; ctx->in = ctx->in->next; ctx->zstream.avail_in = ctx->in_hunk->last - ctx->in_hunk->pos; if (ctx->zstream.avail_in == 0) { continue; } ctx->zstream.next_in = ctx->in_hunk->pos; ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, ctx->zstream.avail_in); if (ctx->in_hunk->type & NGX_HUNK_LAST) { ctx->flush = Z_FINISH; } else if (ctx->in_hunk->type & NGX_HUNK_FLUSH) { ctx->flush = Z_SYNC_FLUSH; } } if (ctx->zstream.avail_out == 0) { if (ctx->free) { ctx->out_hunk = ctx->free->hunk; ctx->free = ctx->free->next; } else if (ctx->hunks < conf->hunks) { ngx_test_null(ctx->out_hunk, ngx_create_temp_hunk(r->pool, conf->hunk_size, 0, 0), ngx_http_gzip_error(ctx)); ctx->hunks++; } else { break; } ctx->zstream.next_out = ctx->out_hunk->pos; ctx->zstream.avail_out = conf->hunk_size; } rc = deflate(&ctx->zstream, ctx->flush); if (rc != Z_OK && rc != Z_STREAM_END) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "deflate() failed: %d, %d", ctx->flush, rc); return ngx_http_gzip_error(ctx); } ngx_log_debug(r->connection->log, "deflate(): %d %d" _ ctx->flush _ rc); ctx->in_hunk->pos = ctx->zstream.next_in; if (ctx->zstream.avail_out == 0) { ctx->out_hunk->last += conf->hunk_size; ngx_add_hunk_to_chain(ce, ctx->out_hunk, r->pool, ngx_http_gzip_error(ctx)); *ctx->last_out = ce; ctx->last_out = &ce->next; } else { ctx->out_hunk->last = ctx->zstream.next_out; if (ctx->flush == Z_SYNC_FLUSH) { ctx->out_hunk->type |= NGX_HUNK_FLUSH; ctx->flush = Z_NO_FLUSH; ngx_add_hunk_to_chain(ce, ctx->out_hunk, r->pool, ngx_http_gzip_error(ctx)); *ctx->last_out = ce; ctx->last_out = &ce->next; break; } else if (ctx->flush == Z_FINISH) { /* rc == Z_STREAM_END */ zin = ctx->zstream.total_in; zout = 10 + ctx->zstream.total_out + 8; ctx->flush = Z_NO_FLUSH; ngx_add_hunk_to_chain(ce, ctx->out_hunk, r->pool, ngx_http_gzip_error(ctx)); *ctx->last_out = ce; ctx->last_out = &ce->next; if (ctx->zstream.avail_out >= 8) { trailer = (struct gztrailer *) &ctx->zstream.avail_in; ctx->out_hunk->type |= NGX_HUNK_LAST; ctx->out_hunk->last += 8; } else { /* STUB */ trailer = NULL; } #if (HAVE_LITTLE_ENDIAN) trailer->crc32 = ctx->crc32; trailer->zlen = zin; #else /* STUB */ #endif deflateEnd(&ctx->zstream); #if 0 ngx_free(); set ctx = NULL; #endif break; } else if (conf->no_buffer && ctx->in == NULL) { ngx_add_hunk_to_chain(ce, ctx->out_hunk, r->pool, ngx_http_gzip_error(ctx)); *ctx->last_out = ce; ctx->last_out = &ce->next; break; } } } rc = next_body_filter(r, ctx->out); if (rc == NGX_ERROR) { return ngx_http_gzip_error(ctx); } ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out); ctx->last_out = &ctx->out; } /* STUB */ return next_body_filter(r, NULL); } ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) { #if 0 ngx_free(ctx->alloc); #endif return NGX_ERROR; } static int ngx_http_gzip_filter_init(ngx_cycle_t *cycle) { next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_gzip_header_filter; next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_gzip_body_filter; return NGX_OK; } static void *ngx_http_gzip_create_conf(ngx_conf_t *cf) { ngx_http_gzip_conf_t *conf; ngx_test_null(conf, ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)), NGX_CONF_ERROR); conf->enable = NGX_CONF_UNSET; conf->hunk_size = NGX_CONF_UNSET; conf->hunks = NGX_CONF_UNSET; conf->no_buffer = NGX_CONF_UNSET; return conf; } static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_gzip_conf_t *prev = parent; ngx_http_gzip_conf_t *conf = child; ngx_conf_merge_value(conf->enable, prev->enable, 0); ngx_conf_merge_size_value(conf->hunk_size, prev->hunk_size, /* STUB: PAGE_SIZE */ 4096); ngx_conf_merge_value(conf->hunks, prev->hunks, 4); ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); return NGX_CONF_OK; }