comparison src/http/modules/ngx_http_charset_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 4b2dafa26fe2
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
12 typedef struct {
13 char **tables;
14 ngx_str_t name;
15 unsigned server;
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 void 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 child */
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 ngx_http_charset_header_filter(ngx_http_request_t *r)
137 {
138 ngx_http_charset_t *charsets;
139 ngx_http_charset_ctx_t *ctx;
140 ngx_http_charset_loc_conf_t *lcf;
141 ngx_http_charset_main_conf_t *mcf;
142
143 mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
144 lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
145
146 if (lcf->enable == 0) {
147 return ngx_http_next_header_filter(r);
148 }
149
150 #if 0
151 if (lcf->default_charset.len == 0) {
152 return ngx_http_next_header_filter(r);
153 }
154 #endif
155
156 if (r->headers_out.content_type == NULL) {
157 return ngx_http_next_header_filter(r);
158 }
159
160 if (ngx_strncasecmp(r->headers_out.content_type->value.data,
161 "text/", 5) != 0
162 && ngx_strncasecmp(r->headers_out.content_type->value.data,
163 "application/x-javascript", 24) != 0)
164 {
165 return ngx_http_next_header_filter(r);
166 }
167
168 if (ngx_strstr(r->headers_out.content_type->value.data, "charset") != NULL)
169 {
170 return ngx_http_next_header_filter(r);
171 }
172
173 if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
174 && r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
175 {
176 /*
177 * do not set charset for the redirect because NN 4.x uses this
178 * charset instead of the next page charset
179 */
180
181 r->headers_out.charset.len = 0;
182 return ngx_http_next_header_filter(r);
183 }
184
185 if (r->headers_out.charset.len) {
186 return ngx_http_next_header_filter(r);
187 }
188
189 charsets = mcf->charsets.elts;
190 r->headers_out.charset = charsets[lcf->default_charset].name;
191
192 if (lcf->default_charset == lcf->source_charset) {
193 return ngx_http_next_header_filter(r);
194 }
195
196 ngx_http_create_ctx(r, ctx, ngx_http_charset_filter_module,
197 sizeof(ngx_http_charset_ctx_t), NGX_ERROR);
198
199 r->filter_need_in_memory = 1;
200
201 return ngx_http_next_header_filter(r);
202 }
203
204
205 static ngx_int_t ngx_http_charset_body_filter(ngx_http_request_t *r,
206 ngx_chain_t *in)
207 {
208 char *table;
209 ngx_chain_t *cl;
210 ngx_http_charset_t *charsets;
211 ngx_http_charset_ctx_t *ctx;
212 ngx_http_charset_loc_conf_t *lcf;
213 ngx_http_charset_main_conf_t *mcf;
214
215 ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);
216
217 if (ctx == NULL) {
218 return ngx_http_next_body_filter(r, in);
219 }
220
221 mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
222 lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
223
224 charsets = mcf->charsets.elts;
225 table = charsets[lcf->source_charset].tables[lcf->default_charset];
226
227 for (cl = in; cl; cl = cl->next) {
228 ngx_charset_recode(cl->buf, table);
229 }
230
231 return ngx_http_next_body_filter(r, in);
232 }
233
234
235 static void ngx_charset_recode(ngx_buf_t *b, char *table)
236 {
237 u_char *p, c;
238
239 for (p = b->pos; p < b->last; p++) {
240 c = *p;
241 *p = table[c];
242 }
243 }
244
245
246 static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
247 void *conf)
248 {
249 ngx_http_charset_main_conf_t *mcf = conf;
250
251 char *rv;
252 ngx_int_t src, dst;
253 ngx_uint_t i;
254 ngx_str_t *value;
255 ngx_conf_t pvcf;
256 ngx_http_charset_tables_t *table;
257
258 value = cf->args->elts;
259
260 src = ngx_http_add_charset(&mcf->charsets, &value[1]);
261 if (src == NGX_ERROR) {
262 return NGX_CONF_ERROR;
263 }
264
265 dst = ngx_http_add_charset(&mcf->charsets, &value[2]);
266 if (dst == NGX_ERROR) {
267 return NGX_CONF_ERROR;
268 }
269
270 if (src == dst) {
271 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
272 "\"charset_map\" between the same charsets "
273 "\"%s\" and \"%s\"",
274 value[1].data, value[2].data);
275 return NGX_CONF_ERROR;
276 }
277
278 table = mcf->tables.elts;
279 for (i = 0; i < mcf->tables.nelts; i++) {
280 if ((src == table->src && dst == table->dst)
281 || (src == table->dst && dst == table->src))
282 {
283 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
284 "duplicate \"charset_map\" between "
285 "\"%s\" and \"%s\"",
286 value[1].data, value[2].data);
287 return NGX_CONF_ERROR;
288 }
289 }
290
291 if (!(table = ngx_push_array(&mcf->tables))) {
292 return NGX_CONF_ERROR;
293 }
294
295 table->src = src;
296 table->dst = dst;
297
298 if (!(table->src2dst = ngx_palloc(cf->pool, 256))) {
299 return NGX_CONF_ERROR;
300 }
301
302 if (!(table->dst2src = ngx_palloc(cf->pool, 256))) {
303 return NGX_CONF_ERROR;
304 }
305
306 for (i = 0; i < 128; i++) {
307 table->src2dst[i] = (char) i;
308 table->dst2src[i] = (char) i;
309 }
310
311 for (/* void */; i < 256; i++) {
312 table->src2dst[i] = '?';
313 table->dst2src[i] = '?';
314 }
315
316 pvcf = *cf;
317 cf->ctx = table;
318 cf->handler = ngx_charset_map;
319 cf->handler_conf = conf;
320 rv = ngx_conf_parse(cf, NULL);
321 *cf = pvcf;
322
323 return rv;
324 }
325
326
327 static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
328 {
329 ngx_int_t src, dst;
330 ngx_str_t *value;
331 ngx_http_charset_tables_t *table;
332
333 if (cf->args->nelts != 2) {
334 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");
335 return NGX_CONF_ERROR;
336 }
337
338 value = cf->args->elts;
339
340 src = ngx_hextoi(value[0].data, value[0].len);
341 if (src == NGX_ERROR || src > 255) {
342 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
343 "invalid value \"%s\"", value[0].data);
344 return NGX_CONF_ERROR;
345 }
346
347 dst = ngx_hextoi(value[1].data, value[1].len);
348 if (dst == NGX_ERROR || dst > 255) {
349 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
350 "invalid value \"%s\"", value[1].data);
351 return NGX_CONF_ERROR;
352 }
353
354 table = cf->ctx;
355
356 table->src2dst[src] = (char) dst;
357 table->dst2src[dst] = (char) src;
358
359 return NGX_CONF_OK;
360 }
361
362
363 static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,
364 void *conf)
365 {
366 char *p = conf;
367
368 ngx_int_t *cp;
369 ngx_str_t *value;
370 ngx_http_charset_t *charset;
371 ngx_http_charset_main_conf_t *mcf;
372
373 cp = (ngx_int_t *) (p + cmd->offset);
374
375 if (*cp != NGX_CONF_UNSET) {
376 return "is duplicate";
377 }
378
379 mcf = ngx_http_conf_get_module_main_conf(cf,
380 ngx_http_charset_filter_module);
381
382 value = cf->args->elts;
383
384 *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);
385 if (*cp == NGX_ERROR) {
386 return NGX_CONF_ERROR;
387 }
388
389 if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, source_charset)) {
390 charset = mcf->charsets.elts;
391 charset[*cp].server = 1;
392 }
393
394 return NGX_CONF_OK;
395 }
396
397
398 static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)
399 {
400 ngx_uint_t i;
401 ngx_http_charset_t *c;
402
403 c = charsets->elts;
404 for (i = 0; i < charsets->nelts; i++) {
405 if (name->len != c[i].name.len) {
406 continue;
407 }
408
409 if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {
410 break;
411 }
412 }
413
414 if (i < charsets->nelts) {
415 return i;
416 }
417
418 if (!(c = ngx_push_array(charsets))) {
419 return NGX_ERROR;
420 }
421
422 c->name = *name;
423
424 return i;
425 }
426
427
428 static ngx_int_t ngx_http_charset_filter_init(ngx_cycle_t *cycle)
429 {
430 ngx_http_next_header_filter = ngx_http_top_header_filter;
431 ngx_http_top_header_filter = ngx_http_charset_header_filter;
432
433 ngx_http_next_body_filter = ngx_http_top_body_filter;
434 ngx_http_top_body_filter = ngx_http_charset_body_filter;
435
436 return NGX_OK;
437 }
438
439
440 static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf)
441 {
442 ngx_http_charset_main_conf_t *mcf;
443
444 if (!(mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t)))) {
445 return NGX_CONF_ERROR;
446 }
447
448 ngx_init_array(mcf->charsets, cf->pool, 5, sizeof(ngx_http_charset_t),
449 NGX_CONF_ERROR);
450
451 ngx_init_array(mcf->tables, cf->pool, 10, sizeof(ngx_http_charset_tables_t),
452 NGX_CONF_ERROR);
453
454 return mcf;
455 }
456
457
458 static char *ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf)
459 {
460 ngx_http_charset_main_conf_t *mcf = conf;
461
462 ngx_uint_t i, n;
463 ngx_http_charset_t *charset;
464 ngx_http_charset_tables_t *tables;
465
466 tables = mcf->tables.elts;
467 charset = mcf->charsets.elts;
468
469 for (i = 0; i < mcf->charsets.nelts; i++) {
470 if (!charset[i].server) {
471 continue;
472 }
473
474 charset[i].tables = ngx_pcalloc(cf->pool,
475 sizeof(char *) * mcf->charsets.nelts);
476
477 if (charset[i].tables == NULL) {
478 return NGX_CONF_ERROR;
479 }
480
481 for (n = 0; n < mcf->tables.nelts; n++) {
482 if ((ngx_int_t) i == tables[n].src) {
483 charset[i].tables[tables[n].dst] = tables[n].src2dst;
484 continue;
485 }
486
487 if ((ngx_int_t) i == tables[n].dst) {
488 charset[i].tables[tables[n].src] = tables[n].dst2src;
489 }
490 }
491 }
492
493 for (i = 0; i < mcf->charsets.nelts; i++) {
494 if (!charset[i].server) {
495 continue;
496 }
497
498 for (n = 0; n < mcf->charsets.nelts; n++) {
499 if (i == n) {
500 continue;
501 }
502
503 if (charset[i].tables[n]) {
504 continue;
505 }
506
507 ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
508 " no \"charset_map\" between the charsets "
509 "\"%s\" and \"%s\"",
510 charset[i].name.data, charset[n].name.data);
511 return NGX_CONF_ERROR;
512 }
513 }
514
515 return NGX_CONF_OK;
516 }
517
518
519 static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf)
520 {
521 ngx_http_charset_loc_conf_t *lcf;
522
523 if (!(lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t)))) {
524 return NGX_CONF_ERROR;
525 }
526
527 lcf->enable = NGX_CONF_UNSET;
528 lcf->autodetect = NGX_CONF_UNSET;
529 lcf->default_charset = NGX_CONF_UNSET;
530 lcf->source_charset = NGX_CONF_UNSET;
531
532 return lcf;
533 }
534
535
536 static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,
537 void *parent, void *child)
538 {
539 ngx_http_charset_loc_conf_t *prev = parent;
540 ngx_http_charset_loc_conf_t *conf = child;
541
542 ngx_conf_merge_value(conf->enable, prev->enable, 0);
543 ngx_conf_merge_value(conf->autodetect, prev->autodetect, 0);
544
545 if (conf->source_charset == NGX_CONF_UNSET) {
546 conf->source_charset = prev->source_charset;
547 }
548
549 ngx_conf_merge_value(conf->default_charset, prev->default_charset,
550 conf->source_charset);
551
552 return NGX_CONF_OK;
553 }