Mercurial > hg > nginx-vendor-1-0
comparison src/http/modules/ngx_http_charset_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 | |
12 typedef struct { | |
13 char **tables; | |
14 ngx_str_t name; | |
15 ngx_uint_t server; /* unsigned server:1; */ | |
16 } ngx_http_charset_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_int_t src; | |
21 ngx_int_t dst; | |
22 char *src2dst; | |
23 char *dst2src; | |
24 } ngx_http_charset_tables_t; | |
25 | |
26 | |
27 typedef struct { | |
28 ngx_array_t charsets; /* ngx_http_charset_t */ | |
29 ngx_array_t tables; /* ngx_http_charset_tables_t */ | |
30 } ngx_http_charset_main_conf_t; | |
31 | |
32 | |
33 typedef struct { | |
34 ngx_flag_t enable; | |
35 ngx_flag_t autodetect; | |
36 | |
37 ngx_int_t default_charset; | |
38 ngx_int_t source_charset; | |
39 } ngx_http_charset_loc_conf_t; | |
40 | |
41 | |
42 typedef struct { | |
43 ngx_int_t server; | |
44 ngx_int_t client; | |
45 } ngx_http_charset_ctx_t; | |
46 | |
47 | |
48 static ngx_uint_t ngx_charset_recode(ngx_buf_t *b, char *table); | |
49 | |
50 static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, | |
51 void *conf); | |
52 static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); | |
53 | |
54 static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
55 void *conf); | |
56 static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name); | |
57 | |
58 static ngx_int_t ngx_http_charset_filter_init(ngx_cycle_t *cycle); | |
59 | |
60 static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf); | |
61 static char *ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf); | |
62 static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf); | |
63 static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, | |
64 void *parent, void *child); | |
65 | |
66 | |
67 static ngx_command_t ngx_http_charset_filter_commands[] = { | |
68 | |
69 { ngx_string("charset_map"), | |
70 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, | |
71 ngx_charset_map_block, | |
72 NGX_HTTP_MAIN_CONF_OFFSET, | |
73 0, | |
74 NULL }, | |
75 | |
76 { ngx_string("default_charset"), | |
77 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
78 ngx_http_set_charset_slot, | |
79 NGX_HTTP_LOC_CONF_OFFSET, | |
80 offsetof(ngx_http_charset_loc_conf_t, default_charset), | |
81 NULL }, | |
82 | |
83 { ngx_string("source_charset"), | |
84 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
85 ngx_http_set_charset_slot, | |
86 NGX_HTTP_LOC_CONF_OFFSET, | |
87 offsetof(ngx_http_charset_loc_conf_t, source_charset), | |
88 NULL }, | |
89 | |
90 { ngx_string("charset"), | |
91 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
92 ngx_conf_set_flag_slot, | |
93 NGX_HTTP_LOC_CONF_OFFSET, | |
94 offsetof(ngx_http_charset_loc_conf_t, enable), | |
95 NULL }, | |
96 | |
97 { ngx_string("autodetect_charset"), | |
98 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
99 ngx_conf_set_flag_slot, | |
100 NGX_HTTP_LOC_CONF_OFFSET, | |
101 offsetof(ngx_http_charset_loc_conf_t, autodetect), | |
102 NULL }, | |
103 | |
104 ngx_null_command | |
105 }; | |
106 | |
107 | |
108 static ngx_http_module_t ngx_http_charset_filter_module_ctx = { | |
109 NULL, /* pre conf */ | |
110 | |
111 ngx_http_charset_create_main_conf, /* create main configuration */ | |
112 ngx_http_charset_init_main_conf, /* init main configuration */ | |
113 | |
114 NULL, /* create server configuration */ | |
115 NULL, /* merge server configuration */ | |
116 | |
117 ngx_http_charset_create_loc_conf, /* create location configuration */ | |
118 ngx_http_charset_merge_loc_conf /* merge location configuration */ | |
119 }; | |
120 | |
121 | |
122 ngx_module_t ngx_http_charset_filter_module = { | |
123 NGX_MODULE, | |
124 &ngx_http_charset_filter_module_ctx, /* module context */ | |
125 ngx_http_charset_filter_commands, /* module directives */ | |
126 NGX_HTTP_MODULE, /* module type */ | |
127 ngx_http_charset_filter_init, /* init module */ | |
128 NULL /* init process */ | |
129 }; | |
130 | |
131 | |
132 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | |
133 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; | |
134 | |
135 | |
136 static ngx_int_t | |
137 ngx_http_charset_header_filter(ngx_http_request_t *r) | |
138 { | |
139 ngx_http_charset_t *charsets; | |
140 ngx_http_charset_ctx_t *ctx; | |
141 ngx_http_charset_loc_conf_t *lcf; | |
142 ngx_http_charset_main_conf_t *mcf; | |
143 | |
144 mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); | |
145 lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); | |
146 | |
147 if (lcf->enable == 0) { | |
148 return ngx_http_next_header_filter(r); | |
149 } | |
150 | |
151 if (r->headers_out.content_type == NULL) { | |
152 return ngx_http_next_header_filter(r); | |
153 } | |
154 | |
155 if (ngx_strncasecmp(r->headers_out.content_type->value.data, | |
156 "text/", 5) != 0 | |
157 && ngx_strncasecmp(r->headers_out.content_type->value.data, | |
158 "application/x-javascript", 24) != 0) | |
159 { | |
160 return ngx_http_next_header_filter(r); | |
161 } | |
162 | |
163 if (ngx_strstr(r->headers_out.content_type->value.data, "charset") != NULL) | |
164 { | |
165 return ngx_http_next_header_filter(r); | |
166 } | |
167 | |
168 if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY | |
169 || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) | |
170 { | |
171 /* | |
172 * do not set charset for the redirect because NN 4.x uses this | |
173 * charset instead of the next page charset | |
174 */ | |
175 | |
176 r->headers_out.charset.len = 0; | |
177 return ngx_http_next_header_filter(r); | |
178 } | |
179 | |
180 if (r->headers_out.charset.len) { | |
181 return ngx_http_next_header_filter(r); | |
182 } | |
183 | |
184 charsets = mcf->charsets.elts; | |
185 r->headers_out.charset = charsets[lcf->default_charset].name; | |
186 | |
187 if (lcf->default_charset == lcf->source_charset) { | |
188 return ngx_http_next_header_filter(r); | |
189 } | |
190 | |
191 | |
192 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t)); | |
193 if (ctx == NULL) { | |
194 return NGX_ERROR; | |
195 } | |
196 | |
197 ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module); | |
198 | |
199 | |
200 r->filter_need_in_memory = 1; | |
201 | |
202 return ngx_http_next_header_filter(r); | |
203 } | |
204 | |
205 | |
206 static ngx_int_t | |
207 ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in) | |
208 { | |
209 char *table; | |
210 ngx_chain_t *cl; | |
211 ngx_http_charset_t *charsets; | |
212 ngx_http_charset_ctx_t *ctx; | |
213 ngx_http_charset_loc_conf_t *lcf; | |
214 ngx_http_charset_main_conf_t *mcf; | |
215 | |
216 ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module); | |
217 | |
218 if (ctx == NULL) { | |
219 return ngx_http_next_body_filter(r, in); | |
220 } | |
221 | |
222 mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); | |
223 lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); | |
224 | |
225 charsets = mcf->charsets.elts; | |
226 table = charsets[lcf->source_charset].tables[lcf->default_charset]; | |
227 | |
228 for (cl = in; cl; cl = cl->next) { | |
229 ngx_charset_recode(cl->buf, table); | |
230 } | |
231 | |
232 return ngx_http_next_body_filter(r, in); | |
233 } | |
234 | |
235 | |
236 static ngx_uint_t | |
237 ngx_charset_recode(ngx_buf_t *b, char *table) | |
238 { | |
239 u_char *p; | |
240 ngx_uint_t change; | |
241 | |
242 change = 0; | |
243 | |
244 for (p = b->pos; p < b->last; p++) { | |
245 if (*p != table[*p]) { | |
246 change = 1; | |
247 break; | |
248 } | |
249 } | |
250 | |
251 if (change) { | |
252 | |
253 while (p < b->last) { | |
254 *p = table[*p]; | |
255 p++; | |
256 } | |
257 | |
258 b->in_file = 0; | |
259 } | |
260 | |
261 return change; | |
262 } | |
263 | |
264 | |
265 static char * | |
266 ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
267 { | |
268 ngx_http_charset_main_conf_t *mcf = conf; | |
269 | |
270 char *rv; | |
271 ngx_int_t src, dst; | |
272 ngx_uint_t i; | |
273 ngx_str_t *value; | |
274 ngx_conf_t pvcf; | |
275 ngx_http_charset_tables_t *table; | |
276 | |
277 value = cf->args->elts; | |
278 | |
279 src = ngx_http_add_charset(&mcf->charsets, &value[1]); | |
280 if (src == NGX_ERROR) { | |
281 return NGX_CONF_ERROR; | |
282 } | |
283 | |
284 dst = ngx_http_add_charset(&mcf->charsets, &value[2]); | |
285 if (dst == NGX_ERROR) { | |
286 return NGX_CONF_ERROR; | |
287 } | |
288 | |
289 if (src == dst) { | |
290 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
291 "\"charset_map\" between the same charsets " | |
292 "\"%V\" and \"%V\"", &value[1], &value[2]); | |
293 return NGX_CONF_ERROR; | |
294 } | |
295 | |
296 table = mcf->tables.elts; | |
297 for (i = 0; i < mcf->tables.nelts; i++) { | |
298 if ((src == table->src && dst == table->dst) | |
299 || (src == table->dst && dst == table->src)) | |
300 { | |
301 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
302 "duplicate \"charset_map\" between " | |
303 "\"%V\" and \"%V\"", &value[1], &value[2]); | |
304 return NGX_CONF_ERROR; | |
305 } | |
306 } | |
307 | |
308 table = ngx_array_push(&mcf->tables); | |
309 if (table == NULL) { | |
310 return NGX_CONF_ERROR; | |
311 } | |
312 | |
313 table->src = src; | |
314 table->dst = dst; | |
315 | |
316 table->src2dst = ngx_palloc(cf->pool, 256); | |
317 if (table->src2dst == NULL) { | |
318 return NGX_CONF_ERROR; | |
319 } | |
320 | |
321 table->dst2src = ngx_palloc(cf->pool, 256); | |
322 if (table->dst2src == NULL) { | |
323 return NGX_CONF_ERROR; | |
324 } | |
325 | |
326 for (i = 0; i < 128; i++) { | |
327 table->src2dst[i] = (char) i; | |
328 table->dst2src[i] = (char) i; | |
329 } | |
330 | |
331 for (/* void */; i < 256; i++) { | |
332 table->src2dst[i] = '?'; | |
333 table->dst2src[i] = '?'; | |
334 } | |
335 | |
336 pvcf = *cf; | |
337 cf->ctx = table; | |
338 cf->handler = ngx_charset_map; | |
339 cf->handler_conf = conf; | |
340 | |
341 rv = ngx_conf_parse(cf, NULL); | |
342 | |
343 *cf = pvcf; | |
344 | |
345 return rv; | |
346 } | |
347 | |
348 | |
349 static char * | |
350 ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | |
351 { | |
352 ngx_int_t src, dst; | |
353 ngx_str_t *value; | |
354 ngx_http_charset_tables_t *table; | |
355 | |
356 if (cf->args->nelts != 2) { | |
357 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number"); | |
358 return NGX_CONF_ERROR; | |
359 } | |
360 | |
361 value = cf->args->elts; | |
362 | |
363 src = ngx_hextoi(value[0].data, value[0].len); | |
364 if (src == NGX_ERROR || src > 255) { | |
365 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
366 "invalid value \"%V\"", &value[0]); | |
367 return NGX_CONF_ERROR; | |
368 } | |
369 | |
370 dst = ngx_hextoi(value[1].data, value[1].len); | |
371 if (dst == NGX_ERROR || dst > 255) { | |
372 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
373 "invalid value \"%V\"", &value[1]); | |
374 return NGX_CONF_ERROR; | |
375 } | |
376 | |
377 table = cf->ctx; | |
378 | |
379 table->src2dst[src] = (char) dst; | |
380 table->dst2src[dst] = (char) src; | |
381 | |
382 return NGX_CONF_OK; | |
383 } | |
384 | |
385 | |
386 static char * | |
387 ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
388 { | |
389 char *p = conf; | |
390 | |
391 ngx_int_t *cp; | |
392 ngx_str_t *value; | |
393 ngx_http_charset_t *charset; | |
394 ngx_http_charset_main_conf_t *mcf; | |
395 | |
396 cp = (ngx_int_t *) (p + cmd->offset); | |
397 | |
398 if (*cp != NGX_CONF_UNSET) { | |
399 return "is duplicate"; | |
400 } | |
401 | |
402 mcf = ngx_http_conf_get_module_main_conf(cf, | |
403 ngx_http_charset_filter_module); | |
404 | |
405 value = cf->args->elts; | |
406 | |
407 *cp = ngx_http_add_charset(&mcf->charsets, &value[1]); | |
408 if (*cp == NGX_ERROR) { | |
409 return NGX_CONF_ERROR; | |
410 } | |
411 | |
412 if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, source_charset)) { | |
413 charset = mcf->charsets.elts; | |
414 charset[*cp].server = 1; | |
415 } | |
416 | |
417 return NGX_CONF_OK; | |
418 } | |
419 | |
420 | |
421 static ngx_int_t | |
422 ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name) | |
423 { | |
424 ngx_uint_t i; | |
425 ngx_http_charset_t *c; | |
426 | |
427 c = charsets->elts; | |
428 for (i = 0; i < charsets->nelts; i++) { | |
429 if (name->len != c[i].name.len) { | |
430 continue; | |
431 } | |
432 | |
433 if (ngx_strcasecmp(name->data, c[i].name.data) == 0) { | |
434 break; | |
435 } | |
436 } | |
437 | |
438 if (i < charsets->nelts) { | |
439 return i; | |
440 } | |
441 | |
442 c = ngx_array_push(charsets); | |
443 if (c == NULL) { | |
444 return NGX_ERROR; | |
445 } | |
446 | |
447 c->tables = NULL; | |
448 c->name = *name; | |
449 c->server = 0; | |
450 | |
451 return i; | |
452 } | |
453 | |
454 | |
455 static ngx_int_t | |
456 ngx_http_charset_filter_init(ngx_cycle_t *cycle) | |
457 { | |
458 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
459 ngx_http_top_header_filter = ngx_http_charset_header_filter; | |
460 | |
461 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
462 ngx_http_top_body_filter = ngx_http_charset_body_filter; | |
463 | |
464 return NGX_OK; | |
465 } | |
466 | |
467 | |
468 static void * | |
469 ngx_http_charset_create_main_conf(ngx_conf_t *cf) | |
470 { | |
471 ngx_http_charset_main_conf_t *mcf; | |
472 | |
473 mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t)); | |
474 if (mcf == NULL) { | |
475 return NGX_CONF_ERROR; | |
476 } | |
477 | |
478 if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t)) | |
479 == NGX_ERROR) | |
480 { | |
481 return NGX_CONF_ERROR; | |
482 } | |
483 | |
484 if (ngx_array_init(&mcf->tables, cf->pool, 4, | |
485 sizeof(ngx_http_charset_tables_t)) == NGX_ERROR) | |
486 { | |
487 return NGX_CONF_ERROR; | |
488 } | |
489 | |
490 return mcf; | |
491 } | |
492 | |
493 | |
494 static char * | |
495 ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf) | |
496 { | |
497 ngx_http_charset_main_conf_t *mcf = conf; | |
498 | |
499 ngx_uint_t i, n; | |
500 ngx_http_charset_t *charset; | |
501 ngx_http_charset_tables_t *tables; | |
502 | |
503 tables = mcf->tables.elts; | |
504 charset = mcf->charsets.elts; | |
505 | |
506 for (i = 0; i < mcf->charsets.nelts; i++) { | |
507 if (!charset[i].server) { | |
508 continue; | |
509 } | |
510 | |
511 charset[i].tables = ngx_pcalloc(cf->pool, | |
512 sizeof(char *) * mcf->charsets.nelts); | |
513 if (charset[i].tables == NULL) { | |
514 return NGX_CONF_ERROR; | |
515 } | |
516 | |
517 for (n = 0; n < mcf->tables.nelts; n++) { | |
518 if ((ngx_int_t) i == tables[n].src) { | |
519 charset[i].tables[tables[n].dst] = tables[n].src2dst; | |
520 continue; | |
521 } | |
522 | |
523 if ((ngx_int_t) i == tables[n].dst) { | |
524 charset[i].tables[tables[n].src] = tables[n].dst2src; | |
525 } | |
526 } | |
527 } | |
528 | |
529 for (i = 0; i < mcf->charsets.nelts; i++) { | |
530 if (!charset[i].server) { | |
531 continue; | |
532 } | |
533 | |
534 for (n = 0; n < mcf->charsets.nelts; n++) { | |
535 if (i == n) { | |
536 continue; | |
537 } | |
538 | |
539 if (charset[i].tables[n]) { | |
540 continue; | |
541 } | |
542 | |
543 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
544 " no \"charset_map\" between the charsets " | |
545 "\"%V\" and \"%V\"", | |
546 &charset[i].name, &charset[n].name); | |
547 return NGX_CONF_ERROR; | |
548 } | |
549 } | |
550 | |
551 return NGX_CONF_OK; | |
552 } | |
553 | |
554 | |
555 static void * | |
556 ngx_http_charset_create_loc_conf(ngx_conf_t *cf) | |
557 { | |
558 ngx_http_charset_loc_conf_t *lcf; | |
559 | |
560 lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t)); | |
561 if (lcf == NULL) { | |
562 return NGX_CONF_ERROR; | |
563 } | |
564 | |
565 lcf->enable = NGX_CONF_UNSET; | |
566 lcf->autodetect = NGX_CONF_UNSET; | |
567 lcf->default_charset = NGX_CONF_UNSET; | |
568 lcf->source_charset = NGX_CONF_UNSET; | |
569 | |
570 return lcf; | |
571 } | |
572 | |
573 | |
574 static char * | |
575 ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
576 { | |
577 ngx_http_charset_loc_conf_t *prev = parent; | |
578 ngx_http_charset_loc_conf_t *conf = child; | |
579 | |
580 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
581 ngx_conf_merge_value(conf->autodetect, prev->autodetect, 0); | |
582 | |
583 | |
584 if (conf->default_charset == NGX_CONF_UNSET) { | |
585 conf->default_charset = prev->default_charset; | |
586 } | |
587 | |
588 if (conf->source_charset == NGX_CONF_UNSET) { | |
589 conf->source_charset = prev->source_charset; | |
590 } | |
591 | |
592 if (conf->default_charset == NGX_CONF_UNSET | |
593 && conf->source_charset != NGX_CONF_UNSET) | |
594 { | |
595 conf->default_charset = conf->source_charset; | |
596 } | |
597 | |
598 if (conf->source_charset == NGX_CONF_UNSET | |
599 && conf->default_charset != NGX_CONF_UNSET) | |
600 { | |
601 conf->source_charset = conf->default_charset; | |
602 } | |
603 | |
604 if (conf->enable | |
605 && (conf->default_charset == NGX_CONF_UNSET | |
606 || conf->source_charset == NGX_CONF_UNSET)) | |
607 { | |
608 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
609 "the \"source_charset\" or \"default_charset\" " | |
610 "must be specified when \"charset\" is on"); | |
611 return NGX_CONF_ERROR; | |
612 } | |
613 | |
614 return NGX_CONF_OK; | |
615 } |