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