Mercurial > hg > nginx-mail
comparison src/http/modules/ngx_http_map_module.c @ 138:8e6d4d96ec4c NGINX_0_3_16
nginx 0.3.16
*) Feature: the ngx_http_map_module.
*) Feature: the "types_hash_max_size" and "types_hash_bucket_size"
directives.
*) Feature: the "ssi_value_length" directive.
*) Feature: the "worker_rlimit_core" directive.
*) Workaround: the connection number in logs was always 1 if nginx was
built by the icc 8.1 or 9.0 compilers with optimization for
Pentium 4.
*) Bugfix: the "config timefmt" SSI command set incorrect time format.
*) Bugfix: nginx did not close connection to IMAP/POP3 backend for the
SSL connections; bug appeared in 0.3.13.
Thanks to Rob Mueller.
*) Bugfix: segmentation fault may occurred in at SSL shutdown; bug
appeared in 0.3.13.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Fri, 16 Dec 2005 00:00:00 +0300 |
parents | |
children | 55a211e5eeb7 |
comparison
equal
deleted
inserted
replaced
137:768f51dd150b | 138:8e6d4d96ec4c |
---|---|
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 | |
12 #define NGX_HTTP_MAP_HASH 10007 | |
13 | |
14 typedef struct { | |
15 ngx_uint_t hash_max_size; | |
16 ngx_uint_t hash_bucket_size; | |
17 } ngx_http_map_conf_t; | |
18 | |
19 | |
20 typedef struct { | |
21 ngx_pool_t *pool; | |
22 | |
23 ngx_array_t keys; | |
24 ngx_array_t *keys_hash; | |
25 | |
26 ngx_array_t dns_wildcards; | |
27 ngx_array_t *dns_hash; | |
28 | |
29 ngx_array_t *values_hash; | |
30 | |
31 ngx_http_variable_value_t *default_value; | |
32 ngx_uint_t hostnames; /* unsigned hostnames:1 */ | |
33 } ngx_http_map_conf_ctx_t; | |
34 | |
35 | |
36 typedef struct { | |
37 ngx_hash_t hash; | |
38 ngx_hash_wildcard_t *dns_wildcards; | |
39 ngx_int_t index; | |
40 ngx_http_variable_value_t *default_value; | |
41 ngx_uint_t hostnames; /* unsigned hostnames:1 */ | |
42 } ngx_http_map_ctx_t; | |
43 | |
44 | |
45 static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one, | |
46 const void *two); | |
47 static void *ngx_http_map_create_conf(ngx_conf_t *cf); | |
48 static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); | |
49 static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); | |
50 | |
51 | |
52 static ngx_command_t ngx_http_map_commands[] = { | |
53 | |
54 { ngx_string("map"), | |
55 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, | |
56 ngx_http_map_block, | |
57 NGX_HTTP_MAIN_CONF_OFFSET, | |
58 0, | |
59 NULL }, | |
60 | |
61 { ngx_string("map_hash_max_size"), | |
62 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, | |
63 ngx_conf_set_num_slot, | |
64 NGX_HTTP_MAIN_CONF_OFFSET, | |
65 offsetof(ngx_http_map_conf_t, hash_max_size), | |
66 NULL }, | |
67 | |
68 { ngx_string("map_hash_bucket_size"), | |
69 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, | |
70 ngx_conf_set_num_slot, | |
71 NGX_HTTP_MAIN_CONF_OFFSET, | |
72 offsetof(ngx_http_map_conf_t, hash_bucket_size), | |
73 NULL }, | |
74 | |
75 ngx_null_command | |
76 }; | |
77 | |
78 | |
79 static ngx_http_module_t ngx_http_map_module_ctx = { | |
80 NULL, /* preconfiguration */ | |
81 NULL, /* postconfiguration */ | |
82 | |
83 ngx_http_map_create_conf, /* create main configuration */ | |
84 NULL, /* init main configuration */ | |
85 | |
86 NULL, /* create server configuration */ | |
87 NULL, /* merge server configuration */ | |
88 | |
89 NULL, /* create location configuration */ | |
90 NULL /* merge location configuration */ | |
91 }; | |
92 | |
93 | |
94 ngx_module_t ngx_http_map_module = { | |
95 NGX_MODULE_V1, | |
96 &ngx_http_map_module_ctx, /* module context */ | |
97 ngx_http_map_commands, /* module directives */ | |
98 NGX_HTTP_MODULE, /* module type */ | |
99 NULL, /* init master */ | |
100 NULL, /* init module */ | |
101 NULL, /* init process */ | |
102 NULL, /* init thread */ | |
103 NULL, /* exit thread */ | |
104 NULL, /* exit process */ | |
105 NULL, /* exit master */ | |
106 NGX_MODULE_V1_PADDING | |
107 }; | |
108 | |
109 | |
110 static ngx_int_t | |
111 ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, | |
112 uintptr_t data) | |
113 { | |
114 ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data; | |
115 | |
116 size_t len; | |
117 u_char *name; | |
118 ngx_uint_t key, i; | |
119 ngx_http_variable_value_t *vv, *value; | |
120 | |
121 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
122 "http map started"); | |
123 | |
124 vv = ngx_http_get_flushed_variable(r, map->index); | |
125 | |
126 len = vv->len; | |
127 | |
128 if (len && map->hostnames && vv->data[len - 1] == '.') { | |
129 len--; | |
130 } | |
131 | |
132 if (len == 0) { | |
133 *v = *map->default_value; | |
134 return NGX_OK; | |
135 } | |
136 | |
137 name = ngx_palloc(r->pool, len); | |
138 if (name == NULL) { | |
139 return NGX_ERROR; | |
140 } | |
141 | |
142 key = 0; | |
143 for (i = 0; i < len; i++) { | |
144 name[i] = ngx_tolower(vv->data[i]); | |
145 key = ngx_hash(key, name[i]); | |
146 } | |
147 | |
148 value = NULL; | |
149 | |
150 if (map->hash.buckets) { | |
151 value = ngx_hash_find(&map->hash, key, name, len); | |
152 } | |
153 | |
154 if (value) { | |
155 *v = *value; | |
156 | |
157 } else { | |
158 if (map->dns_wildcards && map->dns_wildcards->hash.buckets) { | |
159 value = ngx_hash_find_wildcard(map->dns_wildcards, name, len); | |
160 if (value) { | |
161 *v = *value; | |
162 | |
163 } else { | |
164 *v = *map->default_value; | |
165 } | |
166 | |
167 } else { | |
168 *v = *map->default_value; | |
169 } | |
170 } | |
171 | |
172 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
173 "http map: \"%V\" \"%V\"", vv, v); | |
174 | |
175 return NGX_OK; | |
176 } | |
177 | |
178 | |
179 static void * | |
180 ngx_http_map_create_conf(ngx_conf_t *cf) | |
181 { | |
182 ngx_http_map_conf_t *mcf; | |
183 | |
184 mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t)); | |
185 if (mcf == NULL) { | |
186 return NGX_CONF_ERROR; | |
187 } | |
188 | |
189 mcf->hash_max_size = NGX_CONF_UNSET_UINT; | |
190 mcf->hash_bucket_size = NGX_CONF_UNSET_UINT; | |
191 | |
192 return mcf; | |
193 } | |
194 | |
195 | |
196 static char * | |
197 ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
198 { | |
199 ngx_http_map_conf_t *mcf = conf; | |
200 | |
201 char *rv; | |
202 ngx_str_t *value, name; | |
203 ngx_conf_t save; | |
204 ngx_pool_t *pool; | |
205 ngx_hash_init_t hash; | |
206 ngx_http_map_ctx_t *map; | |
207 ngx_http_variable_t *var; | |
208 ngx_http_map_conf_ctx_t ctx; | |
209 | |
210 if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) { | |
211 mcf->hash_max_size = 2048; | |
212 } | |
213 | |
214 if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) { | |
215 mcf->hash_bucket_size = ngx_cacheline_size; | |
216 | |
217 } else { | |
218 mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size, | |
219 ngx_cacheline_size); | |
220 } | |
221 | |
222 map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t)); | |
223 if (map == NULL) { | |
224 return NGX_CONF_ERROR; | |
225 } | |
226 | |
227 value = cf->args->elts; | |
228 | |
229 name = value[1]; | |
230 name.len--; | |
231 name.data++; | |
232 | |
233 map->index = ngx_http_get_variable_index(cf, &name); | |
234 | |
235 if (map->index == NGX_ERROR) { | |
236 return NGX_CONF_ERROR; | |
237 } | |
238 | |
239 name = value[2]; | |
240 name.len--; | |
241 name.data++; | |
242 | |
243 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGABLE); | |
244 if (var == NULL) { | |
245 return NGX_CONF_ERROR; | |
246 } | |
247 | |
248 var->handler = ngx_http_map_variable; | |
249 var->data = (uintptr_t) map; | |
250 | |
251 pool = ngx_create_pool(16384, cf->log); | |
252 if (pool == NULL) { | |
253 return NGX_CONF_ERROR; | |
254 } | |
255 | |
256 if (ngx_array_init(&ctx.keys, pool, 16384, sizeof(ngx_hash_key_t)) | |
257 != NGX_OK) | |
258 { | |
259 ngx_destroy_pool(pool); | |
260 return NGX_CONF_ERROR; | |
261 } | |
262 | |
263 if (ngx_array_init(&ctx.dns_wildcards, pool, 16384, sizeof(ngx_hash_key_t)) | |
264 != NGX_OK) | |
265 { | |
266 ngx_destroy_pool(pool); | |
267 return NGX_CONF_ERROR; | |
268 } | |
269 | |
270 ctx.keys_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * NGX_HTTP_MAP_HASH); | |
271 if (ctx.keys_hash == NULL) { | |
272 ngx_destroy_pool(pool); | |
273 return NGX_CONF_ERROR; | |
274 } | |
275 | |
276 ctx.dns_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * NGX_HTTP_MAP_HASH); | |
277 if (ctx.dns_hash == NULL) { | |
278 ngx_destroy_pool(pool); | |
279 return NGX_CONF_ERROR; | |
280 } | |
281 | |
282 ctx.values_hash = ngx_pcalloc(pool, | |
283 sizeof(ngx_array_t) * NGX_HTTP_MAP_HASH); | |
284 if (ctx.values_hash == NULL) { | |
285 ngx_destroy_pool(pool); | |
286 return NGX_CONF_ERROR; | |
287 } | |
288 | |
289 ctx.pool = cf->pool; | |
290 ctx.default_value = NULL; | |
291 ctx.hostnames = 0; | |
292 | |
293 save = *cf; | |
294 cf->pool = pool; | |
295 cf->ctx = &ctx; | |
296 cf->handler = ngx_http_map; | |
297 cf->handler_conf = conf; | |
298 | |
299 rv = ngx_conf_parse(cf, NULL); | |
300 | |
301 *cf = save; | |
302 | |
303 if (rv != NGX_CONF_OK) { | |
304 ngx_destroy_pool(pool); | |
305 | |
306 return rv; | |
307 } | |
308 | |
309 hash.key = ngx_hash_key_lc; | |
310 hash.max_size = mcf->hash_max_size; | |
311 hash.bucket_size = mcf->hash_bucket_size; | |
312 hash.name = "map_hash"; | |
313 hash.pool = cf->pool; | |
314 | |
315 if (ctx.keys.nelts) { | |
316 hash.hash = &map->hash; | |
317 hash.temp_pool = NULL; | |
318 | |
319 if (ngx_hash_init(&hash, ctx.keys.elts, ctx.keys.nelts) != NGX_OK) { | |
320 return NGX_CONF_ERROR; | |
321 } | |
322 } | |
323 | |
324 map->default_value = ctx.default_value ? ctx.default_value: | |
325 &ngx_http_variable_null_value; | |
326 | |
327 if (ctx.dns_wildcards.nelts) { | |
328 | |
329 ngx_qsort(ctx.dns_wildcards.elts, (size_t) ctx.dns_wildcards.nelts, | |
330 sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards); | |
331 | |
332 hash.hash = NULL; | |
333 hash.temp_pool = pool; | |
334 | |
335 if (ngx_hash_wildcard_init(&hash, ctx.dns_wildcards.elts, | |
336 ctx.dns_wildcards.nelts) | |
337 != NGX_OK) | |
338 { | |
339 return NGX_CONF_ERROR; | |
340 } | |
341 | |
342 map->dns_wildcards = (ngx_hash_wildcard_t *) hash.hash; | |
343 } | |
344 | |
345 ngx_destroy_pool(pool); | |
346 | |
347 return rv; | |
348 } | |
349 | |
350 | |
351 static int ngx_libc_cdecl | |
352 ngx_http_map_cmp_dns_wildcards(const void *one, const void *two) | |
353 { | |
354 ngx_hash_key_t *first, *second; | |
355 | |
356 first = (ngx_hash_key_t *) one; | |
357 second = (ngx_hash_key_t *) two; | |
358 | |
359 return ngx_strcmp(first->key.data, second->key.data); | |
360 } | |
361 | |
362 | |
363 static char * | |
364 ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | |
365 { | |
366 size_t len; | |
367 ngx_str_t *value, file, *name; | |
368 ngx_uint_t i, n, key; | |
369 ngx_hash_key_t *m; | |
370 ngx_http_map_conf_ctx_t *ctx; | |
371 ngx_http_variable_value_t *var, *old, **vp; | |
372 u_char buf[2048]; | |
373 | |
374 ctx = cf->ctx; | |
375 | |
376 value = cf->args->elts; | |
377 | |
378 if (cf->args->nelts == 1 | |
379 && ngx_strcmp(value[0].data, "hostnames") == 0) | |
380 { | |
381 ctx->hostnames = 1; | |
382 return NGX_CONF_OK; | |
383 | |
384 } else if (cf->args->nelts != 2) { | |
385 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
386 "invalid number of the map parameters"); | |
387 return NGX_CONF_ERROR; | |
388 | |
389 } else if (value[0].len == 0) { | |
390 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
391 "invalid first parameter"); | |
392 return NGX_CONF_ERROR; | |
393 } | |
394 | |
395 if (ngx_strcmp(value[0].data, "include") == 0) { | |
396 file = value[1]; | |
397 | |
398 if (ngx_conf_full_name(cf->cycle, &file) == NGX_ERROR){ | |
399 return NGX_CONF_ERROR; | |
400 } | |
401 | |
402 ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); | |
403 | |
404 return ngx_conf_parse(cf, &file); | |
405 } | |
406 | |
407 key = 0; | |
408 | |
409 for (i = 0; i < value[1].len; i++) { | |
410 key = ngx_hash(key, value[1].data[i]); | |
411 } | |
412 | |
413 key %= NGX_HTTP_MAP_HASH; | |
414 | |
415 vp = ctx->values_hash[key].elts; | |
416 | |
417 if (vp) { | |
418 for (i = 0; i < ctx->values_hash[key].nelts; i++) { | |
419 if (value[1].len != (size_t) vp[i]->len) { | |
420 continue; | |
421 } | |
422 | |
423 if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) { | |
424 var = vp[i]; | |
425 goto found; | |
426 } | |
427 } | |
428 | |
429 } else { | |
430 if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4, | |
431 sizeof(ngx_http_variable_value_t *)) | |
432 != NGX_OK) | |
433 { | |
434 return NGX_CONF_ERROR; | |
435 } | |
436 } | |
437 | |
438 var = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t)); | |
439 if (var == NULL) { | |
440 return NGX_CONF_ERROR; | |
441 } | |
442 | |
443 var->len = value[1].len; | |
444 var->data = ngx_pstrdup(ctx->pool, &value[1]); | |
445 if (var->data == NULL) { | |
446 return NGX_CONF_ERROR; | |
447 } | |
448 | |
449 var->valid = 1; | |
450 var->no_cachable = 0; | |
451 var->not_found = 0; | |
452 | |
453 vp = ngx_array_push(&ctx->values_hash[key]); | |
454 if (vp == NULL) { | |
455 return NGX_CONF_ERROR; | |
456 } | |
457 | |
458 *vp = var; | |
459 | |
460 found: | |
461 | |
462 if (value[0].data[0] != '*' || ctx->hostnames == 0) { | |
463 | |
464 if (ngx_strcmp(value[0].data, "default") != 0) { | |
465 | |
466 if (value[0].len && value[0].data[0] == '!') { | |
467 value[0].len--; | |
468 value[0].data++; | |
469 } | |
470 | |
471 key = 0; | |
472 | |
473 for (i = 0; i < value[0].len; i++) { | |
474 value[0].data[i] = ngx_tolower(value[0].data[i]); | |
475 key = ngx_hash(key, value[0].data[i]); | |
476 } | |
477 | |
478 key %= NGX_HTTP_MAP_HASH; | |
479 | |
480 name = ctx->keys_hash[key].elts; | |
481 | |
482 if (name) { | |
483 for (i = 0; i < ctx->keys_hash[key].nelts; i++) { | |
484 if (value[0].len != name[i].len) { | |
485 continue; | |
486 } | |
487 | |
488 if (ngx_strncmp(value[0].data, name[i].data, value[0].len) | |
489 == 0) | |
490 { | |
491 m = ctx->keys.elts; | |
492 for (i = 0; i < ctx->keys.nelts; i++) { | |
493 if (ngx_strcmp(value[0].data, m[i].key.data) == 0) { | |
494 old = m[i].value; | |
495 m[i].value = var; | |
496 | |
497 goto duplicate; | |
498 } | |
499 } | |
500 } | |
501 } | |
502 | |
503 } else { | |
504 if (ngx_array_init(&ctx->keys_hash[key], cf->pool, 4, | |
505 sizeof(ngx_str_t)) | |
506 != NGX_OK) | |
507 { | |
508 return NGX_CONF_ERROR; | |
509 } | |
510 } | |
511 | |
512 name = ngx_array_push(&ctx->keys_hash[key]); | |
513 if (name == NULL) { | |
514 return NGX_CONF_ERROR; | |
515 } | |
516 | |
517 *name = value[0]; | |
518 | |
519 m = ngx_array_push(&ctx->keys); | |
520 if (m == NULL) { | |
521 return NGX_CONF_ERROR; | |
522 } | |
523 | |
524 m->key = value[0]; | |
525 m->key_hash = ngx_hash_key(value[0].data, value[0].len); | |
526 m->value = var; | |
527 | |
528 } else { | |
529 if (ctx->default_value) { | |
530 old = ctx->default_value; | |
531 ctx->default_value = var; | |
532 | |
533 goto duplicate; | |
534 } | |
535 | |
536 ctx->default_value = var; | |
537 } | |
538 | |
539 } else { | |
540 | |
541 if (value[0].len < 3 || value[0].data[1] != '.') { | |
542 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
543 "invalid DNS wildcard \"%V\"", &value[0]); | |
544 return NGX_CONF_ERROR; | |
545 } | |
546 | |
547 key = 0; | |
548 | |
549 for (i = 2; i < value[0].len; i++) { | |
550 value[0].data[i] = ngx_tolower(value[0].data[i]); | |
551 key = ngx_hash(key, value[0].data[i]); | |
552 } | |
553 | |
554 key %= NGX_HTTP_MAP_HASH; | |
555 | |
556 /* convert "*.example.com" into "com.example.\0" */ | |
557 | |
558 len = 0; | |
559 n = 0; | |
560 | |
561 for (i = value[0].len - 1; i; i--) { | |
562 if (value[0].data[i] == '.') { | |
563 ngx_memcpy(&buf[n], &value[0].data[i + 1], len); | |
564 n += len; | |
565 buf[n++] = '.'; | |
566 len = 0; | |
567 continue; | |
568 } | |
569 | |
570 len++; | |
571 } | |
572 | |
573 buf[n] = '\0'; | |
574 | |
575 name = ctx->dns_hash[key].elts; | |
576 | |
577 if (name) { | |
578 for (i = 0; i < ctx->dns_hash[key].nelts; i++) { | |
579 if (value[0].len != name[i].len) { | |
580 continue; | |
581 } | |
582 | |
583 if (ngx_strncmp(value[0].data, name[i].data, value[0].len) | |
584 == 0) | |
585 { | |
586 m = ctx->dns_wildcards.elts; | |
587 for (i = 0; i < ctx->dns_wildcards.nelts; i++) { | |
588 if (ngx_strcmp(buf, m[i].key.data) == 0) { | |
589 old = m[i].value; | |
590 m[i].value = var; | |
591 | |
592 goto duplicate; | |
593 } | |
594 } | |
595 } | |
596 } | |
597 | |
598 } else { | |
599 if (ngx_array_init(&ctx->dns_hash[key], cf->pool, 4, | |
600 sizeof(ngx_str_t)) | |
601 != NGX_OK) | |
602 { | |
603 return NGX_CONF_ERROR; | |
604 } | |
605 } | |
606 | |
607 name = ngx_array_push(&ctx->dns_hash[key]); | |
608 if (name == NULL) { | |
609 return NGX_CONF_ERROR; | |
610 } | |
611 | |
612 *name = value[0]; | |
613 | |
614 ngx_memcpy(value[0].data, buf, value[0].len); | |
615 value[0].len--; | |
616 | |
617 m = ngx_array_push(&ctx->dns_wildcards); | |
618 if (m == NULL) { | |
619 return NGX_CONF_ERROR; | |
620 } | |
621 | |
622 m->key = value[0]; | |
623 m->key_hash = 0; | |
624 m->value = var; | |
625 } | |
626 | |
627 return NGX_CONF_OK; | |
628 | |
629 duplicate: | |
630 | |
631 ngx_conf_log_error(NGX_LOG_WARN, cf, 0, | |
632 "duplicate parameter \"%V\", value: \"%V\", " | |
633 "old value: \"%V\"", | |
634 &value[0], var, old); | |
635 | |
636 return NGX_CONF_OK; | |
637 } |