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_http.h>
11
12
13 /*
14 * The module can check browser versions conforming to the following formats:
15 * X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be
16 * 4000, 4000.99, 4000.99.99, and 4000.99.99.99.
17 */
18
19
20 #define NGX_HTTP_MODERN_BROWSER 0
21 #define NGX_HTTP_ANCIENT_BROWSER 1
22
23
24 typedef struct {
25 u_char browser[12];
26 size_t skip;
27 size_t add;
28 u_char name[12];
29 } ngx_http_modern_browser_mask_t;
30
31
32 typedef struct {
33 ngx_uint_t version;
34 size_t skip;
35 size_t add;
36 u_char name[12];
37 } ngx_http_modern_browser_t;
38
39
40 typedef struct {
41 ngx_array_t *modern_browsers;
42 ngx_array_t *ancient_browsers;
43 ngx_http_variable_value_t *modern_browser_value;
44 ngx_http_variable_value_t *ancient_browser_value;
45
46 unsigned modern_unlisted_browsers:1;
47 unsigned netscape4:1;
48 } ngx_http_browser_conf_t;
49
50
51 static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
52 ngx_http_variable_value_t *v, uintptr_t data);
53 static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
54 ngx_http_variable_value_t *v, uintptr_t data);
55
56 static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
57 ngx_http_browser_conf_t *cf);
58
59 static ngx_int_t ngx_http_browser_add_variables(ngx_conf_t *cf);
60 static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
61 static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
62 void *child);
63 static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
64 const void *two);
65 static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
66 void *conf);
67 static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
68 void *conf);
69 static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
70 void *conf);
71 static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
72 void *conf);
73
74
75 static ngx_command_t ngx_http_browser_commands[] = {
76
77 { ngx_string("modern_browser"),
78 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
79 ngx_http_modern_browser,
80 NGX_HTTP_LOC_CONF_OFFSET,
81 0,
82 NULL },
83
84 { ngx_string("ancient_browser"),
85 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
86 ngx_http_ancient_browser,
87 NGX_HTTP_LOC_CONF_OFFSET,
88 0,
89 NULL },
90
91 { ngx_string("modern_browser_value"),
92 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
93 ngx_http_modern_browser_value,
94 NGX_HTTP_LOC_CONF_OFFSET,
95 0,
96 NULL },
97
98 { ngx_string("ancient_browser_value"),
99 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
100 ngx_http_ancient_browser_value,
101 NGX_HTTP_LOC_CONF_OFFSET,
102 0,
103 NULL },
104
105 ngx_null_command
106 };
107
108
109 static ngx_http_module_t ngx_http_browser_module_ctx = {
110 ngx_http_browser_add_variables, /* preconfiguration */
111 NULL, /* postconfiguration */
112
113 NULL, /* create main configuration */
114 NULL, /* init main configuration */
115
116 NULL, /* create server configuration */
117 NULL, /* merge server configuration */
118
119 ngx_http_browser_create_conf, /* create location configuration */
120 ngx_http_browser_merge_conf /* merge location configuration */
121 };
122
123
124 ngx_module_t ngx_http_browser_module = {
125 NGX_MODULE_V1,
126 &ngx_http_browser_module_ctx, /* module context */
127 ngx_http_browser_commands, /* module directives */
128 NGX_HTTP_MODULE, /* module type */
129 NULL, /* init master */
130 NULL, /* init module */
131 NULL, /* init process */
132 NULL, /* init thread */
133 NULL, /* exit thread */
134 NULL, /* exit process */
135 NULL, /* exit master */
136 NGX_MODULE_V1_PADDING
137 };
138
139
140 static ngx_http_modern_browser_mask_t ngx_http_modern_browser_masks[] = {
141
142 /* Opera must be the first browser to check */
143
144 /*
145 * "Opera/7.50 (X11; FreeBSD i386; U) [en]"
146 * "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50 [en]"
147 * "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50 [en]"
148 * "Opera/8.0 (Windows NT 5.1; U; ru)"
149 * "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"
150 * "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"
151 */
152
153 { "opera",
154 0,
155 sizeof("Opera ") - 1,
156 "Opera"},
157
158 /* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */
159
160 { "msie",
161 sizeof("Mozilla/4.0 (compatible; ") - 1,
162 sizeof("MSIE ") - 1,
163 "MSIE "},
164
165 /*
166 * "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610"
167 * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006"
168 * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206
169 * Firefox/0.8"
170 * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)
171 * Gecko/20050511 Firefox/1.0.4"
172 * "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729
173 * Firefox/1.5.0.5"
174 */
175
176 { "gecko",
177 sizeof("Mozilla/5.0 (") - 1,
178 sizeof("rv:") - 1,
179 "rv:"},
180
181 /*
182 * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2
183 * (KHTML, like Gecko) Safari/125.7"
184 * "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413
185 * (KHTML, like Gecko) Safari/413"
186 * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418
187 * (KHTML, like Gecko) Safari/417.9.3"
188 * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8
189 * (KHTML, like Gecko) Safari/419.3"
190 */
191
192 { "safari",
193 sizeof("Mozilla/5.0 (") - 1,
194 sizeof("Safari/") - 1,
195 "Safari/"},
196
197 /*
198 * "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"
199 * "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"
200 * "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1
201 * (like Gecko)"
202 */
203
204 { "konqueror",
205 sizeof("Mozilla/5.0 (compatible; ") - 1,
206 sizeof("Konqueror/") - 1,
207 "Konqueror/"},
208
209 { "", 0, 0, "" }
210
211 };
212
213
214 static ngx_http_variable_t ngx_http_browser_vars[] = {
215
216 { ngx_string("msie"), NULL, ngx_http_msie_variable,
217 0, NGX_HTTP_VAR_CHANGEABLE, 0 },
218
219 { ngx_string("modern_browser"), NULL, ngx_http_browser_variable,
220 NGX_HTTP_MODERN_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },
221
222 { ngx_string("ancient_browser"), NULL, ngx_http_browser_variable,
223 NGX_HTTP_ANCIENT_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },
224
225 ngx_http_null_variable
226 };
227
228
229 static ngx_int_t
ngx_http_browser_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)230 ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
231 uintptr_t data)
232 {
233 ngx_uint_t rc;
234 ngx_http_browser_conf_t *cf;
235
236 cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);
237
238 rc = ngx_http_browser(r, cf);
239
240 if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {
241 *v = *cf->modern_browser_value;
242 return NGX_OK;
243 }
244
245 if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
246 *v = *cf->ancient_browser_value;
247 return NGX_OK;
248 }
249
250 *v = ngx_http_variable_null_value;
251 return NGX_OK;
252 }
253
254
255 static ngx_uint_t
ngx_http_browser(ngx_http_request_t * r,ngx_http_browser_conf_t * cf)256 ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)
257 {
258 size_t len;
259 u_char *name, *ua, *last, c;
260 ngx_str_t *ancient;
261 ngx_uint_t i, version, ver, scale;
262 ngx_http_modern_browser_t *modern;
263
264 if (r->headers_in.user_agent == NULL) {
265 if (cf->modern_unlisted_browsers) {
266 return NGX_HTTP_MODERN_BROWSER;
267 }
268
269 return NGX_HTTP_ANCIENT_BROWSER;
270 }
271
272 ua = r->headers_in.user_agent->value.data;
273 len = r->headers_in.user_agent->value.len;
274 last = ua + len;
275
276 if (cf->modern_browsers) {
277 modern = cf->modern_browsers->elts;
278
279 for (i = 0; i < cf->modern_browsers->nelts; i++) {
280 name = ua + modern[i].skip;
281
282 if (name >= last) {
283 continue;
284 }
285
286 name = (u_char *) ngx_strstr(name, modern[i].name);
287
288 if (name == NULL) {
289 continue;
290 }
291
292 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
293 "browser: \"%s\"", name);
294
295 name += modern[i].add;
296
297 if (name >= last) {
298 continue;
299 }
300
301 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
302 "version: \"%ui\" \"%s\"", modern[i].version, name);
303
304 version = 0;
305 ver = 0;
306 scale = 1000000;
307
308 while (name < last) {
309
310 c = *name++;
311
312 if (c >= '0' && c <= '9') {
313 ver = ver * 10 + (c - '0');
314 continue;
315 }
316
317 if (c == '.') {
318 version += ver * scale;
319
320 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
321 "version: \"%ui\" \"%ui\"",
322 modern[i].version, version);
323
324 if (version > modern[i].version) {
325 return NGX_HTTP_MODERN_BROWSER;
326 }
327
328 ver = 0;
329 scale /= 100;
330 continue;
331 }
332
333 break;
334 }
335
336 version += ver * scale;
337
338 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
339 "version: \"%ui\" \"%ui\"",
340 modern[i].version, version);
341
342 if (version >= modern[i].version) {
343 return NGX_HTTP_MODERN_BROWSER;
344 }
345
346 return NGX_HTTP_ANCIENT_BROWSER;
347 }
348
349 if (!cf->modern_unlisted_browsers) {
350 return NGX_HTTP_ANCIENT_BROWSER;
351 }
352 }
353
354 if (cf->netscape4) {
355 if (len > sizeof("Mozilla/4.72 ") - 1
356 && ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0
357 && ua[8] > '0' && ua[8] < '5')
358 {
359 return NGX_HTTP_ANCIENT_BROWSER;
360 }
361 }
362
363 if (cf->ancient_browsers) {
364 ancient = cf->ancient_browsers->elts;
365
366 for (i = 0; i < cf->ancient_browsers->nelts; i++) {
367 if (len >= ancient[i].len
368 && ngx_strstr(ua, ancient[i].data) != NULL)
369 {
370 return NGX_HTTP_ANCIENT_BROWSER;
371 }
372 }
373 }
374
375 if (cf->modern_unlisted_browsers) {
376 return NGX_HTTP_MODERN_BROWSER;
377 }
378
379 return NGX_HTTP_ANCIENT_BROWSER;
380 }
381
382
383 static ngx_int_t
ngx_http_msie_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)384 ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
385 uintptr_t data)
386 {
387 if (r->headers_in.msie) {
388 *v = ngx_http_variable_true_value;
389 return NGX_OK;
390 }
391
392 *v = ngx_http_variable_null_value;
393 return NGX_OK;
394 }
395
396
397 static ngx_int_t
ngx_http_browser_add_variables(ngx_conf_t * cf)398 ngx_http_browser_add_variables(ngx_conf_t *cf)
399 {
400 ngx_http_variable_t *var, *v;
401
402 for (v = ngx_http_browser_vars; v->name.len; v++) {
403
404 var = ngx_http_add_variable(cf, &v->name, v->flags);
405 if (var == NULL) {
406 return NGX_ERROR;
407 }
408
409 var->get_handler = v->get_handler;
410 var->data = v->data;
411 }
412
413 return NGX_OK;
414 }
415
416
417 static void *
ngx_http_browser_create_conf(ngx_conf_t * cf)418 ngx_http_browser_create_conf(ngx_conf_t *cf)
419 {
420 ngx_http_browser_conf_t *conf;
421
422 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
423 if (conf == NULL) {
424 return NULL;
425 }
426
427 /*
428 * set by ngx_pcalloc():
429 *
430 * conf->modern_browsers = NULL;
431 * conf->ancient_browsers = NULL;
432 * conf->modern_browser_value = NULL;
433 * conf->ancient_browser_value = NULL;
434 *
435 * conf->modern_unlisted_browsers = 0;
436 * conf->netscape4 = 0;
437 */
438
439 return conf;
440 }
441
442
443 static char *
ngx_http_browser_merge_conf(ngx_conf_t * cf,void * parent,void * child)444 ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)
445 {
446 ngx_http_browser_conf_t *prev = parent;
447 ngx_http_browser_conf_t *conf = child;
448
449 ngx_uint_t i, n;
450 ngx_http_modern_browser_t *browsers, *opera;
451
452 /*
453 * At the merge the skip field is used to store the browser slot,
454 * it will be used in sorting and then will overwritten
455 * with a real skip value. The zero value means Opera.
456 */
457
458 if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {
459 conf->modern_browsers = prev->modern_browsers;
460 conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;
461
462 } else if (conf->modern_browsers != NULL) {
463 browsers = conf->modern_browsers->elts;
464
465 for (i = 0; i < conf->modern_browsers->nelts; i++) {
466 if (browsers[i].skip == 0) {
467 goto found;
468 }
469 }
470
471 /*
472 * Opera may contain MSIE string, so if Opera was not enumerated
473 * as modern browsers, then add it and set a unreachable version
474 */
475
476 opera = ngx_array_push(conf->modern_browsers);
477 if (opera == NULL) {
478 return NGX_CONF_ERROR;
479 }
480
481 opera->skip = 0;
482 opera->version = 4001000000U;
483
484 browsers = conf->modern_browsers->elts;
485
486 found:
487
488 ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,
489 sizeof(ngx_http_modern_browser_t),
490 ngx_http_modern_browser_sort);
491
492 for (i = 0; i < conf->modern_browsers->nelts; i++) {
493 n = browsers[i].skip;
494
495 browsers[i].skip = ngx_http_modern_browser_masks[n].skip;
496 browsers[i].add = ngx_http_modern_browser_masks[n].add;
497 (void) ngx_cpystrn(browsers[i].name,
498 ngx_http_modern_browser_masks[n].name, 12);
499 }
500 }
501
502 if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {
503 conf->ancient_browsers = prev->ancient_browsers;
504 conf->netscape4 = prev->netscape4;
505 }
506
507 if (conf->modern_browser_value == NULL) {
508 conf->modern_browser_value = prev->modern_browser_value;
509 }
510
511 if (conf->modern_browser_value == NULL) {
512 conf->modern_browser_value = &ngx_http_variable_true_value;
513 }
514
515 if (conf->ancient_browser_value == NULL) {
516 conf->ancient_browser_value = prev->ancient_browser_value;
517 }
518
519 if (conf->ancient_browser_value == NULL) {
520 conf->ancient_browser_value = &ngx_http_variable_true_value;
521 }
522
523 return NGX_CONF_OK;
524 }
525
526
527 static int ngx_libc_cdecl
ngx_http_modern_browser_sort(const void * one,const void * two)528 ngx_http_modern_browser_sort(const void *one, const void *two)
529 {
530 ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;
531 ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;
532
533 return (first->skip - second->skip);
534 }
535
536
537 static char *
ngx_http_modern_browser(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)538 ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
539 {
540 ngx_http_browser_conf_t *bcf = conf;
541
542 u_char c;
543 ngx_str_t *value;
544 ngx_uint_t i, n, version, ver, scale;
545 ngx_http_modern_browser_t *browser;
546 ngx_http_modern_browser_mask_t *mask;
547
548 value = cf->args->elts;
549
550 if (cf->args->nelts == 2) {
551 if (ngx_strcmp(value[1].data, "unlisted") == 0) {
552 bcf->modern_unlisted_browsers = 1;
553 return NGX_CONF_OK;
554 }
555
556 return NGX_CONF_ERROR;
557 }
558
559 if (bcf->modern_browsers == NULL) {
560 bcf->modern_browsers = ngx_array_create(cf->pool, 5,
561 sizeof(ngx_http_modern_browser_t));
562 if (bcf->modern_browsers == NULL) {
563 return NGX_CONF_ERROR;
564 }
565 }
566
567 browser = ngx_array_push(bcf->modern_browsers);
568 if (browser == NULL) {
569 return NGX_CONF_ERROR;
570 }
571
572 mask = ngx_http_modern_browser_masks;
573
574 for (n = 0; mask[n].browser[0] != '\0'; n++) {
575 if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
576 goto found;
577 }
578 }
579
580 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
581 "unknown browser name \"%V\"", &value[1]);
582
583 return NGX_CONF_ERROR;
584
585 found:
586
587 /*
588 * at this stage the skip field is used to store the browser slot,
589 * it will be used in sorting in merge stage and then will overwritten
590 * with a real value
591 */
592
593 browser->skip = n;
594
595 version = 0;
596 ver = 0;
597 scale = 1000000;
598
599 for (i = 0; i < value[2].len; i++) {
600
601 c = value[2].data[i];
602
603 if (c >= '0' && c <= '9') {
604 ver = ver * 10 + (c - '0');
605 continue;
606 }
607
608 if (c == '.') {
609 version += ver * scale;
610 ver = 0;
611 scale /= 100;
612 continue;
613 }
614
615 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
616 "invalid browser version \"%V\"", &value[2]);
617
618 return NGX_CONF_ERROR;
619 }
620
621 version += ver * scale;
622
623 browser->version = version;
624
625 return NGX_CONF_OK;
626 }
627
628
629 static char *
ngx_http_ancient_browser(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)630 ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
631 {
632 ngx_http_browser_conf_t *bcf = conf;
633
634 ngx_str_t *value, *browser;
635 ngx_uint_t i;
636
637 value = cf->args->elts;
638
639 for (i = 1; i < cf->args->nelts; i++) {
640 if (ngx_strcmp(value[i].data, "netscape4") == 0) {
641 bcf->netscape4 = 1;
642 continue;
643 }
644
645 if (bcf->ancient_browsers == NULL) {
646 bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
647 sizeof(ngx_str_t));
648 if (bcf->ancient_browsers == NULL) {
649 return NGX_CONF_ERROR;
650 }
651 }
652
653 browser = ngx_array_push(bcf->ancient_browsers);
654 if (browser == NULL) {
655 return NGX_CONF_ERROR;
656 }
657
658 *browser = value[i];
659 }
660
661 return NGX_CONF_OK;
662 }
663
664
665 static char *
ngx_http_modern_browser_value(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)666 ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
667 {
668 ngx_http_browser_conf_t *bcf = conf;
669
670 ngx_str_t *value;
671
672 bcf->modern_browser_value = ngx_palloc(cf->pool,
673 sizeof(ngx_http_variable_value_t));
674 if (bcf->modern_browser_value == NULL) {
675 return NGX_CONF_ERROR;
676 }
677
678 value = cf->args->elts;
679
680 bcf->modern_browser_value->len = value[1].len;
681 bcf->modern_browser_value->valid = 1;
682 bcf->modern_browser_value->no_cacheable = 0;
683 bcf->modern_browser_value->not_found = 0;
684 bcf->modern_browser_value->data = value[1].data;
685
686 return NGX_CONF_OK;
687 }
688
689
690 static char *
ngx_http_ancient_browser_value(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)691 ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
692 {
693 ngx_http_browser_conf_t *bcf = conf;
694
695 ngx_str_t *value;
696
697 bcf->ancient_browser_value = ngx_palloc(cf->pool,
698 sizeof(ngx_http_variable_value_t));
699 if (bcf->ancient_browser_value == NULL) {
700 return NGX_CONF_ERROR;
701 }
702
703 value = cf->args->elts;
704
705 bcf->ancient_browser_value->len = value[1].len;
706 bcf->ancient_browser_value->valid = 1;
707 bcf->ancient_browser_value->no_cacheable = 0;
708 bcf->ancient_browser_value->not_found = 0;
709 bcf->ancient_browser_value->data = value[1].data;
710
711 return NGX_CONF_OK;
712 }
713