Mercurial > hg > nginx-vendor-0-7
comparison src/http/modules/ngx_http_gzip_filter.c @ 0:f0b350454894 NGINX_0_1_0
nginx 0.1.0
*) The first public version.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 04 Oct 2004 00:00:00 +0400 |
parents | |
children | cc9f381affaa |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f0b350454894 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 #include <zlib.h> | |
12 | |
13 | |
14 typedef struct { | |
15 ngx_flag_t enable; | |
16 ngx_flag_t no_buffer; | |
17 | |
18 ngx_bufs_t bufs; | |
19 | |
20 ngx_uint_t http_version; | |
21 ngx_uint_t proxied; | |
22 | |
23 int level; | |
24 size_t wbits; | |
25 size_t memlevel; | |
26 ssize_t min_length; | |
27 } ngx_http_gzip_conf_t; | |
28 | |
29 | |
30 #define NGX_HTTP_GZIP_PROXIED_OFF 0x0002 | |
31 #define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004 | |
32 #define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008 | |
33 #define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010 | |
34 #define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020 | |
35 #define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040 | |
36 #define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080 | |
37 #define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100 | |
38 #define NGX_HTTP_GZIP_PROXIED_ANY 0x0200 | |
39 | |
40 | |
41 typedef struct { | |
42 ngx_chain_t *in; | |
43 ngx_chain_t *free; | |
44 ngx_chain_t *busy; | |
45 ngx_chain_t *out; | |
46 ngx_chain_t **last_out; | |
47 ngx_buf_t *in_buf; | |
48 ngx_buf_t *out_buf; | |
49 ngx_int_t bufs; | |
50 | |
51 off_t length; | |
52 | |
53 void *preallocated; | |
54 char *free_mem; | |
55 ngx_uint_t allocated; | |
56 | |
57 unsigned flush:4; | |
58 unsigned redo:1; | |
59 unsigned done:1; | |
60 #if 0 | |
61 unsigned pass:1; | |
62 unsigned blocked:1; | |
63 #endif | |
64 | |
65 size_t zin; | |
66 size_t zout; | |
67 | |
68 uint32_t crc32; | |
69 z_stream zstream; | |
70 ngx_http_request_t *request; | |
71 } ngx_http_gzip_ctx_t; | |
72 | |
73 | |
74 static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r, | |
75 ngx_http_gzip_conf_t *conf); | |
76 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, | |
77 u_int size); | |
78 static void ngx_http_gzip_filter_free(void *opaque, void *address); | |
79 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx); | |
80 | |
81 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf, | |
82 uintptr_t data); | |
83 | |
84 static ngx_int_t ngx_http_gzip_pre_conf(ngx_conf_t *cf); | |
85 static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle); | |
86 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf); | |
87 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, | |
88 void *parent, void *child); | |
89 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data); | |
90 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data); | |
91 | |
92 | |
93 static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = { | |
94 ngx_conf_check_num_bounds, 1, 9 | |
95 }; | |
96 | |
97 static ngx_conf_post_handler_pt ngx_http_gzip_set_window_p = | |
98 ngx_http_gzip_set_window; | |
99 static ngx_conf_post_handler_pt ngx_http_gzip_set_hash_p = | |
100 ngx_http_gzip_set_hash; | |
101 | |
102 | |
103 | |
104 static ngx_conf_enum_t ngx_http_gzip_http_version[] = { | |
105 { ngx_string("1.0"), NGX_HTTP_VERSION_10 }, | |
106 { ngx_string("1.1"), NGX_HTTP_VERSION_11 }, | |
107 { ngx_null_string, 0 } | |
108 }; | |
109 | |
110 | |
111 static ngx_conf_bitmask_t ngx_http_gzip_proxied_mask[] = { | |
112 { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF }, | |
113 { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED }, | |
114 { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE }, | |
115 { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE }, | |
116 { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE }, | |
117 { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM }, | |
118 { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG }, | |
119 { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH }, | |
120 { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY }, | |
121 { ngx_null_string, 0 } | |
122 }; | |
123 | |
124 | |
125 static ngx_command_t ngx_http_gzip_filter_commands[] = { | |
126 | |
127 { ngx_string("gzip"), | |
128 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
129 ngx_conf_set_flag_slot, | |
130 NGX_HTTP_LOC_CONF_OFFSET, | |
131 offsetof(ngx_http_gzip_conf_t, enable), | |
132 NULL }, | |
133 | |
134 { ngx_string("gzip_buffers"), | |
135 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, | |
136 ngx_conf_set_bufs_slot, | |
137 NGX_HTTP_LOC_CONF_OFFSET, | |
138 offsetof(ngx_http_gzip_conf_t, bufs), | |
139 NULL }, | |
140 | |
141 { ngx_string("gzip_comp_level"), | |
142 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
143 ngx_conf_set_num_slot, | |
144 NGX_HTTP_LOC_CONF_OFFSET, | |
145 offsetof(ngx_http_gzip_conf_t, level), | |
146 &ngx_http_gzip_comp_level_bounds }, | |
147 | |
148 { ngx_string("gzip_window"), | |
149 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
150 ngx_conf_set_size_slot, | |
151 NGX_HTTP_LOC_CONF_OFFSET, | |
152 offsetof(ngx_http_gzip_conf_t, wbits), | |
153 &ngx_http_gzip_set_window_p }, | |
154 | |
155 { ngx_string("gzip_hash"), | |
156 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
157 ngx_conf_set_size_slot, | |
158 NGX_HTTP_LOC_CONF_OFFSET, | |
159 offsetof(ngx_http_gzip_conf_t, memlevel), | |
160 &ngx_http_gzip_set_hash_p }, | |
161 | |
162 { ngx_string("gzip_no_buffer"), | |
163 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
164 ngx_conf_set_flag_slot, | |
165 NGX_HTTP_LOC_CONF_OFFSET, | |
166 offsetof(ngx_http_gzip_conf_t, no_buffer), | |
167 NULL }, | |
168 | |
169 { ngx_string("gzip_http_version"), | |
170 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY, | |
171 ngx_conf_set_enum_slot, | |
172 NGX_HTTP_LOC_CONF_OFFSET, | |
173 offsetof(ngx_http_gzip_conf_t, http_version), | |
174 &ngx_http_gzip_http_version }, | |
175 | |
176 { ngx_string("gzip_proxied"), | |
177 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_ANY, | |
178 ngx_conf_set_bitmask_slot, | |
179 NGX_HTTP_LOC_CONF_OFFSET, | |
180 offsetof(ngx_http_gzip_conf_t, proxied), | |
181 &ngx_http_gzip_proxied_mask }, | |
182 | |
183 { ngx_string("gzip_min_length"), | |
184 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
185 ngx_conf_set_size_slot, | |
186 NGX_HTTP_LOC_CONF_OFFSET, | |
187 offsetof(ngx_http_gzip_conf_t, min_length), | |
188 NULL }, | |
189 | |
190 ngx_null_command | |
191 }; | |
192 | |
193 | |
194 static ngx_http_module_t ngx_http_gzip_filter_module_ctx = { | |
195 ngx_http_gzip_pre_conf, /* pre conf */ | |
196 | |
197 NULL, /* create main configuration */ | |
198 NULL, /* init main configuration */ | |
199 | |
200 NULL, /* create server configuration */ | |
201 NULL, /* merge server configuration */ | |
202 | |
203 ngx_http_gzip_create_conf, /* create location configuration */ | |
204 ngx_http_gzip_merge_conf, /* merge location configuration */ | |
205 }; | |
206 | |
207 | |
208 ngx_module_t ngx_http_gzip_filter_module = { | |
209 NGX_MODULE, | |
210 &ngx_http_gzip_filter_module_ctx, /* module context */ | |
211 ngx_http_gzip_filter_commands, /* module directives */ | |
212 NGX_HTTP_MODULE, /* module type */ | |
213 ngx_http_gzip_filter_init, /* init module */ | |
214 NULL /* init child */ | |
215 }; | |
216 | |
217 | |
218 static ngx_http_log_op_name_t ngx_http_gzip_log_fmt_ops[] = { | |
219 { ngx_string("gzip_ratio"), NGX_INT32_LEN + 3, ngx_http_gzip_log_ratio }, | |
220 { ngx_null_string, 0, NULL } | |
221 }; | |
222 | |
223 | |
224 | |
225 static u_char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 }; | |
226 | |
227 #if (HAVE_LITTLE_ENDIAN) | |
228 | |
229 struct gztrailer { | |
230 uint32_t crc32; | |
231 uint32_t zlen; | |
232 }; | |
233 | |
234 #else /* HAVE_BIG_ENDIAN */ | |
235 | |
236 struct gztrailer { | |
237 u_char crc32[4]; | |
238 u_char zlen[4]; | |
239 }; | |
240 | |
241 #endif | |
242 | |
243 | |
244 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | |
245 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; | |
246 | |
247 | |
248 static ngx_int_t ngx_http_gzip_header_filter(ngx_http_request_t *r) | |
249 { | |
250 ngx_http_gzip_ctx_t *ctx; | |
251 ngx_http_gzip_conf_t *conf; | |
252 | |
253 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); | |
254 | |
255 if (!conf->enable | |
256 || r->headers_out.status != NGX_HTTP_OK | |
257 || r->header_only | |
258 || r->http_version < conf->http_version | |
259 || (r->headers_out.content_encoding | |
260 && r->headers_out.content_encoding->value.len) | |
261 || r->headers_in.accept_encoding == NULL | |
262 || (r->headers_out.content_length_n != -1 | |
263 && r->headers_out.content_length_n < conf->min_length) | |
264 || ngx_strstr(r->headers_in.accept_encoding->value.data, "gzip") == NULL | |
265 ) | |
266 { | |
267 return ngx_http_next_header_filter(r); | |
268 } | |
269 | |
270 /* TODO: "text/html" -> custom types */ | |
271 if (r->headers_out.content_type | |
272 && ngx_strncasecmp(r->headers_out.content_type->value.data, | |
273 "text/html", 9) != 0) | |
274 { | |
275 return ngx_http_next_header_filter(r); | |
276 } | |
277 | |
278 | |
279 if (r->headers_in.via) { | |
280 if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) { | |
281 return ngx_http_next_header_filter(r); | |
282 } | |
283 | |
284 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_ANY) | |
285 && ngx_http_gzip_proxied(r, conf) == NGX_DECLINED) | |
286 { | |
287 return ngx_http_next_header_filter(r); | |
288 } | |
289 } | |
290 | |
291 | |
292 /* | |
293 * if the URL (without the "http://" prefix) is longer than 253 bytes | |
294 * then MSIE 4.x can not handle the compressed stream - it waits too long, | |
295 * hangs up or crashes | |
296 */ | |
297 | |
298 if (r->headers_in.msie4 && r->unparsed_uri.len > 200) { | |
299 return ngx_http_next_header_filter(r); | |
300 } | |
301 | |
302 | |
303 ngx_http_create_ctx(r, ctx, ngx_http_gzip_filter_module, | |
304 sizeof(ngx_http_gzip_ctx_t), NGX_ERROR); | |
305 ctx->request = r; | |
306 | |
307 r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers); | |
308 if (r->headers_out.content_encoding == NULL) { | |
309 return NGX_ERROR; | |
310 } | |
311 | |
312 r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1; | |
313 r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding"; | |
314 r->headers_out.content_encoding->value.len = sizeof("gzip") - 1; | |
315 r->headers_out.content_encoding->value.data = (u_char *) "gzip"; | |
316 | |
317 ctx->length = r->headers_out.content_length_n; | |
318 r->headers_out.content_length_n = -1; | |
319 if (r->headers_out.content_length) { | |
320 r->headers_out.content_length->key.len = 0; | |
321 r->headers_out.content_length = NULL; | |
322 } | |
323 r->filter_need_in_memory = 1; | |
324 | |
325 return ngx_http_next_header_filter(r); | |
326 } | |
327 | |
328 | |
329 static ngx_int_t ngx_http_gzip_proxied(ngx_http_request_t *r, | |
330 ngx_http_gzip_conf_t *conf) | |
331 { | |
332 time_t date, expires; | |
333 | |
334 if (r->headers_in.authorization | |
335 && (conf->proxied & NGX_HTTP_GZIP_PROXIED_AUTH)) | |
336 { | |
337 return NGX_OK; | |
338 } | |
339 | |
340 if (r->headers_out.expires) { | |
341 | |
342 if (!(conf->proxied & NGX_HTTP_GZIP_PROXIED_EXPIRED)) { | |
343 return NGX_DECLINED; | |
344 } | |
345 | |
346 expires = ngx_http_parse_time(r->headers_out.expires->value.data, | |
347 r->headers_out.expires->value.len); | |
348 if (expires == NGX_ERROR) { | |
349 return NGX_DECLINED; | |
350 } | |
351 | |
352 if (r->headers_out.date) { | |
353 date = ngx_http_parse_time(r->headers_out.date->value.data, | |
354 r->headers_out.date->value.len); | |
355 if (date == NGX_ERROR) { | |
356 return NGX_DECLINED; | |
357 } | |
358 | |
359 } else { | |
360 date = ngx_time(); | |
361 } | |
362 | |
363 if (expires < date) { | |
364 return NGX_OK; | |
365 } | |
366 | |
367 return NGX_DECLINED; | |
368 } | |
369 | |
370 if (r->headers_out.cache_control) { | |
371 | |
372 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_CACHE) | |
373 && ngx_strstr(r->headers_out.cache_control->value.data, "no-cache")) | |
374 { | |
375 return NGX_OK; | |
376 } | |
377 | |
378 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_STORE) | |
379 && ngx_strstr(r->headers_out.cache_control->value.data, "no-store")) | |
380 { | |
381 return NGX_OK; | |
382 } | |
383 | |
384 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_PRIVATE) | |
385 && ngx_strstr(r->headers_out.cache_control->value.data, "private")) | |
386 { | |
387 return NGX_OK; | |
388 } | |
389 | |
390 return NGX_DECLINED; | |
391 } | |
392 | |
393 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_LM) | |
394 && r->headers_out.last_modified) | |
395 { | |
396 return NGX_DECLINED; | |
397 } | |
398 | |
399 if ((conf->proxied & NGX_HTTP_GZIP_PROXIED_NO_ETAG) | |
400 && r->headers_out.etag) | |
401 { | |
402 return NGX_DECLINED; | |
403 } | |
404 | |
405 return NGX_OK; | |
406 } | |
407 | |
408 | |
409 static ngx_int_t ngx_http_gzip_body_filter(ngx_http_request_t *r, | |
410 ngx_chain_t *in) | |
411 { | |
412 int rc, wbits, memlevel, last; | |
413 struct gztrailer *trailer; | |
414 ngx_buf_t *b; | |
415 ngx_chain_t *cl; | |
416 ngx_http_gzip_ctx_t *ctx; | |
417 ngx_http_gzip_conf_t *conf; | |
418 | |
419 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); | |
420 | |
421 if (ctx == NULL || ctx->done) { | |
422 return ngx_http_next_body_filter(r, in); | |
423 } | |
424 | |
425 conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); | |
426 | |
427 if (ctx->preallocated == NULL) { | |
428 wbits = conf->wbits; | |
429 memlevel = conf->memlevel; | |
430 | |
431 if (ctx->length > 0) { | |
432 | |
433 /* the actual zlib window size is smaller by 262 bytes */ | |
434 | |
435 while (ctx->length < ((1 << (wbits - 1)) - 262)) { | |
436 wbits--; | |
437 memlevel--; | |
438 } | |
439 } | |
440 | |
441 /* | |
442 * We preallocate a memory for zlib in one buffer (200K-400K), this | |
443 * dicreases a number of malloc() and free() calls and also probably | |
444 * dicreases a number of syscalls (sbrk() or so). | |
445 * Besides we free this memory as soon as the gzipping will complete | |
446 * and do not wait while a whole response will be sent to a client. | |
447 * | |
448 * 8K is for zlib deflate_state, it takes | |
449 * * 5816 bytes on x86 and sparc64 (32-bit mode) | |
450 * * 5920 bytes on amd64 and sparc64 | |
451 */ | |
452 | |
453 ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9)); | |
454 | |
455 if (!(ctx->preallocated = ngx_palloc(r->pool, ctx->allocated))) { | |
456 return NGX_ERROR; | |
457 } | |
458 | |
459 ctx->free_mem = ctx->preallocated; | |
460 | |
461 ctx->zstream.zalloc = ngx_http_gzip_filter_alloc; | |
462 ctx->zstream.zfree = ngx_http_gzip_filter_free; | |
463 ctx->zstream.opaque = ctx; | |
464 | |
465 rc = deflateInit2(&ctx->zstream, conf->level, Z_DEFLATED, | |
466 -wbits, memlevel, Z_DEFAULT_STRATEGY); | |
467 | |
468 if (rc != Z_OK) { | |
469 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
470 "deflateInit2() failed: %d", rc); | |
471 return ngx_http_gzip_error(ctx); | |
472 } | |
473 | |
474 if (!(b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)))) { | |
475 return ngx_http_gzip_error(ctx); | |
476 } | |
477 | |
478 b->memory = 1; | |
479 b->pos = gzheader; | |
480 b->last = b->pos + 10; | |
481 | |
482 ngx_alloc_link_and_set_buf(cl, b, r->pool, ngx_http_gzip_error(ctx)); | |
483 ctx->out = cl; | |
484 ctx->last_out = &cl->next; | |
485 | |
486 ctx->crc32 = crc32(0L, Z_NULL, 0); | |
487 ctx->flush = Z_NO_FLUSH; | |
488 } | |
489 | |
490 if (in) { | |
491 if (ngx_chain_add_copy(r->pool, &ctx->in, in) == NGX_ERROR) { | |
492 return ngx_http_gzip_error(ctx); | |
493 } | |
494 } | |
495 | |
496 last = NGX_NONE; | |
497 | |
498 for ( ;; ) { | |
499 | |
500 for ( ;; ) { | |
501 | |
502 /* does zlib need a new data ? */ | |
503 | |
504 if (ctx->zstream.avail_in == 0 | |
505 && ctx->flush == Z_NO_FLUSH | |
506 && !ctx->redo) | |
507 { | |
508 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
509 "gzip in: " PTR_FMT, ctx->in); | |
510 | |
511 if (ctx->in == NULL) { | |
512 break; | |
513 } | |
514 | |
515 ctx->in_buf = ctx->in->buf; | |
516 ctx->in = ctx->in->next; | |
517 | |
518 ctx->zstream.next_in = ctx->in_buf->pos; | |
519 ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos; | |
520 | |
521 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
522 "gzip in_buf:" PTR_FMT " ni:" PTR_FMT " ai:%d", | |
523 ctx->in_buf, | |
524 ctx->zstream.next_in, ctx->zstream.avail_in); | |
525 | |
526 /* STUB */ | |
527 if (ctx->in_buf->last < ctx->in_buf->pos) { | |
528 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
529 "zstream.avail_in is huge"); | |
530 ctx->done = 1; | |
531 return NGX_ERROR; | |
532 } | |
533 /**/ | |
534 | |
535 if (ctx->in_buf->last_buf) { | |
536 ctx->flush = Z_FINISH; | |
537 | |
538 } else if (ctx->in_buf->flush) { | |
539 ctx->flush = Z_SYNC_FLUSH; | |
540 } | |
541 | |
542 if (ctx->zstream.avail_in == 0) { | |
543 if (ctx->flush == Z_NO_FLUSH) { | |
544 continue; | |
545 } | |
546 | |
547 } else { | |
548 ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in, | |
549 ctx->zstream.avail_in); | |
550 } | |
551 } | |
552 | |
553 | |
554 /* is there a space for the gzipped data ? */ | |
555 | |
556 if (ctx->zstream.avail_out == 0) { | |
557 | |
558 if (ctx->free) { | |
559 ctx->out_buf = ctx->free->buf; | |
560 ctx->free = ctx->free->next; | |
561 | |
562 } else if (ctx->bufs < conf->bufs.num) { | |
563 ctx->out_buf = ngx_create_temp_buf(r->pool, | |
564 conf->bufs.size); | |
565 if (ctx->out_buf == NULL) { | |
566 return ngx_http_gzip_error(ctx); | |
567 } | |
568 | |
569 ctx->out_buf->tag = (ngx_buf_tag_t) | |
570 &ngx_http_gzip_filter_module; | |
571 ctx->out_buf->recycled = 1; | |
572 ctx->bufs++; | |
573 | |
574 } else { | |
575 #if 0 | |
576 ctx->blocked = 1; | |
577 #endif | |
578 break; | |
579 } | |
580 | |
581 #if 0 | |
582 ctx->blocked = 0; | |
583 #endif | |
584 ctx->zstream.next_out = ctx->out_buf->pos; | |
585 ctx->zstream.avail_out = conf->bufs.size; | |
586 } | |
587 | |
588 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
589 "deflate in: ni:%X no:%X ai:%d ao:%d fl:%d redo:%d", | |
590 ctx->zstream.next_in, ctx->zstream.next_out, | |
591 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
592 ctx->flush, ctx->redo); | |
593 | |
594 rc = deflate(&ctx->zstream, ctx->flush); | |
595 | |
596 if (rc != Z_OK && rc != Z_STREAM_END) { | |
597 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
598 "deflate() failed: %d, %d", ctx->flush, rc); | |
599 return ngx_http_gzip_error(ctx); | |
600 } | |
601 | |
602 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
603 "deflate out: ni:%X no:%X ai:%d ao:%d rc:%d", | |
604 ctx->zstream.next_in, ctx->zstream.next_out, | |
605 ctx->zstream.avail_in, ctx->zstream.avail_out, | |
606 rc); | |
607 | |
608 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
609 "gzip in_buf:" PTR_FMT " pos:" PTR_FMT, | |
610 ctx->in_buf, ctx->in_buf->pos); | |
611 | |
612 | |
613 if (ctx->zstream.next_in) { | |
614 ctx->in_buf->pos = ctx->zstream.next_in; | |
615 | |
616 if (ctx->zstream.avail_in == 0) { | |
617 ctx->zstream.next_in = NULL; | |
618 } | |
619 } | |
620 | |
621 ctx->out_buf->last = ctx->zstream.next_out; | |
622 | |
623 if (ctx->zstream.avail_out == 0) { | |
624 | |
625 /* zlib wants to output some more gzipped data */ | |
626 | |
627 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, | |
628 ngx_http_gzip_error(ctx)); | |
629 *ctx->last_out = cl; | |
630 ctx->last_out = &cl->next; | |
631 | |
632 ctx->redo = 1; | |
633 | |
634 continue; | |
635 } | |
636 | |
637 ctx->redo = 0; | |
638 | |
639 if (ctx->flush == Z_SYNC_FLUSH) { | |
640 | |
641 ctx->out_buf->flush = 0; | |
642 ctx->flush = Z_NO_FLUSH; | |
643 | |
644 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, | |
645 ngx_http_gzip_error(ctx)); | |
646 *ctx->last_out = cl; | |
647 ctx->last_out = &cl->next; | |
648 | |
649 #if 0 | |
650 ctx->pass = 1; | |
651 #endif | |
652 | |
653 break; | |
654 } | |
655 | |
656 if (rc == Z_STREAM_END) { | |
657 | |
658 ctx->zin = ctx->zstream.total_in; | |
659 ctx->zout = 10 + ctx->zstream.total_out + 8; | |
660 | |
661 rc = deflateEnd(&ctx->zstream); | |
662 | |
663 if (rc != Z_OK) { | |
664 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
665 "deflateEnd() failed: %d", rc); | |
666 return ngx_http_gzip_error(ctx); | |
667 } | |
668 | |
669 ngx_pfree(r->pool, ctx->preallocated); | |
670 | |
671 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, | |
672 ngx_http_gzip_error(ctx)); | |
673 *ctx->last_out = cl; | |
674 ctx->last_out = &cl->next; | |
675 | |
676 if (ctx->zstream.avail_out >= 8) { | |
677 trailer = (struct gztrailer *) ctx->out_buf->last; | |
678 ctx->out_buf->last += 8; | |
679 ctx->out_buf->last_buf = 1; | |
680 | |
681 } else { | |
682 if (!(b = ngx_create_temp_buf(r->pool, 8))) { | |
683 return ngx_http_gzip_error(ctx); | |
684 } | |
685 | |
686 b->last_buf = 1; | |
687 | |
688 ngx_alloc_link_and_set_buf(cl, b, r->pool, | |
689 ngx_http_gzip_error(ctx)); | |
690 *ctx->last_out = cl; | |
691 ctx->last_out = &cl->next; | |
692 trailer = (struct gztrailer *) b->pos; | |
693 b->last += 8; | |
694 } | |
695 | |
696 #if (HAVE_LITTLE_ENDIAN) | |
697 trailer->crc32 = ctx->crc32; | |
698 trailer->zlen = ctx->zin; | |
699 #else | |
700 trailer->crc32[0] = ctx->crc32 & 0xff; | |
701 trailer->crc32[1] = (ctx->crc32 >> 8) & 0xff; | |
702 trailer->crc32[2] = (ctx->crc32 >> 16) & 0xff; | |
703 trailer->crc32[3] = (ctx->crc32 >> 24) & 0xff; | |
704 | |
705 trailer->zlen[0] = ctx->zin & 0xff; | |
706 trailer->zlen[1] = (ctx->zin >> 8) & 0xff; | |
707 trailer->zlen[2] = (ctx->zin >> 16) & 0xff; | |
708 trailer->zlen[3] = (ctx->zin >> 24) & 0xff; | |
709 #endif | |
710 | |
711 ctx->zstream.avail_in = 0; | |
712 ctx->zstream.avail_out = 0; | |
713 | |
714 ctx->done = 1; | |
715 #if 0 | |
716 ctx->pass = 1; | |
717 #endif | |
718 | |
719 break; | |
720 } | |
721 | |
722 if (conf->no_buffer && ctx->in == NULL) { | |
723 ngx_alloc_link_and_set_buf(cl, ctx->out_buf, r->pool, | |
724 ngx_http_gzip_error(ctx)); | |
725 *ctx->last_out = cl; | |
726 ctx->last_out = &cl->next; | |
727 | |
728 #if 0 | |
729 ctx->pass = 1; | |
730 #endif | |
731 | |
732 break; | |
733 } | |
734 } | |
735 | |
736 #if 0 | |
737 | |
738 /* OLD CODE */ | |
739 | |
740 if (ctx->out) { | |
741 if (ctx->pass) { | |
742 ctx->pass = 0; | |
743 | |
744 } else if (last == NGX_AGAIN) { | |
745 return last; | |
746 } | |
747 | |
748 } else if (ctx->busy->buf && ngx_buf_size(ctx->busy->buf)) { | |
749 if (last != NGX_NONE) { | |
750 return last; | |
751 } | |
752 | |
753 } else if (ctx->blocked) { | |
754 if (last != NGX_NONE) { | |
755 return last; | |
756 } | |
757 | |
758 } else { | |
759 if (last == NGX_NONE) { | |
760 return NGX_OK; | |
761 } | |
762 | |
763 return last; | |
764 } | |
765 #endif | |
766 | |
767 /* NEW CODE */ | |
768 | |
769 if (last == NGX_AGAIN) { | |
770 return NGX_AGAIN; | |
771 } | |
772 | |
773 if (ctx->out == NULL && ctx->busy == NULL) { | |
774 return NGX_OK; | |
775 } | |
776 | |
777 /**/ | |
778 | |
779 last = ngx_http_next_body_filter(r, ctx->out); | |
780 | |
781 /* | |
782 * we do not check NGX_AGAIN here because the downstream filters | |
783 * may free some buffers and zlib may compress some data into them | |
784 */ | |
785 | |
786 if (last == NGX_ERROR) { | |
787 return ngx_http_gzip_error(ctx); | |
788 } | |
789 | |
790 ngx_chain_update_chains(&ctx->free, &ctx->busy, &ctx->out, | |
791 (ngx_buf_tag_t) &ngx_http_gzip_filter_module); | |
792 ctx->last_out = &ctx->out; | |
793 | |
794 if (ctx->done) { | |
795 return last; | |
796 } | |
797 } | |
798 } | |
799 | |
800 | |
801 static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size) | |
802 { | |
803 ngx_http_gzip_ctx_t *ctx = opaque; | |
804 | |
805 void *p; | |
806 ngx_uint_t alloc; | |
807 | |
808 alloc = items * size; | |
809 if (alloc % 512 != 0) { | |
810 | |
811 /* | |
812 * the zlib deflate_state allocation, it takes about 6K, we allocate 8K | |
813 */ | |
814 | |
815 alloc = (alloc + ngx_pagesize - 1) & ~(ngx_pagesize - 1); | |
816 } | |
817 | |
818 if (alloc <= ctx->allocated) { | |
819 p = ctx->free_mem; | |
820 ctx->free_mem += alloc; | |
821 ctx->allocated -= alloc; | |
822 | |
823 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, | |
824 "gzip alloc: n:%d s:%d a:%d p:" PTR_FMT, | |
825 items, size, alloc, p); | |
826 | |
827 return p; | |
828 } | |
829 | |
830 ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0, | |
831 "gzip filter failed to use preallocated memory: %d of %d", | |
832 items * size, ctx->allocated); | |
833 | |
834 p = ngx_palloc(ctx->request->pool, items * size); | |
835 | |
836 return p; | |
837 } | |
838 | |
839 | |
840 static void ngx_http_gzip_filter_free(void *opaque, void *address) | |
841 { | |
842 #if 0 | |
843 ngx_http_gzip_ctx_t *ctx = opaque; | |
844 | |
845 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0, | |
846 "gzip free: %X", address); | |
847 #endif | |
848 } | |
849 | |
850 | |
851 static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf, | |
852 uintptr_t data) | |
853 { | |
854 ngx_uint_t zint, zfrac; | |
855 ngx_http_gzip_ctx_t *ctx; | |
856 | |
857 ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module); | |
858 | |
859 if (ctx == NULL || ctx->zout == 0) { | |
860 *buf = '-'; | |
861 return buf + 1; | |
862 } | |
863 | |
864 #if 0 | |
865 return buf + ngx_snprintf((char *) buf, NGX_INT32_LEN + 4, "%.2f", | |
866 (float) ctx->zin / ctx->zout); | |
867 #endif | |
868 | |
869 /* we prefer do not use FPU */ | |
870 | |
871 zint = (ngx_uint_t) (ctx->zin / ctx->zout); | |
872 zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100); | |
873 | |
874 if ((ctx->zin * 1000 / ctx->zout) %10 > 4) { | |
875 if (++zfrac > 99) { | |
876 zint++; | |
877 zfrac = 0; | |
878 } | |
879 } | |
880 | |
881 return buf + ngx_snprintf((char *) buf, NGX_INT32_LEN + 4, | |
882 "%" NGX_UINT_T_FMT ".%02" NGX_UINT_T_FMT, | |
883 zint, zfrac); | |
884 } | |
885 | |
886 | |
887 ngx_inline static int ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx) | |
888 { | |
889 deflateEnd(&ctx->zstream); | |
890 | |
891 ngx_pfree(ctx->request->pool, ctx->preallocated); | |
892 | |
893 ctx->zstream.avail_in = 0; | |
894 ctx->zstream.avail_out = 0; | |
895 | |
896 ctx->done = 1; | |
897 | |
898 return NGX_ERROR; | |
899 } | |
900 | |
901 | |
902 static ngx_int_t ngx_http_gzip_pre_conf(ngx_conf_t *cf) | |
903 { | |
904 ngx_http_log_op_name_t *op; | |
905 | |
906 for (op = ngx_http_gzip_log_fmt_ops; op->name.len; op++) { /* void */ } | |
907 op->op = NULL; | |
908 | |
909 op = ngx_http_log_fmt_ops; | |
910 | |
911 for (op = ngx_http_log_fmt_ops; op->op; op++) { | |
912 if (op->name.len == 0) { | |
913 op = (ngx_http_log_op_name_t *) op->op; | |
914 } | |
915 } | |
916 | |
917 op->op = (ngx_http_log_op_pt) ngx_http_gzip_log_fmt_ops; | |
918 | |
919 return NGX_OK; | |
920 } | |
921 | |
922 | |
923 static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle) | |
924 { | |
925 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
926 ngx_http_top_header_filter = ngx_http_gzip_header_filter; | |
927 | |
928 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
929 ngx_http_top_body_filter = ngx_http_gzip_body_filter; | |
930 | |
931 return NGX_OK; | |
932 } | |
933 | |
934 | |
935 static void *ngx_http_gzip_create_conf(ngx_conf_t *cf) | |
936 { | |
937 ngx_http_gzip_conf_t *conf; | |
938 | |
939 if (!(conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t)))) { | |
940 return NGX_CONF_ERROR; | |
941 } | |
942 | |
943 /* | |
944 | |
945 set by ngx_pcalloc(): | |
946 | |
947 conf->bufs.num = 0; | |
948 conf->proxied = 0; | |
949 | |
950 */ | |
951 | |
952 conf->enable = NGX_CONF_UNSET; | |
953 conf->no_buffer = NGX_CONF_UNSET; | |
954 | |
955 conf->http_version = NGX_CONF_UNSET_UINT; | |
956 | |
957 conf->level = NGX_CONF_UNSET; | |
958 conf->wbits = (size_t) NGX_CONF_UNSET; | |
959 conf->memlevel = (size_t) NGX_CONF_UNSET; | |
960 conf->min_length = NGX_CONF_UNSET; | |
961 | |
962 return conf; | |
963 } | |
964 | |
965 | |
966 static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf, | |
967 void *parent, void *child) | |
968 { | |
969 ngx_http_gzip_conf_t *prev = parent; | |
970 ngx_http_gzip_conf_t *conf = child; | |
971 | |
972 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
973 | |
974 ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 4, ngx_pagesize); | |
975 | |
976 ngx_conf_merge_unsigned_value(conf->http_version, prev->http_version, | |
977 NGX_HTTP_VERSION_11); | |
978 ngx_conf_merge_bitmask_value(conf->proxied, prev->proxied, | |
979 (NGX_CONF_BITMASK_SET | |
980 |NGX_HTTP_GZIP_PROXIED_OFF)); | |
981 | |
982 ngx_conf_merge_value(conf->level, prev->level, 1); | |
983 ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS); | |
984 ngx_conf_merge_size_value(conf->memlevel, prev->memlevel, | |
985 MAX_MEM_LEVEL - 1); | |
986 ngx_conf_merge_value(conf->min_length, prev->min_length, 0); | |
987 ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0); | |
988 | |
989 return NGX_CONF_OK; | |
990 } | |
991 | |
992 | |
993 static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data) | |
994 { | |
995 int *np = data; | |
996 | |
997 int wbits, wsize; | |
998 | |
999 wbits = 15; | |
1000 | |
1001 for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) { | |
1002 | |
1003 if (wsize == *np) { | |
1004 *np = wbits; | |
1005 | |
1006 return NGX_CONF_OK; | |
1007 } | |
1008 | |
1009 wbits--; | |
1010 } | |
1011 | |
1012 return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k"; | |
1013 } | |
1014 | |
1015 | |
1016 static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data) | |
1017 { | |
1018 int *np = data; | |
1019 | |
1020 int memlevel, hsize; | |
1021 | |
1022 memlevel = 9; | |
1023 | |
1024 for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) { | |
1025 | |
1026 if (hsize == *np) { | |
1027 *np = memlevel; | |
1028 | |
1029 return NGX_CONF_OK; | |
1030 } | |
1031 | |
1032 memlevel--; | |
1033 } | |
1034 | |
1035 return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k"; | |
1036 } |