comparison src/http/modules/ngx_http_browser_module.c @ 718:d488926182ea

ancient browsers support in ngx_http_browser_module
author Igor Sysoev <igor@sysoev.ru>
date Tue, 26 Sep 2006 12:19:34 +0000
parents 86600665553c
children 4ac89c5aa10d
comparison
equal deleted inserted replaced
717:08652aae6fab 718:d488926182ea
12 /* 12 /*
13 * The module can check browser versions conforming to the following formats: 13 * The module can check browser versions conforming to the following formats:
14 * X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be 14 * X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be
15 * 4000, 4000.99, 4000.99.99, and 4000.99.99.99. 15 * 4000, 4000.99, 4000.99.99, and 4000.99.99.99.
16 */ 16 */
17
18
19 #define NGX_HTTP_MODERN_BROWSER 0
20 #define NGX_HTTP_ANCIENT_BROWSER 1
17 21
18 22
19 typedef struct { 23 typedef struct {
20 u_char browser[12]; 24 u_char browser[12];
21 size_t skip; 25 size_t skip;
33 37
34 38
35 typedef struct { 39 typedef struct {
36 ngx_str_t name; 40 ngx_str_t name;
37 ngx_http_get_variable_pt handler; 41 ngx_http_get_variable_pt handler;
38 } ngx_http_browser_t; 42 uintptr_t data;
43 } ngx_http_browser_variable_t;
39 44
40 45
41 typedef struct { 46 typedef struct {
42 ngx_array_t *modern_browsers; 47 ngx_array_t *modern_browsers;
43 ngx_http_variable_value_t *value; 48 ngx_array_t *ancient_browsers;
49 ngx_http_variable_value_t *modern_browser_value;
50 ngx_http_variable_value_t *ancient_browser_value;
51
52 unsigned modern_unlisted_browsers:1;
53 unsigned netscape4:1;
44 } ngx_http_browser_conf_t; 54 } ngx_http_browser_conf_t;
45 55
46 56
47 static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r, 57 static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
48 ngx_http_variable_value_t *v, uintptr_t data); 58 ngx_http_variable_value_t *v, uintptr_t data);
59 static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
60 ngx_http_variable_value_t *v, uintptr_t data);
61
62 static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
63 ngx_http_browser_conf_t *cf);
64
49 static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf); 65 static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf);
50 static void *ngx_http_browser_create_conf(ngx_conf_t *cf); 66 static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
51 static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, 67 static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
52 void *child); 68 void *child);
53 static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one, 69 static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
54 const void *two); 70 const void *two);
55 static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, 71 static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
56 void *conf); 72 void *conf);
73 static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
74 void *conf);
57 static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, 75 static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
58 void *conf); 76 void *conf);
77 static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
78 void *conf);
59 79
60 80
61 static ngx_command_t ngx_http_browser_commands[] = { 81 static ngx_command_t ngx_http_browser_commands[] = {
62 82
63 { ngx_string("modern_browser"), 83 { ngx_string("modern_browser"),
64 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, 84 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
65 ngx_http_modern_browser, 85 ngx_http_modern_browser,
66 NGX_HTTP_LOC_CONF_OFFSET, 86 NGX_HTTP_LOC_CONF_OFFSET,
67 0, 87 0,
68 NULL }, 88 NULL },
69 89
90 { ngx_string("ancient_browser"),
91 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
92 ngx_http_ancient_browser,
93 NGX_HTTP_LOC_CONF_OFFSET,
94 0,
95 NULL },
96
70 { ngx_string("modern_browser_value"), 97 { ngx_string("modern_browser_value"),
71 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 98 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
72 ngx_http_modern_browser_value, 99 ngx_http_modern_browser_value,
100 NGX_HTTP_LOC_CONF_OFFSET,
101 0,
102 NULL },
103
104 { ngx_string("ancient_browser_value"),
105 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
106 ngx_http_ancient_browser_value,
73 NGX_HTTP_LOC_CONF_OFFSET, 107 NGX_HTTP_LOC_CONF_OFFSET,
74 0, 108 0,
75 NULL }, 109 NULL },
76 110
77 ngx_null_command 111 ngx_null_command
181 { "", 0, 0, "" } 215 { "", 0, 0, "" }
182 216
183 }; 217 };
184 218
185 219
186 static ngx_http_browser_t ngx_http_browsers[] = { 220 static ngx_http_browser_variable_t ngx_http_browsers[] = {
187 { ngx_string("msie"), ngx_http_msie_variable }, 221 { ngx_string("msie"), ngx_http_msie_variable, 0 },
188 { ngx_null_string, NULL } 222 { ngx_string("modern_browser"), ngx_http_browser_variable,
223 NGX_HTTP_MODERN_BROWSER },
224 { ngx_string("ancient_browser"), ngx_http_browser_variable,
225 NGX_HTTP_ANCIENT_BROWSER },
226 { ngx_null_string, NULL, 0 }
189 }; 227 };
190 228
191 229
192 static ngx_int_t 230 static ngx_int_t
193 ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 231 ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
194 uintptr_t data) 232 uintptr_t data)
195 { 233 {
234 ngx_uint_t rc;
235 ngx_http_browser_conf_t *cf;
236
237 cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);
238
239 rc = ngx_http_browser(r, cf);
240
241 if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {
242 *v = *cf->modern_browser_value;
243 return NGX_OK;
244 }
245
246 if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
247 *v = *cf->ancient_browser_value;
248 return NGX_OK;
249 }
250
251 *v = ngx_http_variable_null_value;
252 return NGX_OK;
253 }
254
255
256 static ngx_uint_t
257 ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)
258 {
259 size_t len;
196 u_char *name, *ua, *last, c; 260 u_char *name, *ua, *last, c;
261 ngx_str_t *ancient;
197 ngx_uint_t i, version, ver, scale; 262 ngx_uint_t i, version, ver, scale;
198 ngx_http_browser_conf_t *cf; 263 ngx_http_modern_browser_t *modern;
199 ngx_http_modern_browser_t *browsers;
200
201 cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);
202
203 if (cf->modern_browsers == NULL || cf->value == NULL) {
204 *v = ngx_http_variable_null_value;
205 return NGX_OK;
206 }
207
208 #if 0
209 if (!r->headers_in.msie && !r->headers_in.opera
210 && !r->headers_in.gecko && !r->headers_in.konqueror)
211 {
212 *v = ngx_http_variable_null_value;
213 return NGX_OK;
214 }
215 #endif
216 264
217 if (r->headers_in.user_agent == NULL) { 265 if (r->headers_in.user_agent == NULL) {
218 *v = ngx_http_variable_null_value; 266 if (cf->modern_unlisted_browsers) {
219 return NGX_OK; 267 return NGX_HTTP_MODERN_BROWSER;
220 } 268 }
221 269
270 return NGX_HTTP_ANCIENT_BROWSER;
271 }
222 272
223 ua = r->headers_in.user_agent->value.data; 273 ua = r->headers_in.user_agent->value.data;
224 last = ua + r->headers_in.user_agent->value.len; 274 len = r->headers_in.user_agent->value.len;
225 275 last = ua + len;
226 browsers = cf->modern_browsers->elts; 276
227 277 if (cf->modern_browsers) {
228 for (i = 0; i < cf->modern_browsers->nelts; i++) { 278 modern = cf->modern_browsers->elts;
229 name = ua + browsers[i].skip; 279
230 280 for (i = 0; i < cf->modern_browsers->nelts; i++) {
231 if (name >= last) { 281 name = ua + modern[i].skip;
232 continue; 282
233 } 283 if (name >= last) {
234
235 name = (u_char *) ngx_strstr(name, browsers[i].name);
236
237 if (name == NULL) {
238 continue;
239 }
240
241 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
242 "browser: \"%s\"", name);
243
244 name += browsers[i].add;
245
246 if (name >= last) {
247 continue;
248 }
249
250 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
251 "version: \"%ui\" \"%s\"", browsers[i].version, name);
252
253 version = 0;
254 ver = 0;
255 scale = 1000000;
256
257 while (name < last) {
258
259 c = *name++;
260
261 if (c >= '0' && c <= '9') {
262 ver = ver * 10 + (c - '0');
263 continue; 284 continue;
264 } 285 }
265 286
266 if (c == '.') { 287 name = (u_char *) ngx_strstr(name, modern[i].name);
267 version += ver * scale; 288
268 289 if (name == NULL) {
269 if (version > browsers[i].version) {
270 *v = *cf->value;
271 return NGX_OK;
272 }
273
274 ver = 0;
275 scale /= 100;
276 continue; 290 continue;
277 } 291 }
278 292
279 break; 293 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
280 } 294 "browser: \"%s\"", name);
281 295
282 version += ver * scale; 296 name += modern[i].add;
283 297
284 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 298 if (name >= last) {
285 "version: \"%ui\" \"%ui\"", 299 continue;
286 browsers[i].version, version); 300 }
287 301
288 if (version >= browsers[i].version) { 302 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
289 *v = *cf->value; 303 "version: \"%ui\" \"%s\"", modern[i].version, name);
290 return NGX_OK; 304
291 } 305 version = 0;
292 } 306 ver = 0;
293 307 scale = 1000000;
294 *v = ngx_http_variable_null_value; 308
295 309 while (name < last) {
296 return NGX_OK; 310
311 c = *name++;
312
313 if (c >= '0' && c <= '9') {
314 ver = ver * 10 + (c - '0');
315 continue;
316 }
317
318 if (c == '.') {
319 version += ver * scale;
320
321 if (version > modern[i].version) {
322 return NGX_HTTP_MODERN_BROWSER;
323 }
324
325 ver = 0;
326 scale /= 100;
327 continue;
328 }
329
330 break;
331 }
332
333 version += ver * scale;
334
335 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
336 "version: \"%ui\" \"%ui\"",
337 modern[i].version, version);
338
339 if (version >= modern[i].version) {
340 return NGX_HTTP_MODERN_BROWSER;
341 }
342 }
343
344 if (!cf->modern_unlisted_browsers) {
345 return NGX_HTTP_ANCIENT_BROWSER;
346 }
347 }
348
349 if (cf->netscape4) {
350 if (len > sizeof("Mozilla/4.72 ") - 1
351 && ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0
352 && ua[8] > '0' && ua[8] < '5')
353 {
354 return NGX_HTTP_ANCIENT_BROWSER;
355 }
356 }
357
358 if (cf->ancient_browsers) {
359 ancient = cf->ancient_browsers->elts;
360
361 for (i = 0; i < cf->ancient_browsers->nelts; i++) {
362 if (len >= ancient[i].len
363 && ngx_strstr(ua, ancient[i].data) != NULL)
364 {
365 return NGX_HTTP_ANCIENT_BROWSER;
366 }
367 }
368 }
369
370 if (cf->modern_unlisted_browsers) {
371 return NGX_HTTP_MODERN_BROWSER;
372 }
373
374 return NGX_HTTP_ANCIENT_BROWSER;
297 } 375 }
298 376
299 377
300 static ngx_int_t 378 static ngx_int_t
301 ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, 379 ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
302 uintptr_t data) 380 uintptr_t data)
303 { 381 {
304 if (r->headers_in.msie) { 382 if (r->headers_in.msie) {
305 *v = ngx_http_variable_true_value; 383 *v = ngx_http_variable_true_value;
306
307 return NGX_OK; 384 return NGX_OK;
308 } 385 }
309 386
310 *v = ngx_http_variable_null_value; 387 *v = ngx_http_variable_null_value;
311
312 return NGX_OK; 388 return NGX_OK;
313 } 389 }
314 390
315 391
316 static ngx_int_t 392 static ngx_int_t
317 ngx_http_browser_add_variable(ngx_conf_t *cf) 393 ngx_http_browser_add_variable(ngx_conf_t *cf)
318 { 394 {
319 ngx_http_browser_t *browser; 395 ngx_http_browser_variable_t *var;
320 ngx_http_variable_t *var; 396 ngx_http_variable_t *v;
321 397
322 for (browser = ngx_http_browsers; browser->name.len; browser++) { 398 for (var = ngx_http_browsers; var->name.len; var++) {
323 399
324 var = ngx_http_add_variable(cf, &browser->name, NGX_HTTP_VAR_CHANGABLE); 400 v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGABLE);
325 if (var == NULL) { 401 if (v == NULL) {
326 return NGX_ERROR; 402 return NGX_ERROR;
327 } 403 }
328 404
329 var->get_handler = browser->handler; 405 v->get_handler = var->handler;
406 v->data = var->data;
330 } 407 }
331 408
332 return NGX_OK; 409 return NGX_OK;
333 } 410 }
334 411
344 } 421 }
345 422
346 /* 423 /*
347 * set by ngx_pcalloc(): 424 * set by ngx_pcalloc():
348 * 425 *
349 * conf->browsers = NULL; 426 * conf->modern_browsers = NULL;
350 * conf->value = NULL; 427 * conf->ancient_browsers = NULL;
428 * conf->modern_browser_value = NULL;
429 * conf->ancient_browser_value = NULL;
430 *
431 * conf->modern_unlisted_browsers = 0;
432 * conf->netscape4 = 0;
351 */ 433 */
352 434
353 return conf; 435 return conf;
354 } 436 }
355 437
410 (void) ngx_cpystrn(browsers[i].name, 492 (void) ngx_cpystrn(browsers[i].name,
411 ngx_http_modern_browser_masks[n].name, 12); 493 ngx_http_modern_browser_masks[n].name, 12);
412 } 494 }
413 } 495 }
414 496
415 if (conf->value == NULL) { 497 if (conf->ancient_browsers == NULL) {
416 conf->value = prev->value; 498 conf->ancient_browsers = prev->ancient_browsers;
499 }
500
501 if (conf->modern_browser_value == NULL) {
502 conf->modern_browser_value = prev->modern_browser_value;
503 }
504
505 if (conf->modern_browser_value == NULL) {
506 conf->modern_browser_value = &ngx_http_variable_true_value;
507 }
508
509 if (conf->ancient_browser_value == NULL) {
510 conf->ancient_browser_value = prev->ancient_browser_value;
511 }
512
513 if (conf->ancient_browser_value == NULL) {
514 conf->ancient_browser_value = &ngx_http_variable_true_value;
417 } 515 }
418 516
419 return NGX_CONF_OK; 517 return NGX_CONF_OK;
420 } 518 }
421 519
439 ngx_str_t *value; 537 ngx_str_t *value;
440 ngx_uint_t i, n, version, ver, scale; 538 ngx_uint_t i, n, version, ver, scale;
441 ngx_http_modern_browser_t *browser; 539 ngx_http_modern_browser_t *browser;
442 ngx_http_modern_browser_mask_t *mask; 540 ngx_http_modern_browser_mask_t *mask;
443 541
542 value = cf->args->elts;
543
544 if (cf->args->nelts == 2) {
545 if (ngx_strcmp(value[1].data, "unlisted") == 0) {
546 bcf->modern_unlisted_browsers = 1;
547 return NGX_CONF_OK;
548 }
549
550 return NGX_CONF_ERROR;
551 }
552
444 if (bcf->modern_browsers == NULL) { 553 if (bcf->modern_browsers == NULL) {
445 bcf->modern_browsers = ngx_array_create(cf->pool, 4, 554 bcf->modern_browsers = ngx_array_create(cf->pool, 5,
446 sizeof(ngx_http_modern_browser_t)); 555 sizeof(ngx_http_modern_browser_t));
447 if (bcf->modern_browsers == NULL) { 556 if (bcf->modern_browsers == NULL) {
448 return NGX_CONF_ERROR; 557 return NGX_CONF_ERROR;
449 } 558 }
450 } 559 }
451 560
452 browser = ngx_array_push(bcf->modern_browsers); 561 browser = ngx_array_push(bcf->modern_browsers);
453 if (browser == NULL) { 562 if (browser == NULL) {
454 return NGX_CONF_ERROR; 563 return NGX_CONF_ERROR;
455 } 564 }
456
457 value = cf->args->elts;
458 565
459 mask = ngx_http_modern_browser_masks; 566 mask = ngx_http_modern_browser_masks;
460 567
461 for (n = 0; mask[n].browser[0] != '\0'; n++) { 568 for (n = 0; mask[n].browser[0] != '\0'; n++) {
462 if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) { 569 if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
512 return NGX_CONF_OK; 619 return NGX_CONF_OK;
513 } 620 }
514 621
515 622
516 static char * 623 static char *
624 ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
625 {
626 ngx_http_browser_conf_t *bcf = conf;
627
628 ngx_str_t *value, *browser;
629 ngx_uint_t i;
630
631 value = cf->args->elts;
632
633 for (i = 1; i < cf->args->nelts; i++) {
634 if (ngx_strcmp(value[i].data, "netscape4") == 0) {
635 bcf->netscape4 = 1;
636 continue;
637 }
638
639 if (bcf->ancient_browsers == NULL) {
640 bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
641 sizeof(ngx_str_t));
642 if (bcf->ancient_browsers == NULL) {
643 return NGX_CONF_ERROR;
644 }
645 }
646
647 browser = ngx_array_push(bcf->ancient_browsers);
648 if (browser == NULL) {
649 return NGX_CONF_ERROR;
650 }
651
652 *browser = value[i];
653 }
654
655 return NGX_CONF_OK;
656 }
657
658
659 static char *
517 ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 660 ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
518 { 661 {
519 ngx_http_browser_conf_t *bcf = conf; 662 ngx_http_browser_conf_t *bcf = conf;
520 663
521 ngx_str_t *value, name; 664 ngx_str_t *value;
522 ngx_http_variable_t *var; 665
666 bcf->modern_browser_value = ngx_palloc(cf->pool,
667 sizeof(ngx_http_variable_value_t));
668 if (bcf->modern_browser_value == NULL) {
669 return NGX_CONF_ERROR;
670 }
523 671
524 value = cf->args->elts; 672 value = cf->args->elts;
525 673
526 name.len = sizeof("browser") - 1; 674 bcf->modern_browser_value->len = value[1].len;
527 name.data = (u_char *) "browser"; 675 bcf->modern_browser_value->valid = 1;
528 676 bcf->modern_browser_value->no_cachable = 0;
529 var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGABLE); 677 bcf->modern_browser_value->not_found = 0;
530 if (var == NULL) { 678 bcf->modern_browser_value->data = value[1].data;
679
680 return NGX_CONF_OK;
681 }
682
683
684 static char *
685 ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
686 {
687 ngx_http_browser_conf_t *bcf = conf;
688
689 ngx_str_t *value;
690
691 bcf->ancient_browser_value = ngx_palloc(cf->pool,
692 sizeof(ngx_http_variable_value_t));
693 if (bcf->ancient_browser_value == NULL) {
531 return NGX_CONF_ERROR; 694 return NGX_CONF_ERROR;
532 } 695 }
533 696
534 var->get_handler = ngx_http_browser_variable; 697 value = cf->args->elts;
535 698
536 bcf->value = ngx_palloc(cf->pool, sizeof(ngx_http_variable_value_t)); 699 bcf->ancient_browser_value->len = value[1].len;
537 if (bcf->value == NULL) { 700 bcf->ancient_browser_value->valid = 1;
538 return NGX_CONF_ERROR; 701 bcf->ancient_browser_value->no_cachable = 0;
539 } 702 bcf->ancient_browser_value->not_found = 0;
540 703 bcf->ancient_browser_value->data = value[1].data;
541 bcf->value->len = value[1].len;
542 bcf->value->valid = 1;
543 bcf->value->no_cachable = 0;
544 bcf->value->not_found = 0;
545 bcf->value->data = value[1].data;
546 704
547 return NGX_CONF_OK; 705 return NGX_CONF_OK;
548 } 706 }