comparison src/http/modules/ngx_http_gzip_filter.c @ 156:afc333135a6b

nginx-0.0.1-2003-10-23-10:13:16 import
author Igor Sysoev <igor@sysoev.ru>
date Thu, 23 Oct 2003 06:13:16 +0000
parents 46eb23d9471d
children 70b36c805682
comparison
equal deleted inserted replaced
155:46eb23d9471d 156:afc333135a6b
5 5
6 #include <zlib.h> 6 #include <zlib.h>
7 7
8 8
9 typedef struct { 9 typedef struct {
10 int enable; 10 int enable;
11 ngx_bufs_t bufs; 11 ngx_bufs_t bufs;
12 int no_buffer; 12 int level;
13 int wbits;
14 int memlevel;
15 int no_buffer;
13 } ngx_http_gzip_conf_t; 16 } ngx_http_gzip_conf_t;
14 17
15 18
16 typedef struct { 19 typedef struct {
17 ngx_chain_t *in; 20 ngx_chain_t *in;
18 ngx_chain_t *free; 21 ngx_chain_t *free;
19 ngx_chain_t *busy; 22 ngx_chain_t *busy;
20 ngx_chain_t *out; 23 ngx_chain_t *out;
21 ngx_chain_t **last_out; 24 ngx_chain_t **last_out;
22 ngx_hunk_t *in_hunk; 25 ngx_hunk_t *in_hunk;
23 ngx_hunk_t *out_hunk; 26 ngx_hunk_t *out_hunk;
24 int hunks; 27 int hunks;
25 28
26 off_t length; 29 off_t length;
27 void *alloc; 30
28 31 void *preallocated;
29 unsigned flush:4; 32 char *free_mem;
30 unsigned redo:1; 33 int allocated;
31 34
32 u_int crc32; 35 unsigned flush:4;
33 z_stream zstream; 36 unsigned redo:1;
37
38 u_int crc32;
39 z_stream zstream;
40 ngx_http_request_t *request;
34 } ngx_http_gzip_ctx_t; 41 } ngx_http_gzip_ctx_t;
35 42
43
44 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
45 u_int size);
46 static void ngx_http_gzip_filter_free(void *opaque, void *address);
36 47
37 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); 48 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx);
38 static int ngx_http_gzip_filter_init(ngx_cycle_t *cycle); 49 static int ngx_http_gzip_filter_init(ngx_cycle_t *cycle);
39 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf); 50 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
40 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, 51 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
41 void *parent, void *child); 52 void *parent, void *child);
53 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, ngx_command_t *cmd,
54 void *conf);
55 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, ngx_command_t *cmd,
56 void *conf);
57
58 static ngx_conf_bounds_t ngx_http_gzip_comp_level_bounds;
42 59
43 60
44 static ngx_command_t ngx_http_gzip_filter_commands[] = { 61 static ngx_command_t ngx_http_gzip_filter_commands[] = {
45 62
46 {ngx_string("gzip"), 63 {ngx_string("gzip"),
53 {ngx_string("gzip_buffers"), 70 {ngx_string("gzip_buffers"),
54 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 71 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
55 ngx_conf_set_bufs_slot, 72 ngx_conf_set_bufs_slot,
56 NGX_HTTP_LOC_CONF_OFFSET, 73 NGX_HTTP_LOC_CONF_OFFSET,
57 offsetof(ngx_http_gzip_conf_t, bufs), 74 offsetof(ngx_http_gzip_conf_t, bufs),
75 NULL},
76
77 {ngx_string("gzip_comp_level"),
78 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
79 ngx_conf_set_num_slot,
80 NGX_HTTP_LOC_CONF_OFFSET,
81 offsetof(ngx_http_gzip_conf_t, level),
82 &ngx_http_gzip_comp_level_bounds},
83
84 {ngx_string("gzip_window"),
85 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
86 ngx_http_gzip_set_window,
87 NGX_HTTP_LOC_CONF_OFFSET,
88 offsetof(ngx_http_gzip_conf_t, wbits),
89 NULL},
90
91 {ngx_string("gzip_hash"),
92 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
93 ngx_http_gzip_set_hash,
94 NGX_HTTP_LOC_CONF_OFFSET,
95 offsetof(ngx_http_gzip_conf_t, memlevel),
58 NULL}, 96 NULL},
59 97
60 {ngx_string("gzip_no_buffer"), 98 {ngx_string("gzip_no_buffer"),
61 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 99 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
62 ngx_conf_set_flag_slot, 100 ngx_conf_set_flag_slot,
88 ngx_http_gzip_filter_init, /* init module */ 126 ngx_http_gzip_filter_init, /* init module */
89 NULL /* init child */ 127 NULL /* init child */
90 }; 128 };
91 129
92 130
93 static char gzheader[10] = { 0x1f, 131 static ngx_conf_bounds_t ngx_http_gzip_comp_level_bounds = {
94 (char) 0x8b, /* suppress MSVC warning */ 132 ngx_conf_check_num_bounds, { { 1, 9 } }
95 Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; 133 };
134
135
136 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
96 137
97 #if (HAVE_LITTLE_ENDIAN) 138 #if (HAVE_LITTLE_ENDIAN)
98 139
99 struct gztrailer { 140 struct gztrailer {
100 u_int crc32; 141 u_int crc32;
102 }; 143 };
103 144
104 #else /* HAVE_BIG_ENDIAN */ 145 #else /* HAVE_BIG_ENDIAN */
105 146
106 struct gztrailer { 147 struct gztrailer {
107 unsigned char crc32[4]; 148 u_char crc32[4];
108 unsigned char zlen[4]; 149 u_char zlen[4];
109 }; 150 };
110 151
111 #endif 152 #endif
112 153
113 154
144 return ngx_http_next_header_filter(r); 185 return ngx_http_next_header_filter(r);
145 } 186 }
146 187
147 ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module, 188 ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module,
148 sizeof(ngx_http_gzip_ctx_t), NGX_ERROR); 189 sizeof(ngx_http_gzip_ctx_t), NGX_ERROR);
190 ctx->request = r;
149 191
150 ngx_test_null(r->headers_out.content_encoding, 192 ngx_test_null(r->headers_out.content_encoding,
151 ngx_push_table(r->headers_out.headers), 193 ngx_push_table(r->headers_out.headers),
152 NGX_ERROR); 194 NGX_ERROR);
153 195
165 } 207 }
166 208
167 209
168 static int ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in) 210 static int ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
169 { 211 {
170 int rc, wbits, mem_level, zin, zout, last; 212 int rc, wbits, memlevel, zin, zout, last;
171 struct gztrailer *trailer; 213 struct gztrailer *trailer;
172 ngx_hunk_t *h; 214 ngx_hunk_t *h;
173 ngx_chain_t *cl; 215 ngx_chain_t *cl;
174 ngx_http_gzip_ctx_t *ctx; 216 ngx_http_gzip_ctx_t *ctx;
175 ngx_http_gzip_conf_t *conf; 217 ngx_http_gzip_conf_t *conf;
180 return ngx_http_next_body_filter(r, in); 222 return ngx_http_next_body_filter(r, in);
181 } 223 }
182 224
183 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); 225 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
184 226
185 if (ctx->alloc == NULL) { 227 if (ctx->preallocated == NULL) {
186 wbits = MAX_WBITS; 228 wbits = conf->wbits;
187 mem_level = MAX_MEM_LEVEL - 1; 229 memlevel = conf->memlevel;
188 230
189 if (ctx->length > 0) { 231 if (ctx->length > 0) {
190 232
191 /* the actual zlib window size is smaller by 262 bytes */ 233 /* the actual zlib window size is smaller by 262 bytes */
192 234
193 while (ctx->length < ((1 << (wbits - 1)) - 262)) { 235 while (ctx->length < ((1 << (wbits - 1)) - 262)) {
194 wbits--; 236 wbits--;
195 mem_level--; 237 memlevel--;
196 } 238 }
197 } 239 }
198 240
199 #if 0 241 /*
200 ngx_test_null(ctx->alloc, ngx_alloc(200K, r->log), NGX_ERROR); 242 * We preallocate a memory for zlib in one hunk (200K-400K), this
201 #else 243 * dicreases number of malloc() and free() calls and probably
202 ctx->alloc = (void *) -1; 244 * syscalls.
203 #endif 245 * Besides we free() this memory as soon as the gzipping will complete
204 246 * and do not wait while a whole response will be sent to a client.
205 rc = deflateInit2(&ctx->zstream, /**/ 1, Z_DEFLATED, 247 *
206 -wbits, mem_level, Z_DEFAULT_STRATEGY); 248 * 8K is for zlib deflate_state (~6K).
249 *
250 * TODO: 64-bit, round to PAGE_SIZE
251 */
252
253 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
254
255 ngx_test_null(ctx->preallocated, ngx_palloc(r->pool, ctx->allocated),
256 NGX_ERROR);
257 ctx->free_mem = ctx->preallocated;
258
259 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
260 ctx->zstream.zfree = ngx_http_gzip_filter_free;
261 ctx->zstream.opaque = ctx;
262
263 rc = deflateInit2(&ctx->zstream, conf->level, Z_DEFLATED,
264 -wbits, memlevel, Z_DEFAULT_STRATEGY);
207 265
208 if (rc != Z_OK) { 266 if (rc != Z_OK) {
209 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 267 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
210 "deflateInit2() failed: %d", rc); 268 "deflateInit2() failed: %d", rc);
211 return ngx_http_gzip_error(ctx); 269 return ngx_http_gzip_error(ctx);
212 } 270 }
213 271
214 ngx_test_null(h, ngx_calloc_hunk(r->pool), ngx_http_gzip_error(ctx)); 272 ngx_test_null(h, ngx_calloc_hunk(r->pool), ngx_http_gzip_error(ctx));
215 273
216 h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY; 274 h->type = NGX_HUNK_IN_MEMORY|NGX_HUNK_MEMORY;
217 h->pos = gzheader; 275 h->pos = (char *) gzheader;
218 h->last = h->pos + 10; 276 h->last = h->pos + 10;
219 277
220 ngx_alloc_link_and_set_hunk(cl, h, r->pool, ngx_http_gzip_error(ctx)); 278 ngx_alloc_link_and_set_hunk(cl, h, r->pool, ngx_http_gzip_error(ctx));
221 ctx->out = cl; 279 ctx->out = cl;
222 ctx->last_out = &cl->next; 280 ctx->last_out = &cl->next;
248 } 306 }
249 307
250 ctx->in_hunk = ctx->in->hunk; 308 ctx->in_hunk = ctx->in->hunk;
251 ctx->in = ctx->in->next; 309 ctx->in = ctx->in->next;
252 310
253 ctx->zstream.next_in = (unsigned char *) ctx->in_hunk->pos; 311 ctx->zstream.next_in = (u_char *) ctx->in_hunk->pos;
254 ctx->zstream.avail_in = ctx->in_hunk->last - ctx->in_hunk->pos; 312 ctx->zstream.avail_in = ctx->in_hunk->last - ctx->in_hunk->pos;
255 313
256 if (ctx->in_hunk->type & NGX_HUNK_LAST) { 314 if (ctx->in_hunk->type & NGX_HUNK_LAST) {
257 ctx->flush = Z_FINISH; 315 ctx->flush = Z_FINISH;
258 316
290 348
291 } else { 349 } else {
292 break; 350 break;
293 } 351 }
294 352
295 ctx->zstream.next_out = (unsigned char *) ctx->out_hunk->pos; 353 ctx->zstream.next_out = (u_char *) ctx->out_hunk->pos;
296 ctx->zstream.avail_out = conf->bufs.size; 354 ctx->zstream.avail_out = conf->bufs.size;
297 } 355 }
298 356
299 ngx_log_debug(r->connection->log, "deflate(): %08x %08x %d %d %d" _ 357 ngx_log_debug(r->connection->log, "deflate(): %08x %08x %d %d %d" _
300 ctx->zstream.next_in _ ctx->zstream.next_out _ 358 ctx->zstream.next_in _ ctx->zstream.next_out _
335 ctx->last_out = &cl->next; 393 ctx->last_out = &cl->next;
336 394
337 break; 395 break;
338 396
339 } else if (ctx->flush == Z_FINISH) { 397 } else if (ctx->flush == Z_FINISH) {
398
340 /* rc == Z_STREAM_END */ 399 /* rc == Z_STREAM_END */
341 400
342 zin = ctx->zstream.total_in; 401 zin = ctx->zstream.total_in;
343 zout = 10 + ctx->zstream.total_out + 8; 402 zout = 10 + ctx->zstream.total_out + 8;
344 403
346 if (rc != Z_OK) { 405 if (rc != Z_OK) {
347 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 406 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
348 "deflateEnd() failed: %d", rc); 407 "deflateEnd() failed: %d", rc);
349 return ngx_http_gzip_error(ctx); 408 return ngx_http_gzip_error(ctx);
350 } 409 }
410
411 ngx_pfree(r->pool, ctx->preallocated);
351 412
352 ctx->flush = Z_NO_FLUSH; 413 ctx->flush = Z_NO_FLUSH;
353 414
354 ngx_alloc_link_and_set_hunk(cl, ctx->out_hunk, r->pool, 415 ngx_alloc_link_and_set_hunk(cl, ctx->out_hunk, r->pool,
355 ngx_http_gzip_error(ctx)); 416 ngx_http_gzip_error(ctx));
383 /* STUB */ 444 /* STUB */
384 #endif 445 #endif
385 446
386 ctx->zstream.avail_in = 0; 447 ctx->zstream.avail_in = 0;
387 ctx->zstream.avail_out = 0; 448 ctx->zstream.avail_out = 0;
388 #if 0 449
389 ngx_free(ctx->alloc);
390 #endif
391 ngx_http_delete_ctx(r, ngx_http_gzip_filter_module); 450 ngx_http_delete_ctx(r, ngx_http_gzip_filter_module);
392 451
393 break; 452 break;
394 453
395 } else if (conf->no_buffer && ctx->in == NULL) { 454 } else if (conf->no_buffer && ctx->in == NULL) {
418 ctx->last_out = &ctx->out; 477 ctx->last_out = &ctx->out;
419 } 478 }
420 } 479 }
421 480
422 481
482 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
483 {
484 ngx_http_gzip_ctx_t *ctx = opaque;
485
486 int alloc;
487 void *p;
488
489
490 alloc = items * size;
491 if (alloc % 512 != 0) {
492
493 /* we allocate 8K for zlib deflate_state (~6K) */
494 /* TODO: PAGE_SIZE */
495
496 alloc = (alloc + 4095) & ~4095;
497 }
498
499 if (alloc <= ctx->allocated) {
500 p = ctx->free_mem;
501 ctx->free_mem += alloc;
502 ctx->allocated -= alloc;
503
504 #if 0
505 ngx_log_debug(ctx->request->connection->log, "ALLOC: %d:%d:%d:%08X" _
506 items _ size _ alloc _ p);
507 #endif
508
509 return p;
510 }
511
512 ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
513 "gzip filter failed to use preallocated memory: %d of %d",
514 items * size, ctx->allocated);
515
516 p = ngx_palloc(ctx->request->pool, items * size);
517
518 return p;
519 }
520
521
522 static void ngx_http_gzip_filter_free(void *opaque, void *address)
523 {
524 ngx_http_gzip_ctx_t *ctx = opaque;
525
526 #if 0
527 ngx_log_debug(ctx->request->connection->log, "FREE: %08X" _ address);
528 #endif
529 }
530
531
423 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) 532 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
424 { 533 {
425 #if 0
426 ngx_free(ctx->alloc);
427 #else
428 deflateEnd(&ctx->zstream); 534 deflateEnd(&ctx->zstream);
429 #endif 535
536 ngx_pfree(ctx->request->pool, ctx->preallocated);
430 537
431 ctx->zstream.avail_in = 0; 538 ctx->zstream.avail_in = 0;
432 ctx->zstream.avail_out = 0; 539 ctx->zstream.avail_out = 0;
433 540
434 return NGX_ERROR; 541 return NGX_ERROR;
454 ngx_test_null(conf, 561 ngx_test_null(conf,
455 ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)), 562 ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)),
456 NGX_CONF_ERROR); 563 NGX_CONF_ERROR);
457 564
458 conf->enable = NGX_CONF_UNSET; 565 conf->enable = NGX_CONF_UNSET;
459 /* conf->bufs.num = 0; */ 566 /* conf->bufs.num = 0; */
460 conf->no_buffer = NGX_CONF_UNSET; 567 conf->no_buffer = NGX_CONF_UNSET;
568 conf->level = NGX_CONF_UNSET;
569 conf->wbits = NGX_CONF_UNSET;
570 conf->memlevel = NGX_CONF_UNSET;
461 571
462 return conf; 572 return conf;
463 } 573 }
464 574
465 575
470 ngx_http_gzip_conf_t *conf = child; 580 ngx_http_gzip_conf_t *conf = child;
471 581
472 ngx_conf_merge_value(conf->enable, prev->enable, 0); 582 ngx_conf_merge_value(conf->enable, prev->enable, 0);
473 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, 583 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4,
474 /* STUB: PAGE_SIZE */ 4096); 584 /* STUB: PAGE_SIZE */ 4096);
585 ngx_conf_merge_value(conf->level, prev->level, 1);
586 ngx_conf_merge_value(conf->wbits, prev->wbits, MAX_WBITS);
587 ngx_conf_merge_value(conf->memlevel, prev->memlevel, MAX_MEM_LEVEL - 1);
475 ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); 588 ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
476 589
477 return NGX_CONF_OK; 590 return NGX_CONF_OK;
478 } 591 }
592
593
594 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, ngx_command_t *cmd,
595 void *conf)
596 {
597 ngx_http_gzip_conf_t *lcf = conf;
598
599 int wbits, wsize;
600 char *rv;
601
602
603 rv = ngx_conf_set_size_slot(cf, cmd, conf);
604 if (rv) {
605 return rv;
606 }
607
608 ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "WBITS: %d", lcf->wbits);
609
610 wbits = 15;
611 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
612
613 if (wsize == lcf->wbits) {
614 lcf->wbits = wbits;
615 ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "WBITS: %d", lcf->wbits);
616 return NULL;
617 }
618
619 wbits--;
620 }
621
622 return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
623 }
624
625
626 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, ngx_command_t *cmd,
627 void *conf)
628 {
629 ngx_http_gzip_conf_t *lcf = conf;
630
631 int memlevel, hsize;
632 char *rv;
633
634
635 rv = ngx_conf_set_size_slot(cf, cmd, conf);
636 if (rv) {
637 return rv;
638 }
639
640 ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "MEMLEVEL: %d", lcf->memlevel);
641
642 memlevel = 9;
643 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
644
645 if (hsize == lcf->memlevel) {
646 lcf->memlevel = memlevel;
647 ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "MEMLEVEL: %d", lcf->memlevel);
648 return NULL;
649 }
650
651 memlevel--;
652 }
653
654 return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
655 }