Mercurial > hg > nginx
annotate src/stream/ngx_stream_map_module.c @ 8906:41caf5410110 quic
QUIC: reject streams which we could not create.
The reasons why a stream may not be created by server currently include hitting
worker_connections limit and memory allocation error. Previously in these
cases the entire QUIC connection was closed and all its streams were shut down.
Now the new stream is rejected and existing streams continue working.
To reject an HTTP/3 request stream, RESET_STREAM and STOP_SENDING with
H3_REQUEST_REJECTED error code are sent to client. HTTP/3 uni streams and
Stream streams are not rejected.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Thu, 11 Nov 2021 19:07:00 +0300 |
parents | ec10ce307dc0 |
children |
rev | line source |
---|---|
6609 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_stream.h> | |
11 | |
12 | |
13 typedef struct { | |
14 ngx_uint_t hash_max_size; | |
15 ngx_uint_t hash_bucket_size; | |
16 } ngx_stream_map_conf_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_hash_keys_arrays_t keys; | |
21 | |
22 ngx_array_t *values_hash; | |
23 #if (NGX_PCRE) | |
24 ngx_array_t regexes; | |
25 #endif | |
26 | |
27 ngx_stream_variable_value_t *default_value; | |
28 ngx_conf_t *cf; | |
6832
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
29 unsigned hostnames:1; |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
30 unsigned no_cacheable:1; |
6609 | 31 } ngx_stream_map_conf_ctx_t; |
32 | |
33 | |
34 typedef struct { | |
35 ngx_stream_map_t map; | |
36 ngx_stream_complex_value_t value; | |
37 ngx_stream_variable_value_t *default_value; | |
38 ngx_uint_t hostnames; /* unsigned hostnames:1 */ | |
39 } ngx_stream_map_ctx_t; | |
40 | |
41 | |
42 static int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one, | |
43 const void *two); | |
44 static void *ngx_stream_map_create_conf(ngx_conf_t *cf); | |
45 static char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, | |
46 void *conf); | |
47 static char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); | |
48 | |
49 | |
50 static ngx_command_t ngx_stream_map_commands[] = { | |
51 | |
52 { ngx_string("map"), | |
53 NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, | |
54 ngx_stream_map_block, | |
55 NGX_STREAM_MAIN_CONF_OFFSET, | |
56 0, | |
57 NULL }, | |
58 | |
59 { ngx_string("map_hash_max_size"), | |
60 NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, | |
61 ngx_conf_set_num_slot, | |
62 NGX_STREAM_MAIN_CONF_OFFSET, | |
63 offsetof(ngx_stream_map_conf_t, hash_max_size), | |
64 NULL }, | |
65 | |
66 { ngx_string("map_hash_bucket_size"), | |
67 NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, | |
68 ngx_conf_set_num_slot, | |
69 NGX_STREAM_MAIN_CONF_OFFSET, | |
70 offsetof(ngx_stream_map_conf_t, hash_bucket_size), | |
71 NULL }, | |
72 | |
73 ngx_null_command | |
74 }; | |
75 | |
76 | |
77 static ngx_stream_module_t ngx_stream_map_module_ctx = { | |
78 NULL, /* preconfiguration */ | |
79 NULL, /* postconfiguration */ | |
80 | |
81 ngx_stream_map_create_conf, /* create main configuration */ | |
82 NULL, /* init main configuration */ | |
83 | |
84 NULL, /* create server configuration */ | |
6629 | 85 NULL /* merge server configuration */ |
6609 | 86 }; |
87 | |
88 | |
89 ngx_module_t ngx_stream_map_module = { | |
90 NGX_MODULE_V1, | |
91 &ngx_stream_map_module_ctx, /* module context */ | |
92 ngx_stream_map_commands, /* module directives */ | |
93 NGX_STREAM_MODULE, /* module type */ | |
94 NULL, /* init master */ | |
95 NULL, /* init module */ | |
96 NULL, /* init process */ | |
97 NULL, /* init thread */ | |
98 NULL, /* exit thread */ | |
99 NULL, /* exit process */ | |
100 NULL, /* exit master */ | |
101 NGX_MODULE_V1_PADDING | |
102 }; | |
103 | |
104 | |
105 static ngx_int_t | |
106 ngx_stream_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, | |
107 uintptr_t data) | |
108 { | |
109 ngx_stream_map_ctx_t *map = (ngx_stream_map_ctx_t *) data; | |
110 | |
111 ngx_str_t val, str; | |
112 ngx_stream_complex_value_t *cv; | |
113 ngx_stream_variable_value_t *value; | |
114 | |
115 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, | |
116 "stream map started"); | |
117 | |
118 if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) { | |
119 return NGX_ERROR; | |
120 } | |
121 | |
122 if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') { | |
123 val.len--; | |
124 } | |
125 | |
126 value = ngx_stream_map_find(s, &map->map, &val); | |
127 | |
128 if (value == NULL) { | |
129 value = map->default_value; | |
130 } | |
131 | |
132 if (!value->valid) { | |
133 cv = (ngx_stream_complex_value_t *) value->data; | |
134 | |
135 if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) { | |
136 return NGX_ERROR; | |
137 } | |
138 | |
139 v->valid = 1; | |
140 v->no_cacheable = 0; | |
141 v->not_found = 0; | |
142 v->len = str.len; | |
143 v->data = str.data; | |
144 | |
145 } else { | |
146 *v = *value; | |
147 } | |
148 | |
149 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, | |
150 "stream map: \"%V\" \"%v\"", &val, v); | |
151 | |
152 return NGX_OK; | |
153 } | |
154 | |
155 | |
156 static void * | |
157 ngx_stream_map_create_conf(ngx_conf_t *cf) | |
158 { | |
159 ngx_stream_map_conf_t *mcf; | |
160 | |
161 mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t)); | |
162 if (mcf == NULL) { | |
163 return NULL; | |
164 } | |
165 | |
166 mcf->hash_max_size = NGX_CONF_UNSET_UINT; | |
167 mcf->hash_bucket_size = NGX_CONF_UNSET_UINT; | |
168 | |
169 return mcf; | |
170 } | |
171 | |
172 | |
173 static char * | |
174 ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
175 { | |
176 ngx_stream_map_conf_t *mcf = conf; | |
177 | |
178 char *rv; | |
179 ngx_str_t *value, name; | |
180 ngx_conf_t save; | |
181 ngx_pool_t *pool; | |
182 ngx_hash_init_t hash; | |
183 ngx_stream_map_ctx_t *map; | |
184 ngx_stream_variable_t *var; | |
185 ngx_stream_map_conf_ctx_t ctx; | |
186 ngx_stream_compile_complex_value_t ccv; | |
187 | |
188 if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) { | |
189 mcf->hash_max_size = 2048; | |
190 } | |
191 | |
192 if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) { | |
193 mcf->hash_bucket_size = ngx_cacheline_size; | |
194 | |
195 } else { | |
196 mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size, | |
197 ngx_cacheline_size); | |
198 } | |
199 | |
200 map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t)); | |
201 if (map == NULL) { | |
202 return NGX_CONF_ERROR; | |
203 } | |
204 | |
205 value = cf->args->elts; | |
206 | |
207 ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); | |
208 | |
209 ccv.cf = cf; | |
210 ccv.value = &value[1]; | |
211 ccv.complex_value = &map->value; | |
212 | |
213 if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { | |
214 return NGX_CONF_ERROR; | |
215 } | |
216 | |
217 name = value[2]; | |
218 | |
219 if (name.data[0] != '$') { | |
220 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
221 "invalid variable name \"%V\"", &name); | |
222 return NGX_CONF_ERROR; | |
223 } | |
224 | |
225 name.len--; | |
226 name.data++; | |
227 | |
228 var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); | |
229 if (var == NULL) { | |
230 return NGX_CONF_ERROR; | |
231 } | |
232 | |
233 var->get_handler = ngx_stream_map_variable; | |
234 var->data = (uintptr_t) map; | |
235 | |
236 pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); | |
237 if (pool == NULL) { | |
238 return NGX_CONF_ERROR; | |
239 } | |
240 | |
241 ctx.keys.pool = cf->pool; | |
242 ctx.keys.temp_pool = pool; | |
243 | |
244 if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) { | |
245 ngx_destroy_pool(pool); | |
246 return NGX_CONF_ERROR; | |
247 } | |
248 | |
249 ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize); | |
250 if (ctx.values_hash == NULL) { | |
251 ngx_destroy_pool(pool); | |
252 return NGX_CONF_ERROR; | |
253 } | |
254 | |
255 #if (NGX_PCRE) | |
256 if (ngx_array_init(&ctx.regexes, cf->pool, 2, | |
257 sizeof(ngx_stream_map_regex_t)) | |
258 != NGX_OK) | |
259 { | |
260 ngx_destroy_pool(pool); | |
261 return NGX_CONF_ERROR; | |
262 } | |
263 #endif | |
264 | |
265 ctx.default_value = NULL; | |
266 ctx.cf = &save; | |
267 ctx.hostnames = 0; | |
6832
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
268 ctx.no_cacheable = 0; |
6609 | 269 |
270 save = *cf; | |
271 cf->pool = pool; | |
272 cf->ctx = &ctx; | |
273 cf->handler = ngx_stream_map; | |
274 cf->handler_conf = conf; | |
275 | |
276 rv = ngx_conf_parse(cf, NULL); | |
277 | |
278 *cf = save; | |
279 | |
280 if (rv != NGX_CONF_OK) { | |
281 ngx_destroy_pool(pool); | |
282 return rv; | |
283 } | |
284 | |
6832
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
285 if (ctx.no_cacheable) { |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
286 var->flags |= NGX_STREAM_VAR_NOCACHEABLE; |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
287 } |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
288 |
6609 | 289 map->default_value = ctx.default_value ? ctx.default_value: |
290 &ngx_stream_variable_null_value; | |
291 | |
292 map->hostnames = ctx.hostnames; | |
293 | |
294 hash.key = ngx_hash_key_lc; | |
295 hash.max_size = mcf->hash_max_size; | |
296 hash.bucket_size = mcf->hash_bucket_size; | |
297 hash.name = "map_hash"; | |
298 hash.pool = cf->pool; | |
299 | |
300 if (ctx.keys.keys.nelts) { | |
301 hash.hash = &map->map.hash.hash; | |
302 hash.temp_pool = NULL; | |
303 | |
304 if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts) | |
305 != NGX_OK) | |
306 { | |
307 ngx_destroy_pool(pool); | |
308 return NGX_CONF_ERROR; | |
309 } | |
310 } | |
311 | |
312 if (ctx.keys.dns_wc_head.nelts) { | |
313 | |
314 ngx_qsort(ctx.keys.dns_wc_head.elts, | |
315 (size_t) ctx.keys.dns_wc_head.nelts, | |
316 sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards); | |
317 | |
318 hash.hash = NULL; | |
319 hash.temp_pool = pool; | |
320 | |
321 if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts, | |
322 ctx.keys.dns_wc_head.nelts) | |
323 != NGX_OK) | |
324 { | |
325 ngx_destroy_pool(pool); | |
326 return NGX_CONF_ERROR; | |
327 } | |
328 | |
329 map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; | |
330 } | |
331 | |
332 if (ctx.keys.dns_wc_tail.nelts) { | |
333 | |
334 ngx_qsort(ctx.keys.dns_wc_tail.elts, | |
335 (size_t) ctx.keys.dns_wc_tail.nelts, | |
336 sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards); | |
337 | |
338 hash.hash = NULL; | |
339 hash.temp_pool = pool; | |
340 | |
341 if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts, | |
342 ctx.keys.dns_wc_tail.nelts) | |
343 != NGX_OK) | |
344 { | |
345 ngx_destroy_pool(pool); | |
346 return NGX_CONF_ERROR; | |
347 } | |
348 | |
349 map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; | |
350 } | |
351 | |
352 #if (NGX_PCRE) | |
353 | |
354 if (ctx.regexes.nelts) { | |
355 map->map.regex = ctx.regexes.elts; | |
356 map->map.nregex = ctx.regexes.nelts; | |
357 } | |
358 | |
359 #endif | |
360 | |
361 ngx_destroy_pool(pool); | |
362 | |
363 return rv; | |
364 } | |
365 | |
366 | |
367 static int ngx_libc_cdecl | |
368 ngx_stream_map_cmp_dns_wildcards(const void *one, const void *two) | |
369 { | |
370 ngx_hash_key_t *first, *second; | |
371 | |
372 first = (ngx_hash_key_t *) one; | |
373 second = (ngx_hash_key_t *) two; | |
374 | |
375 return ngx_dns_strcmp(first->key.data, second->key.data); | |
376 } | |
377 | |
378 | |
379 static char * | |
380 ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | |
381 { | |
382 u_char *data; | |
383 size_t len; | |
384 ngx_int_t rv; | |
385 ngx_str_t *value, v; | |
386 ngx_uint_t i, key; | |
387 ngx_stream_map_conf_ctx_t *ctx; | |
388 ngx_stream_complex_value_t cv, *cvp; | |
389 ngx_stream_variable_value_t *var, **vp; | |
390 ngx_stream_compile_complex_value_t ccv; | |
391 | |
392 ctx = cf->ctx; | |
393 | |
394 value = cf->args->elts; | |
395 | |
396 if (cf->args->nelts == 1 | |
397 && ngx_strcmp(value[0].data, "hostnames") == 0) | |
398 { | |
399 ctx->hostnames = 1; | |
400 return NGX_CONF_OK; | |
6831
014905eb7b3d
Map: simplified "map" block parser.
Ruslan Ermilov <ru@nginx.com>
parents:
6629
diff
changeset
|
401 } |
6609 | 402 |
6832
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
403 if (cf->args->nelts == 1 |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
404 && ngx_strcmp(value[0].data, "volatile") == 0) |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
405 { |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
406 ctx->no_cacheable = 1; |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
407 return NGX_CONF_OK; |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
408 } |
ec10ce307dc0
Map: the "volatile" parameter.
Ruslan Ermilov <ru@nginx.com>
parents:
6831
diff
changeset
|
409 |
6831
014905eb7b3d
Map: simplified "map" block parser.
Ruslan Ermilov <ru@nginx.com>
parents:
6629
diff
changeset
|
410 if (cf->args->nelts != 2) { |
6609 | 411 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
412 "invalid number of the map parameters"); | |
413 return NGX_CONF_ERROR; | |
414 } | |
415 | |
416 if (ngx_strcmp(value[0].data, "include") == 0) { | |
417 return ngx_conf_include(cf, dummy, conf); | |
418 } | |
419 | |
420 key = 0; | |
421 | |
422 for (i = 0; i < value[1].len; i++) { | |
423 key = ngx_hash(key, value[1].data[i]); | |
424 } | |
425 | |
426 key %= ctx->keys.hsize; | |
427 | |
428 vp = ctx->values_hash[key].elts; | |
429 | |
430 if (vp) { | |
431 for (i = 0; i < ctx->values_hash[key].nelts; i++) { | |
432 | |
433 if (vp[i]->valid) { | |
434 data = vp[i]->data; | |
435 len = vp[i]->len; | |
436 | |
437 } else { | |
438 cvp = (ngx_stream_complex_value_t *) vp[i]->data; | |
439 data = cvp->value.data; | |
440 len = cvp->value.len; | |
441 } | |
442 | |
443 if (value[1].len != len) { | |
444 continue; | |
445 } | |
446 | |
447 if (ngx_strncmp(value[1].data, data, len) == 0) { | |
448 var = vp[i]; | |
449 goto found; | |
450 } | |
451 } | |
452 | |
453 } else { | |
454 if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4, | |
455 sizeof(ngx_stream_variable_value_t *)) | |
456 != NGX_OK) | |
457 { | |
458 return NGX_CONF_ERROR; | |
459 } | |
460 } | |
461 | |
462 var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t)); | |
463 if (var == NULL) { | |
464 return NGX_CONF_ERROR; | |
465 } | |
466 | |
467 v.len = value[1].len; | |
468 v.data = ngx_pstrdup(ctx->keys.pool, &value[1]); | |
469 if (v.data == NULL) { | |
470 return NGX_CONF_ERROR; | |
471 } | |
472 | |
473 ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); | |
474 | |
475 ccv.cf = ctx->cf; | |
476 ccv.value = &v; | |
477 ccv.complex_value = &cv; | |
478 | |
479 if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { | |
480 return NGX_CONF_ERROR; | |
481 } | |
482 | |
483 if (cv.lengths != NULL) { | |
484 cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t)); | |
485 if (cvp == NULL) { | |
486 return NGX_CONF_ERROR; | |
487 } | |
488 | |
489 *cvp = cv; | |
490 | |
491 var->len = 0; | |
492 var->data = (u_char *) cvp; | |
493 var->valid = 0; | |
494 | |
495 } else { | |
496 var->len = v.len; | |
497 var->data = v.data; | |
498 var->valid = 1; | |
499 } | |
500 | |
501 var->no_cacheable = 0; | |
502 var->not_found = 0; | |
503 | |
504 vp = ngx_array_push(&ctx->values_hash[key]); | |
505 if (vp == NULL) { | |
506 return NGX_CONF_ERROR; | |
507 } | |
508 | |
509 *vp = var; | |
510 | |
511 found: | |
512 | |
513 if (ngx_strcmp(value[0].data, "default") == 0) { | |
514 | |
515 if (ctx->default_value) { | |
516 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
517 "duplicate default map parameter"); | |
518 return NGX_CONF_ERROR; | |
519 } | |
520 | |
521 ctx->default_value = var; | |
522 | |
523 return NGX_CONF_OK; | |
524 } | |
525 | |
526 #if (NGX_PCRE) | |
527 | |
528 if (value[0].len && value[0].data[0] == '~') { | |
529 ngx_regex_compile_t rc; | |
530 ngx_stream_map_regex_t *regex; | |
531 u_char errstr[NGX_MAX_CONF_ERRSTR]; | |
532 | |
533 regex = ngx_array_push(&ctx->regexes); | |
534 if (regex == NULL) { | |
535 return NGX_CONF_ERROR; | |
536 } | |
537 | |
538 value[0].len--; | |
539 value[0].data++; | |
540 | |
541 ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); | |
542 | |
543 if (value[0].data[0] == '*') { | |
544 value[0].len--; | |
545 value[0].data++; | |
546 rc.options = NGX_REGEX_CASELESS; | |
547 } | |
548 | |
549 rc.pattern = value[0]; | |
550 rc.err.len = NGX_MAX_CONF_ERRSTR; | |
551 rc.err.data = errstr; | |
552 | |
553 regex->regex = ngx_stream_regex_compile(ctx->cf, &rc); | |
554 if (regex->regex == NULL) { | |
555 return NGX_CONF_ERROR; | |
556 } | |
557 | |
558 regex->value = var; | |
559 | |
560 return NGX_CONF_OK; | |
561 } | |
562 | |
563 #endif | |
564 | |
565 if (value[0].len && value[0].data[0] == '\\') { | |
566 value[0].len--; | |
567 value[0].data++; | |
568 } | |
569 | |
570 rv = ngx_hash_add_key(&ctx->keys, &value[0], var, | |
571 (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); | |
572 | |
573 if (rv == NGX_OK) { | |
574 return NGX_CONF_OK; | |
575 } | |
576 | |
577 if (rv == NGX_DECLINED) { | |
578 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
579 "invalid hostname or wildcard \"%V\"", &value[0]); | |
580 } | |
581 | |
582 if (rv == NGX_BUSY) { | |
583 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
584 "conflicting parameter \"%V\"", &value[0]); | |
585 } | |
586 | |
587 return NGX_CONF_ERROR; | |
588 } |