Apache HTTPD
mod_negotiation.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * mod_negotiation.c: keeps track of MIME types the client is willing to
19 * accept, and contains code to handle type arbitration.
20 *
21 * rst
22 */
23
24#include "apr.h"
25#include "apr_strings.h"
26#include "apr_file_io.h"
27#include "apr_lib.h"
28
29#define APR_WANT_STRFUNC
30#include "apr_want.h"
31
32#include "ap_config.h"
33#include "httpd.h"
34#include "http_config.h"
35#include "http_request.h"
36#include "http_protocol.h"
37#include "http_core.h"
38#include "http_log.h"
39#include "util_script.h"
40
41
42#define MAP_FILE_MAGIC_TYPE "application/x-type-map"
43
44/* Commands --- configuring document caching on a per (virtual?)
45 * server basis...
46 */
47
52
53/* forcelangpriority flags
54 */
55#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
56#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
57#define FLP_PREFER 2 /* Use language_priority rather than MC */
58#define FLP_FALLBACK 4 /* Use language_priority rather than NA */
59
60#define FLP_DEFAULT FLP_PREFER
61
62/* env evaluation
63 */
64#define DISCARD_ALL_ENCODINGS 1 /* no-gzip */
65#define DISCARD_ALL_BUT_HTML 2 /* gzip-only-text/html */
66
67module AP_MODULE_DECLARE_DATA negotiation_module;
68
70{
72 sizeof(neg_dir_config));
73
75 new->language_priority = NULL;
76 return new;
77}
78
79static void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
80{
84 sizeof(neg_dir_config));
85
86 /* give priority to the config in the subdirectory */
89 : base->forcelangpriority;
90 new->language_priority = add->language_priority
92 : base->language_priority;
93 return new;
94}
95
96static const char *set_language_priority(cmd_parms *cmd, void *n_,
97 const char *lang)
98{
100 const char **langp;
101
102 if (!n->language_priority)
103 n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *));
104
105 langp = (const char **) apr_array_push(n->language_priority);
106 *langp = lang;
107 return NULL;
108}
109
110static const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w)
111{
113
114 if (!strcasecmp(w, "None")) {
115 if (n->forcelangpriority & ~FLP_NONE) {
116 return "Cannot combine ForceLanguagePriority options with None";
117 }
119 }
120 else if (!strcasecmp(w, "Prefer")) {
121 if (n->forcelangpriority & FLP_NONE) {
122 return "Cannot combine ForceLanguagePriority options None and "
123 "Prefer";
124 }
125 n->forcelangpriority |= FLP_PREFER;
126 }
127 else if (!strcasecmp(w, "Fallback")) {
128 if (n->forcelangpriority & FLP_NONE) {
129 return "Cannot combine ForceLanguagePriority options None and "
130 "Fallback";
131 }
132 n->forcelangpriority |= FLP_FALLBACK;
133 }
134 else {
135 return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ",
136 w, NULL);
137 }
138
139 return NULL;
140}
141
142static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
143 int arg)
144{
145 ap_set_module_config(cmd->server->module_config, &negotiation_module,
146 (arg ? "Cache" : NULL));
147 return NULL;
148}
149
151{
152 return (ap_get_module_config(s->module_config,
153 &negotiation_module) != NULL);
154}
155
157{
158 AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF,
159 "Either 'on' or 'off' (default)"),
160 AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL,
162 "space-delimited list of MIME language abbreviations"),
163 AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL,
165 "Force LanguagePriority elections, either None, or "
166 "Fallback and/or Prefer"),
167 {NULL}
168};
169
170/*
171 * Record of available info on a media type specified by the client
172 * (we also use 'em for encodings and languages)
173 */
174
175typedef struct accept_rec {
176 char *name; /* MUST be lowercase */
177 float quality;
178 float level;
179 char *charset; /* for content-type only */
181
182/*
183 * Record of available info on a particular variant
184 *
185 * Note that a few of these fields are updated by the actual negotiation
186 * code. These are:
187 *
188 * level_matched --- initialized to zero. Set to the value of level
189 * if the client actually accepts this media type at that
190 * level (and *not* if it got in on a wildcard). See level_cmp
191 * below.
192 * mime_stars -- initialized to zero. Set to the number of stars
193 * present in the best matching Accept header element.
194 * 1 for star/star, 2 for type/star and 3 for
195 * type/subtype.
196 *
197 * definite -- initialized to 1. Set to 0 if there is a match which
198 * makes the variant non-definite according to the rules
199 * in rfc2296.
200 */
201
202typedef struct var_rec {
203 request_rec *sub_req; /* May be NULL (is, for map files) */
204 const char *mime_type; /* MUST be lowercase */
205 const char *file_name; /* Set to 'this' (for map file body content) */
206 apr_off_t body; /* Only for map file body content */
207 const char *content_encoding;
208 apr_array_header_t *content_languages; /* list of lang. for this variant */
209 const char *content_charset;
210 const char *description;
211
212 /* The next five items give the quality values for the dimensions
213 * of negotiation for this variant. They are obtained from the
214 * appropriate header lines, except for source_quality, which
215 * is obtained from the variant itself (the 'qs' parameter value
216 * from the variant's mime-type). Apart from source_quality,
217 * these values are set when we find the quality for each variant
218 * (see best_match()). source_quality is set from the 'qs' parameter
219 * of the variant description or mime type: see set_mime_fields().
220 */
221 float lang_quality; /* quality of this variant's language */
222 float encoding_quality; /* ditto encoding */
223 float charset_quality; /* ditto charset */
224 float mime_type_quality; /* ditto media type */
225 float source_quality; /* source quality for this variant */
226
227 /* Now some special values */
228 float level; /* Auxiliary to content-type... */
229 apr_off_t bytes; /* content length, if known */
230 int lang_index; /* Index into LanguagePriority list */
231 int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
232
233 /* Above are all written-once properties of the variant. The
234 * three fields below are changed during negotiation:
235 */
236
241
242/* Something to carry around the state of negotiation (and to keep
243 * all of this thread-safe)...
244 */
245
246typedef struct {
250 char *dir_name;
251 int accept_q; /* 1 if an Accept item has a q= param */
252 float default_lang_quality; /* fiddle lang q for variants with no lang */
253
254 /* the array pointers below are NULL if the corresponding accept
255 * headers are not present
256 */
257 apr_array_header_t *accepts; /* accept_recs */
261
262 apr_array_header_t *avail_vars; /* available variants */
263
264 int count_multiviews_variants; /* number of variants found on disk */
265
266 int is_transparent; /* 1 if this resource is trans. negotiable */
267
268 int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */
269 int ua_supports_trans; /* 1 if ua supports trans negotiation */
270 int send_alternates; /* 1 if we want to send an Alternates header */
271 int may_choose; /* 1 if we may choose a variant for the client */
272 int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */
274
275/* A few functions to manipulate var_recs.
276 * Cleaning out the fields...
277 */
278
280{
281 mime_info->sub_req = NULL;
282 mime_info->mime_type = "";
283 mime_info->file_name = "";
284 mime_info->body = 0;
285 mime_info->content_encoding = NULL;
286 mime_info->content_languages = NULL;
287 mime_info->content_charset = "";
288 mime_info->description = "";
289
290 mime_info->is_pseudo_html = 0;
291 mime_info->level = 0.0f;
292 mime_info->level_matched = 0.0f;
293 mime_info->bytes = -1;
294 mime_info->lang_index = -1;
295 mime_info->mime_stars = 0;
296 mime_info->definite = 1;
297
298 mime_info->charset_quality = 1.0f;
299 mime_info->encoding_quality = 1.0f;
300 mime_info->lang_quality = 1.0f;
301 mime_info->mime_type_quality = 1.0f;
302 mime_info->source_quality = 0.0f;
303}
304
305/* Initializing the relevant fields of a variant record from the
306 * accept_info read out of its content-type, one way or another.
307 */
308
310{
311 var->mime_type = mime_info->name;
312 var->source_quality = mime_info->quality;
313 var->level = mime_info->level;
314 var->content_charset = mime_info->charset;
315
316 var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
317 || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE)
318 || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));
319}
320
321/* Create a variant list validator in r using info from vlistr. */
322
324{
325 /* Calculating the variant list validator is similar to
326 * calculating an etag for the source of the variant list
327 * information, so we use ap_make_etag(). Note that this
328 * validator can be 'weak' in extreme case.
329 */
330 ap_update_mtime(vlistr, vlistr->finfo.mtime);
332
333 /* ap_set_etag will later take r->vlist_validator into account
334 * when creating the etag header
335 */
336}
337
338
339/*****************************************************************
340 *
341 * Parsing (lists of) media types and their parameters, as seen in
342 * HTTPD header lines and elsewhere.
343 */
344
345/*
346 * parse quality value. atof(3) is not well-usable here, because it
347 * depends on the locale (argh).
348 *
349 * However, RFC 2616 states:
350 * 3.9 Quality Values
351 *
352 * [...] HTTP/1.1 applications MUST NOT generate more than three digits
353 * after the decimal point. User configuration of these values SHOULD also
354 * be limited in this fashion.
355 *
356 * qvalue = ( "0" [ "." 0*3DIGIT ] )
357 * | ( "1" [ "." 0*3("0") ] )
358 *
359 * This is quite easy. If the supplied string doesn't match the above
360 * definition (loosely), we simply return 1 (same as if there's no qvalue)
361 */
362
363static float atoq(const char *string)
364{
365 if (!string || !*string) {
366 return 1.0f;
367 }
368
369 while (apr_isspace(*string)) {
370 ++string;
371 }
372
373 /* be tolerant and accept qvalues without leading zero
374 * (also for backwards compat, where atof() was in use)
375 */
376 if (*string != '.' && *string++ != '0') {
377 return 1.0f;
378 }
379
380 if (*string == '.') {
381 /* better only one division later, than dealing with fscking
382 * IEEE format 0.1 factors ...
383 */
384 int i = 0;
385
386 if (*++string >= '0' && *string <= '9') {
387 i += (*string - '0') * 100;
388
389 if (*++string >= '0' && *string <= '9') {
390 i += (*string - '0') * 10;
391
392 if (*++string > '0' && *string <= '9') {
393 i += (*string - '0');
394 }
395 }
396 }
397
398 return (float)i / 1000.0f;
399 }
400
401 return 0.0f;
402}
403
404/*
405 * Get a single mime type entry --- one media type and parameters;
406 * enter the values we recognize into the argument accept_rec
407 */
408
409static const char *get_entry(apr_pool_t *p, accept_rec *result,
410 const char *accept_line)
411{
412 result->quality = 1.0f;
413 result->level = 0.0f;
414 result->charset = "";
415
416 /*
417 * Note that this handles what I gather is the "old format",
418 *
419 * Accept: text/html text/plain moo/zot
420 *
421 * without any compatibility kludges --- if the token after the
422 * MIME type begins with a semicolon, we know we're looking at parms,
423 * otherwise, we know we aren't. (So why all the pissing and moaning
424 * in the CERN server code? I must be missing something).
425 */
426
427 result->name = ap_get_token(p, &accept_line, 0);
428 ap_str_tolower(result->name); /* You want case insensitive,
429 * you'll *get* case insensitive.
430 */
431
432 /* KLUDGE!!! Default HTML to level 2.0 unless the browser
433 * *explicitly* says something else.
434 */
435
436 if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
437 result->level = 2.0f;
438 }
439 else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
440 result->level = 2.0f;
441 }
442 else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
443 result->level = 3.0f;
444 }
445
446 while (*accept_line == ';') {
447 /* Parameters ... */
448
449 char *parm;
450 char *cp;
451 char *end;
452
453 ++accept_line;
455
456 /* Look for 'var = value' --- and make sure the var is in lcase. */
457
458 for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
459 *cp = apr_tolower(*cp);
460 }
461
462 if (!*cp) {
463 continue; /* No '='; just ignore it. */
464 }
465
466 *cp++ = '\0'; /* Delimit var */
467 while (apr_isspace(*cp) || *cp == '=') {
468 ++cp;
469 }
470
471 if (*cp == '"') {
472 ++cp;
473 for (end = cp;
474 (*end && *end != '\n' && *end != '\r' && *end != '\"');
475 end++);
476 }
477 else {
478 for (end = cp; (*end && !apr_isspace(*end)); end++);
479 }
480 if (*end) {
481 *end = '\0'; /* strip ending quote or return */
482 }
483 ap_str_tolower(cp);
484
485 if (parm[0] == 'q'
486 && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
487 result->quality = atoq(cp);
488 }
489 else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) {
490 result->level = (float)atoi(cp);
491 }
492 else if (!strcmp(parm, "charset")) {
493 result->charset = cp;
494 }
495 }
496
497 if (*accept_line == ',') {
498 ++accept_line;
499 }
500
501 return accept_line;
502}
503
504/*****************************************************************
505 *
506 * Dealing with header lines ...
507 *
508 * Accept, Accept-Charset, Accept-Language and Accept-Encoding
509 * are handled by do_header_line() - they all have the same
510 * basic structure of a list of items of the format
511 * name; q=N; charset=TEXT
512 *
513 * where charset is only valid in Accept.
514 */
515
517 const char *accept_line)
518{
520
521 if (!accept_line) {
522 return NULL;
523 }
524
525 accept_recs = apr_array_make(p, 40, sizeof(accept_rec));
526
527 while (*accept_line) {
530 }
531
532 return accept_recs;
533}
534
535/* Given the text of the Content-Languages: line from the var map file,
536 * return an array containing the languages of this variant
537 */
538
540 const char **lang_line)
541{
542 apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *));
543
544 if (!lang_line) {
545 return lang_recs;
546 }
547
548 while (**lang_line) {
549 char **new = (char **) apr_array_push(lang_recs);
550 *new = ap_get_token(p, lang_line, 0);
551 ap_str_tolower(*new);
552 if (**lang_line == ',' || **lang_line == ';') {
553 ++(*lang_line);
554 }
555 }
556
557 return lang_recs;
558}
559
560/*****************************************************************
561 *
562 * Handling header lines from clients...
563 */
564
566{
567 negotiation_state *new =
569 accept_rec *elts;
570 apr_table_t *hdrs = r->headers_in;
571 int i;
572
573 new->pool = r->pool;
574 new->r = r;
576 &negotiation_module);
577
578 new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
579
580 new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
581
582 /* calculate new->accept_q value */
583 if (new->accepts) {
584 elts = (accept_rec *) new->accepts->elts;
585
586 for (i = 0; i < new->accepts->nelts; ++i) {
587 if (elts[i].quality < 1.0) {
588 new->accept_q = 1;
589 }
590 }
591 }
592
593 new->accept_encodings =
594 do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
595 new->accept_langs =
596 do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
597 new->accept_charsets =
598 do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
599
600 /* This is possibly overkill for some servers, heck, we have
601 * only 33 index.html variants in docs/docroot (today).
602 * Make this configurable?
603 */
604 new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
605
606 return new;
607}
608
609
611{
612 const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
613 char *tok;
614
615 /* First, default to no TCN, no Alternates, and the original Apache
616 * negotiation algorithm with fiddles for broken browser configs.
617 *
618 * To save network bandwidth, we do not configure to send an
619 * Alternates header to the user agent by default. User
620 * agents that want an Alternates header for agent-driven
621 * negotiation will have to request it by sending an
622 * appropriate Negotiate header.
623 */
624 neg->ua_supports_trans = 0;
625 neg->send_alternates = 0;
626 neg->may_choose = 1;
627 neg->use_rvsa = 0;
628 neg->dont_fiddle_headers = 0;
629
630 if (!negotiate)
631 return;
632
633 if (strcmp(negotiate, "trans") == 0) {
634 /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
635 * do not support transparent content negotiation, so for Lynx we
636 * ignore the negotiate header when its contents are exactly "trans".
637 * If future versions of Lynx ever need to say 'negotiate: trans',
638 * they can send the equivalent 'negotiate: trans, trans' instead
639 * to avoid triggering the workaround below.
640 */
641 const char *ua = apr_table_get(r->headers_in, "User-Agent");
642
643 if (ua && (strncmp(ua, "Lynx", 4) == 0))
644 return;
645 }
646
647 neg->may_choose = 0; /* An empty Negotiate would require 300 response */
648
649 while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
650
651 if (strcmp(tok, "trans") == 0 ||
652 strcmp(tok, "vlist") == 0 ||
653 strcmp(tok, "guess-small") == 0 ||
654 apr_isdigit(tok[0]) ||
655 strcmp(tok, "*") == 0) {
656
657 /* The user agent supports transparent negotiation */
658 neg->ua_supports_trans = 1;
659
660 /* Send-alternates could be configurable, but note
661 * that it must be 1 if we have 'vlist' in the
662 * negotiate header.
663 */
664 neg->send_alternates = 1;
665
666 if (strcmp(tok, "1.0") == 0) {
667 /* we may use the RVSA/1.0 algorithm, configure for it */
668 neg->may_choose = 1;
669 neg->use_rvsa = 1;
670 neg->dont_fiddle_headers = 1;
671 }
672 else if (tok[0] == '*') {
673 /* we may use any variant selection algorithm, configure
674 * to use the Apache algorithm
675 */
676 neg->may_choose = 1;
677
678 /* We disable header fiddles on the assumption that a
679 * client sending Negotiate knows how to send correct
680 * headers which don't need fiddling.
681 */
682 neg->dont_fiddle_headers = 1;
683 }
684 }
685 }
686
687#ifdef NEG_DEBUG
689 "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
690 "send_alternates=%d, may_choose=%d",
691 neg->dont_fiddle_headers, neg->use_rvsa,
692 neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
693#endif
694
695}
696
697/* Sometimes clients will give us no Accept info at all; this routine sets
698 * up the standard default for that case, and also arranges for us to be
699 * willing to run a CGI script if we find one. (In fact, we set up to
700 * dramatically prefer CGI scripts in cases where that's appropriate,
701 * e.g., POST or when URI includes query args or extra path info).
702 */
704 int prefer_scripts)
705{
707
708 if (!neg->accepts) {
709 neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec));
710
711 new_accept = (accept_rec *) apr_array_push(neg->accepts);
712
713 new_accept->name = "*/*";
714 new_accept->quality = 1.0f;
715 new_accept->level = 0.0f;
716 }
717
718 new_accept = (accept_rec *) apr_array_push(neg->accepts);
719
721 if (neg->use_rvsa) {
722 new_accept->quality = 0;
723 }
724 else {
725 new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
726 }
727 new_accept->level = 0.0f;
728}
729
730/*****************************************************************
731 *
732 * Parsing type-map files, in Roy's meta/http format augmented with
733 * #-comments.
734 */
735
736/* Reading RFC822-style header lines, ignoring #-comments and
737 * handling continuations.
738 */
739
743
744static enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
745{
746 char *buf_end = buffer + len;
747 char *cp;
748 char c;
749
750 /* Get a noncommented line */
751
752 do {
754 return header_eof;
755 }
756 } while (buffer[0] == '#');
757
758 /* If blank, just return it --- this ends information on this variant */
759
760 for (cp = buffer; apr_isspace(*cp); ++cp) {
761 continue;
762 }
763
764 if (*cp == '\0') {
765 return header_sep;
766 }
767
768 /* If non-blank, go looking for header lines, but note that we still
769 * have to treat comments specially...
770 */
771
772 cp += strlen(cp);
773
774 /* We need to shortcut the rest of this block following the Body:
775 * tag - we will not look for continutation after this line.
776 */
777 if (!ap_cstr_casecmpn(buffer, "Body:", 5))
778 return header_seen;
779
780 while (apr_file_getc(&c, map) != APR_EOF) {
781 if (c == '#') {
782 /* Comment line */
783 while (apr_file_getc(&c, map) != APR_EOF && c != '\n') {
784 continue;
785 }
786 }
787 else if (apr_isspace(c)) {
788 /* Leading whitespace. POSSIBLE continuation line
789 * Also, possibly blank --- if so, we ungetc() the final newline
790 * so that we will pick up the blank line the next time 'round.
791 */
792
793 while (c != '\n' && apr_isspace(c)) {
794 if (apr_file_getc(&c, map) != APR_SUCCESS) {
795 break;
796 }
797 }
798
799 apr_file_ungetc(c, map);
800
801 if (c == '\n') {
802 return header_seen; /* Blank line */
803 }
804
805 /* Continuation */
806
807 while ( cp < buf_end - 2
808 && (apr_file_getc(&c, map)) != APR_EOF
809 && c != '\n') {
810 *cp++ = c;
811 }
812
813 *cp++ = '\n';
814 *cp = '\0';
815 }
816 else {
817
818 /* Line beginning with something other than whitespace */
819
820 apr_file_ungetc(c, map);
821 return header_seen;
822 }
823 }
824
825 return header_seen;
826}
827
828static apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag,
829 apr_file_t *map)
830{
831 char *endbody;
833 apr_off_t pos;
834
835
836 /* We are at the first character following a body:tag\n entry
837 * Suck in the body, then backspace to the first char after the
838 * closing tag entry. If we fail to read, find the tag or back
839 * up then we have a hosed file, so give up already
840 */
841 --*len; /* Reserve space for '\0' */
842 if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
843 return -1;
844 }
845 buffer[*len] = '\0';
846
848 if (!endbody) {
849 return -1;
850 }
852 endbody += strlen(tag);
853 /* Skip all the trailing cruft after the end tag to the next line */
854 while (*endbody) {
855 if (*endbody == '\n') {
856 ++endbody;
857 break;
858 }
859 ++endbody;
860 }
861
862 pos = -(apr_off_t)(*len - (endbody - buffer));
863 if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
864 return -1;
865 }
866
867 /* Give the caller back the actual body's file offset and length */
868 *len = bodylen;
869 return pos - (endbody - buffer);
870}
871
872
873/* Stripping out RFC822 comments */
874
875static void strip_paren_comments(char *hdr)
876{
877 /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
878 /* Nope, it isn't correct. Fails to handle backslash escape as well. */
879
880 while (*hdr) {
881 if (*hdr == '"') {
882 hdr = strchr(hdr, '"');
883 if (hdr == NULL) {
884 return;
885 }
886 ++hdr;
887 }
888 else if (*hdr == '(') {
889 while (*hdr && *hdr != ')') {
890 *hdr++ = ' ';
891 }
892
893 if (*hdr) {
894 *hdr++ = ' ';
895 }
896 }
897 else {
898 ++hdr;
899 }
900 }
901}
902
903/* Getting to a header body from the header */
904
905static char *lcase_header_name_return_body(char *header, request_rec *r)
906{
907 char *cp = header;
908
909 for ( ; *cp && *cp != ':' ; ++cp) {
910 *cp = apr_tolower(*cp);
911 }
912
913 if (!*cp) {
915 "Syntax error in type map, no ':' in %s for header %s",
916 r->filename, header);
917 return NULL;
918 }
919
920 do {
921 ++cp;
922 } while (apr_isspace(*cp));
923
924 if (!*cp) {
926 "Syntax error in type map --- no header body: %s for %s",
927 r->filename, header);
928 return NULL;
929 }
930
931 return cp;
932}
933
936{
937 request_rec *r = neg->r;
941 enum header_state hstate;
942 struct var_rec mime_info;
943 int has_content;
944
945 if (!map)
946 map = &map_;
947
948 /* We are not using multiviews */
949 neg->count_multiviews_variants = 0;
950
951 if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED,
954 "cannot access type map file: %s", rr->filename);
956 return HTTP_NOT_FOUND;
957 }
958 else {
959 return HTTP_FORBIDDEN;
960 }
961 }
962
964 has_content = 0;
965
966 do {
968
969 if (hstate == header_seen) {
971 const char *body;
972
973 if (body1 == NULL) {
975 }
976
978 body = body1;
979
980 if (!strncmp(buffer, "uri:", 4)) {
981 mime_info.file_name = ap_get_token(neg->pool, &body, 0);
982 }
983 else if (!strncmp(buffer, "content-type:", 13)) {
984 struct accept_rec accept_info;
985
986 get_entry(neg->pool, &accept_info, body);
988 has_content = 1;
989 }
990 else if (!strncmp(buffer, "content-length:", 15)) {
992
993 body1 = ap_get_token(neg->pool, &body, 0);
996 "Parse error in type map, Content-Length: "
997 "'%s' in %s is invalid.",
998 body1, r->filename);
999 break;
1000 }
1001 mime_info.bytes = clen;
1002 has_content = 1;
1003 }
1004 else if (!strncmp(buffer, "content-language:", 17)) {
1005 mime_info.content_languages = do_languages_line(neg->pool,
1006 &body);
1007 has_content = 1;
1008 }
1009 else if (!strncmp(buffer, "content-encoding:", 17)) {
1010 mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
1011 has_content = 1;
1012 }
1013 else if (!strncmp(buffer, "description:", 12)) {
1014 char *desc = apr_pstrdup(neg->pool, body);
1015 char *cp;
1016
1017 for (cp = desc; *cp; ++cp) {
1018 if (*cp=='\n') *cp=' ';
1019 }
1020 if (cp>desc) *(cp-1)=0;
1021 mime_info.description = desc;
1022 }
1023 else if (!strncmp(buffer, "body:", 5)) {
1024 char *tag = apr_pstrdup(neg->pool, body);
1025 char *eol = strchr(tag, '\0');
1027 while (--eol >= tag && apr_isspace(*eol))
1028 *eol = '\0';
1029 if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) {
1031 "Syntax error in type map, no end tag '%s' "
1032 "found in %s for Body: content.",
1033 tag, r->filename);
1034 break;
1035 }
1036 mime_info.bytes = len;
1037 mime_info.file_name = apr_filepath_name_get(rr->filename);
1038 }
1039 }
1040 else {
1041 if (*mime_info.file_name && has_content) {
1042 void *new_var = apr_array_push(neg->avail_vars);
1043
1044 memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
1045 }
1046
1048 has_content = 0;
1049 }
1050 } while (hstate != header_eof);
1051
1052 if (map_)
1054
1056
1057 return OK;
1058}
1059
1060/* Sort function used by read_types_multi. */
1062{
1063 /* First key is the source quality, sort in descending order. */
1064
1065 /* XXX: note that we currently implement no method of setting the
1066 * source quality for multiviews variants, so we are always comparing
1067 * 1.0 to 1.0 for now
1068 */
1069 if (a->source_quality < b->source_quality)
1070 return 1;
1071 if (a->source_quality > b->source_quality)
1072 return -1;
1073
1074 /* Second key is the variant name */
1075 return strcmp(a->file_name, b->file_name);
1076}
1077
1078/*****************************************************************
1079 *
1080 * Same as read_type_map, except we use a filtered directory listing
1081 * as the map...
1082 */
1083
1085{
1086 request_rec *r = neg->r;
1087
1088 char *filp;
1089 int prefix_len;
1090 apr_dir_t *dirp;
1093 struct var_rec mime_info;
1094 struct accept_rec accept_info;
1095 void *new_var;
1096 int anymatch = 0;
1097
1099
1100 if (r->proxyreq || !r->filename
1101 || !ap_os_is_path_absolute(neg->pool, r->filename)) {
1102 return DECLINED;
1103 }
1104
1105 /* Only absolute paths here */
1106 if (!(filp = strrchr(r->filename, '/'))) {
1107 return DECLINED;
1108 }
1109 ++filp;
1110 prefix_len = strlen(filp);
1111
1112 if ((status = apr_dir_open(&dirp, neg->dir_name,
1113 neg->pool)) != APR_SUCCESS) {
1115 "cannot read directory for multi: %s", neg->dir_name);
1116 return HTTP_FORBIDDEN;
1117 }
1118
1121 request_rec *sub_req;
1122
1123 /* Do we have a match? */
1124#ifdef CASE_BLIND_FILESYSTEM
1125 if (strncasecmp(dirent.name, filp, prefix_len)) {
1126#else
1127 if (strncmp(dirent.name, filp, prefix_len)) {
1128#endif
1129 continue;
1130 }
1131 if (dirent.name[prefix_len] != '.') {
1132 continue;
1133 }
1134
1135 /* Don't negotiate directories and other unusual files
1136 * Really shouldn't see anything but DIR/LNK/REG here,
1137 * and we aught to discover if the LNK was interesting.
1138 *
1139 * Of course, this only helps platforms that capture the
1140 * the filetype in apr_dir_read(), which most can once
1141 * they are optimized with some magic [it's known to the
1142 * dirent, not associated to the inode, on most FS's.]
1143 */
1144 if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR))
1145 continue;
1146
1147 /* Ok, something's here. Maybe nothing useful. Remember that
1148 * we tried, if we completely fail, so we can reject the request!
1149 */
1150 anymatch = 1;
1151
1152 /* See if it's something which we have access to, and which
1153 * has a known type and encoding.
1154 */
1156 NULL);
1157
1158 /* Double check, we still don't multi-resolve non-ordinary files
1159 */
1160 if (sub_req->finfo.filetype != APR_REG) {
1161 /* XXX sub req not destroyed -- may be a bug/unintentional ? */
1162 continue;
1163 }
1164
1165 /* If it has a handler, we'll pretend it's a CGI script,
1166 * since that's a good indication of the sort of thing it
1167 * might be doing.
1168 */
1169 if (sub_req->handler && !sub_req->content_type) {
1171 }
1172
1173 /*
1174 * mod_mime will _always_ provide us the base name in the
1175 * ap-mime-exception-list, if it processed anything. If
1176 * this list is empty, give up immediately, there was
1177 * nothing interesting. For example, looking at the files
1178 * readme.txt and readme.foo, we will throw away .foo if
1179 * it's an insignificant file (e.g. did not identify a
1180 * language, charset, encoding, content type or handler,)
1181 */
1184 "ap-mime-exceptions-list");
1185
1186 if (!exception_list) {
1187 ap_destroy_sub_req(sub_req);
1188 continue;
1189 }
1190
1191 /* Each unregonized bit better match our base name, in sequence.
1192 * A test of index.html.foo will match index.foo or index.html.foo,
1193 * but it will never transpose the segments and allow index.foo.html
1194 * because that would introduce too much CPU consumption. Better that
1195 * we don't attempt a many-to-many match here.
1196 */
1197 {
1198 int nexcept = exception_list->nelts;
1199 char **cur_except = (char**)exception_list->elts;
1200 char *segstart = filp, *segend, saveend;
1201
1202 while (*segstart && nexcept) {
1203 if (!(segend = strchr(segstart, '.')))
1204 segend = strchr(segstart, '\0');
1205 saveend = *segend;
1206 *segend = '\0';
1207
1208#ifdef CASE_BLIND_FILESYSTEM
1209 if (strcasecmp(segstart, *cur_except) == 0) {
1210#else
1211 if (strcmp(segstart, *cur_except) == 0) {
1212#endif
1213 --nexcept;
1214 ++cur_except;
1215 }
1216
1217 if (!saveend)
1218 break;
1219
1220 *segend = saveend;
1221 segstart = segend + 1;
1222 }
1223
1224 if (nexcept) {
1225 /* Something you don't know is, something you don't know...
1226 */
1227 ap_destroy_sub_req(sub_req);
1228 continue;
1229 }
1230 }
1231
1232 /*
1233 * If we failed the subrequest, or don't
1234 * know what we are serving, then continue.
1235 */
1236 if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
1237 ap_destroy_sub_req(sub_req);
1238 continue;
1239 }
1240
1241 /* If it's a map file, we use that instead of the map
1242 * we're building...
1243 */
1244 if (((sub_req->content_type) &&
1246 ((sub_req->handler) &&
1247 !strcmp(sub_req->handler, "type-map"))) {
1248
1250 neg->avail_vars->nelts = 0;
1251 if (sub_req->status != HTTP_OK) {
1252 return sub_req->status;
1253 }
1254 return read_type_map(NULL, neg, sub_req);
1255 }
1256
1257 /* Have reasonable variant --- gather notes. */
1258
1259 mime_info.sub_req = sub_req;
1260 mime_info.file_name = apr_pstrdup(neg->pool, dirent.name);
1261 if (sub_req->content_encoding) {
1262 mime_info.content_encoding = sub_req->content_encoding;
1263 }
1264 if (sub_req->content_languages) {
1265 mime_info.content_languages = sub_req->content_languages;
1266 }
1267
1268 get_entry(neg->pool, &accept_info, sub_req->content_type);
1270
1271 new_var = apr_array_push(neg->avail_vars);
1272 memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
1273
1274 neg->count_multiviews_variants++;
1275
1277 }
1278
1280
1281 /* We found some file names that matched. None could be served.
1282 * Rather than fall out to autoindex or some other mapper, this
1283 * request must die.
1284 */
1285 if (anymatch && !neg->avail_vars->nelts) {
1287 "Negotiation: discovered file(s) matching request: %s"
1288 " (None could be negotiated).",
1289 r->filename);
1290 return HTTP_NOT_FOUND;
1291 }
1292
1294
1295 /* Sort the variants into a canonical order. The negotiation
1296 * result sometimes depends on the order of the variants. By
1297 * sorting the variants into a canonical order, rather than using
1298 * the order in which readdir() happens to return them, we ensure
1299 * that the negotiation result will be consistent over filesystem
1300 * backup/restores and over all mirror sites.
1301 */
1302
1303 qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
1304 sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
1305
1306 return OK;
1307}
1308
1309
1310/*****************************************************************
1311 * And now for the code you've been waiting for... actually
1312 * finding a match to the client's requirements.
1313 */
1314
1315/* Matching MIME types ... the star/star and foo/star commenting conventions
1316 * are implemented here. (You know what I mean by star/star, but just
1317 * try mentioning those three characters in a C comment). Using strcmp()
1318 * is legit, because everything has already been smashed to lowercase.
1319 *
1320 * Note also that if we get an exact match on the media type, we update
1321 * level_matched for use in level_cmp below...
1322 *
1323 * We also give a value for mime_stars, which is used later. It should
1324 * be 1 for star/star, 2 for type/star and 3 for type/subtype.
1325 */
1326
1328{
1329 const char *accept_type = accept_r->name;
1330 const char *avail_type = avail->mime_type;
1331 int len = strlen(accept_type);
1332
1333 if ((len == 1 && accept_type[0] == '*')
1334 || (len == 3 && !strncmp(accept_type, "*/*", 3))) {
1335 /* Anything matches star or star/star */
1336 if (avail->mime_stars < 1) {
1337 avail->mime_stars = 1;
1338 }
1339 return 1;
1340 }
1341 else if (len > 2 && accept_type[len - 2] == '/'
1342 && accept_type[len - 1] == '*'
1344 && avail_type[len - 2] == '/') {
1345 /* Any subtype matches for type/star */
1346 if (avail->mime_stars < 2) {
1347 avail->mime_stars = 2;
1348 }
1349 return 1;
1350 }
1351 else if (!strcmp(accept_type, avail_type)
1352 || (!strcmp(accept_type, "text/html")
1355 if (accept_r->level >= avail->level) {
1356 avail->level_matched = avail->level;
1357 avail->mime_stars = 3;
1358 return 1;
1359 }
1360 }
1361
1362 return OK;
1363}
1364
1365/* This code implements a piece of the tie-breaking algorithm between
1366 * variants of equal quality. This piece is the treatment of variants
1367 * of the same base media type, but different levels. What we want to
1368 * return is the variant at the highest level that the client explicitly
1369 * claimed to accept.
1370 *
1371 * If all the variants available are at a higher level than that, or if
1372 * the client didn't say anything specific about this media type at all
1373 * and these variants just got in on a wildcard, we prefer the lowest
1374 * level, on grounds that that's the one that the client is least likely
1375 * to choke on.
1376 *
1377 * (This is all motivated by treatment of levels in HTML --- we only
1378 * want to give level 3 to browsers that explicitly ask for it; browsers
1379 * that don't, including HTTP/0.9 browsers that only get the implicit
1380 * "Accept: * / *" [space added to avoid confusing cpp --- no, that
1381 * syntax doesn't really work] should get HTML2 if available).
1382 *
1383 * (Note that this code only comes into play when we are choosing among
1384 * variants of equal quality, where the draft standard gives us a fair
1385 * bit of leeway about what to do. It ain't specified by the standard;
1386 * rather, it is a choice made by this server about what to do in cases
1387 * where the standard does not specify a unique course of action).
1388 */
1389
1391{
1392 /* Levels are only comparable between matching media types */
1393
1394 if (var1->is_pseudo_html && !var2->is_pseudo_html) {
1395 return 0;
1396 }
1397
1398 if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
1399 return 0;
1400 }
1401 /* The result of the above if statements is that, if we get to
1402 * here, both variants have the same mime_type or both are
1403 * pseudo-html.
1404 */
1405
1406 /* Take highest level that matched, if either did match. */
1407
1408 if (var1->level_matched > var2->level_matched) {
1409 return 1;
1410 }
1411 if (var1->level_matched < var2->level_matched) {
1412 return -1;
1413 }
1414
1415 /* Neither matched. Take lowest level, if there's a difference. */
1416
1417 if (var1->level < var2->level) {
1418 return 1;
1419 }
1420 if (var1->level > var2->level) {
1421 return -1;
1422 }
1423
1424 /* Tied */
1425
1426 return 0;
1427}
1428
1429/* Finding languages. The main entry point is set_language_quality()
1430 * which is called for each variant. It sets two elements in the
1431 * variant record:
1432 * language_quality - the 'q' value of the 'best' matching language
1433 * from Accept-Language: header (HTTP/1.1)
1434 * lang_index - Non-negotiated language priority, using
1435 * position of language on the Accept-Language:
1436 * header, if present, else LanguagePriority
1437 * directive order.
1438 *
1439 * When we do the variant checking for best variant, we use language
1440 * quality first, and if a tie, language_index next (this only applies
1441 * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
1442 * algorithm, lang_index is never used.
1443 *
1444 * set_language_quality() calls find_lang_index() and find_default_index()
1445 * to set lang_index.
1446 */
1447
1448static int find_lang_index(apr_array_header_t *accept_langs, char *lang)
1449{
1450 const char **alang;
1451 int i;
1452
1453 if (!lang || !accept_langs) {
1454 return -1;
1455 }
1456
1457 alang = (const char **) accept_langs->elts;
1458
1459 for (i = 0; i < accept_langs->nelts; ++i) {
1460 if (!ap_cstr_casecmpn(lang, *alang, strlen(*alang))) {
1461 return i;
1462 }
1463 alang += (accept_langs->elt_size / sizeof(char*));
1464 }
1465
1466 return -1;
1467}
1468
1469/* set_default_lang_quality() sets the quality we apply to variants
1470 * which have no language assigned to them. If none of the variants
1471 * have a language, we are not negotiating on language, so all are
1472 * acceptable, and we set the default q value to 1.0. However if
1473 * some of the variants have languages, we set this default to 0.0001.
1474 * The value of this default will be applied to all variants with
1475 * no explicit language -- which will have the effect of making them
1476 * acceptable, but only if no variants with an explicit language
1477 * are acceptable. The default q value set here is assigned to variants
1478 * with no language type in set_language_quality().
1479 *
1480 * Note that if using the RVSA/1.0 algorithm, we don't use this
1481 * fiddle.
1482 */
1483
1485{
1486 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
1487 int j;
1488
1489 if (!neg->dont_fiddle_headers) {
1490 for (j = 0; j < neg->avail_vars->nelts; ++j) {
1491 var_rec *variant = &avail_recs[j];
1492 if (variant->content_languages &&
1493 variant->content_languages->nelts) {
1494 neg->default_lang_quality = 0.0001f;
1495 return;
1496 }
1497 }
1498 }
1499
1500 neg->default_lang_quality = 1.0f;
1501}
1502
1503/* Set the language_quality value in the variant record. Also
1504 * assigns lang_index for ForceLanguagePriority.
1505 *
1506 * To find the language_quality value, we look for the 'q' value
1507 * of the 'best' matching language on the Accept-Language
1508 * header. The 'best' match is the language on Accept-Language
1509 * header which matches the language of this variant either fully,
1510 * or as far as the prefix marker (-). If two or more languages
1511 * match, use the longest string from the Accept-Language header
1512 * (see HTTP/1.1 [14.4])
1513 *
1514 * When a variant has multiple languages, we find the 'best'
1515 * match for each variant language tag as above, then select the
1516 * one with the highest q value. Because both the accept-header
1517 * and variant can have multiple languages, we now have a hairy
1518 * loop-within-a-loop here.
1519 *
1520 * If the variant has no language and we have no Accept-Language
1521 * items, leave the quality at 1.0 and return.
1522 *
1523 * If the variant has no language, we use the default as set by
1524 * set_default_lang_quality() (1.0 if we are not negotiating on
1525 * language, 0.001 if we are).
1526 *
1527 * Following the setting of the language quality, we drop through to
1528 * set the old 'lang_index'. This is set based on either the order
1529 * of the languages on the Accept-Language header, or the
1530 * order on the LanguagePriority directive. This is only used
1531 * in the negotiation if the language qualities tie.
1532 */
1533
1535{
1536 int forcepriority = neg->conf->forcelangpriority;
1537 if (forcepriority == FLP_UNDEF) {
1539 }
1540
1541 if (!variant->content_languages || !variant->content_languages->nelts) {
1542 /* This variant has no content-language, so use the default
1543 * quality factor for variants with no content-language
1544 * (previously set by set_default_lang_quality()).
1545 * Leave the factor alone (it remains at 1.0) when we may not fiddle
1546 * with the headers.
1547 */
1548 if (!neg->dont_fiddle_headers) {
1549 variant->lang_quality = neg->default_lang_quality;
1550 }
1551 return;
1552 }
1553 else {
1554 /* Variant has one (or more) languages. Look for the best
1555 * match. We do this by going through each language on the
1556 * variant description looking for a match on the
1557 * Accept-Language header. The best match is the longest
1558 * matching language on the header. The final result is the
1559 * best q value from all the languages on the variant
1560 * description.
1561 */
1562
1563 if (!neg->accept_langs) {
1564 /* no accept-language header makes the variant indefinite */
1565 variant->definite = 0;
1566 }
1567 else { /* There is an accept-language with 0 or more items */
1568 accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
1569 accept_rec *best = NULL, *star = NULL;
1571 char *lang, *p;
1572 float fiddle_q = 0.0f;
1573 int any_match_on_star = 0;
1574 int i, j;
1576
1577 for (j = 0; j < variant->content_languages->nelts; ++j) {
1578 p = NULL;
1579 bestthistag = NULL;
1581
1582 /* lang is the variant's language-tag, which is the one
1583 * we are allowed to use the prefix of in HTTP/1.1
1584 */
1585 lang = ((char **) (variant->content_languages->elts))[j];
1586
1587 /* now find the best (i.e. longest) matching
1588 * Accept-Language header language. We put the best match
1589 * for this tag in bestthistag. We cannot update the
1590 * overall best (based on q value) because the best match
1591 * for this tag is the longest language item on the accept
1592 * header, not necessarily the highest q.
1593 */
1594 for (i = 0; i < neg->accept_langs->nelts; ++i) {
1595 if (!strcmp(accs[i].name, "*")) {
1596 if (!star) {
1597 star = &accs[i];
1598 }
1599 continue;
1600 }
1601 /* Find language. We match if either the variant
1602 * language tag exactly matches the language range
1603 * from the accept header, or a prefix of the variant
1604 * language tag up to a '-' character matches the
1605 * whole of the language range in the Accept-Language
1606 * header. Note that HTTP/1.x allows any number of
1607 * '-' characters in a tag or range, currently only
1608 * tags with zero or one '-' characters are defined
1609 * for general use (see rfc1766).
1610 *
1611 * We only use language range in the Accept-Language
1612 * header the best match for the variant language tag
1613 * if it is longer than the previous best match.
1614 */
1615
1616 alen = strlen(accs[i].name);
1617
1618 if ((strlen(lang) >= alen) &&
1619 !strncmp(lang, accs[i].name, alen) &&
1620 ((lang[alen] == 0) || (lang[alen] == '-')) ) {
1621
1624 bestthistag = &accs[i];
1625 }
1626 }
1627
1628 if (!bestthistag && !neg->dont_fiddle_headers) {
1629 /* The next bit is a fiddle. Some browsers might
1630 * be configured to send more specific language
1631 * ranges than desirable. For example, an
1632 * Accept-Language of en-US should never match
1633 * variants with languages en or en-GB. But US
1634 * English speakers might pick en-US as their
1635 * language choice. So this fiddle checks if the
1636 * language range has a prefix, and if so, it
1637 * matches variants which match that prefix with a
1638 * priority of 0.001. So a request for en-US would
1639 * match variants of types en and en-GB, but at
1640 * much lower priority than matches of en-US
1641 * directly, or of any other language listed on
1642 * the Accept-Language header. Note that this
1643 * fiddle does not handle multi-level prefixes.
1644 */
1645 if ((p = strchr(accs[i].name, '-'))) {
1646 int plen = p - accs[i].name;
1647
1648 if (!strncmp(lang, accs[i].name, plen)) {
1649 fiddle_q = 0.001f;
1650 }
1651 }
1652 }
1653 }
1654 /* Finished looking at Accept-Language headers, the best
1655 * (longest) match is in bestthistag, or NULL if no match
1656 */
1657 if (!best ||
1658 (bestthistag && bestthistag->quality > best->quality)) {
1659 best = bestthistag;
1660 }
1661
1662 /* See if the tag matches on a * in the Accept-Language
1663 * header. If so, record this fact for later use
1664 */
1665 if (!bestthistag && star) {
1667 }
1668 }
1669
1670 /* If one of the language tags of the variant matched on *, we
1671 * need to see if its q is better than that of any non-* match
1672 * on any other tag of the variant. If so the * match takes
1673 * precedence and the overall match is not definite.
1674 */
1675 if ( any_match_on_star &&
1676 ((best && star->quality > best->quality) ||
1677 (!best)) ) {
1678 best = star;
1679 variant->definite = 0;
1680 }
1681
1682 variant->lang_quality = best ? best->quality : fiddle_q;
1683 }
1684 }
1685
1686 /* Handle the ForceDefaultLanguage overrides, based on the best match
1687 * to LanguagePriority order. The best match is the lowest index of
1688 * any LanguagePriority match.
1689 */
1690 if (((forcepriority & FLP_PREFER)
1691 && (variant->lang_index < 0))
1693 && !variant->lang_quality))
1694 {
1695 int bestidx = -1;
1696 int j;
1697
1698 for (j = 0; j < variant->content_languages->nelts; ++j)
1699 {
1700 /* lang is the variant's language-tag, which is the one
1701 * we are allowed to use the prefix of in HTTP/1.1
1702 */
1703 char *lang = ((char **) (variant->content_languages->elts))[j];
1704 int idx;
1705
1706 /* If we wish to fallback or
1707 * we use our own LanguagePriority index.
1708 */
1709 idx = find_lang_index(neg->conf->language_priority, lang);
1710 if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
1711 bestidx = idx;
1712 }
1713 }
1714
1715 if (bestidx >= 0) {
1716 if (variant->lang_quality) {
1717 if (forcepriority & FLP_PREFER) {
1718 variant->lang_index = bestidx;
1719 }
1720 }
1721 else {
1723 variant->lang_index = bestidx;
1724 variant->lang_quality = .0001f;
1725 variant->definite = 0;
1726 }
1727 }
1728 }
1729 }
1730}
1731
1732/* Determining the content length --- if the map didn't tell us,
1733 * we have to do a stat() and remember for next time.
1734 */
1735
1737{
1739
1740 if (variant->bytes < 0) {
1741 if ( variant->sub_req
1742 && (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) {
1743 variant->bytes = variant->sub_req->finfo.size;
1744 }
1745 else {
1746 char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
1747 variant->file_name);
1748
1749 if (apr_stat(&statb, fullname,
1750 APR_FINFO_SIZE, neg->pool) == APR_SUCCESS) {
1751 variant->bytes = statb.size;
1752 }
1753 }
1754 }
1755
1756 return variant->bytes;
1757}
1758
1759/* For a given variant, find the best matching Accept: header
1760 * and assign the Accept: header's quality value to the
1761 * mime_type_quality field of the variant, for later use in
1762 * determining the best matching variant.
1763 */
1764
1766{
1767 int i;
1769 float q = 0.0f;
1770 int q_definite = 1;
1771
1772 /* if no Accept: header, leave quality alone (will
1773 * remain at the default value of 1)
1774 *
1775 * XXX: This if is currently never true because of the effect of
1776 * maybe_add_default_accepts().
1777 */
1778 if (!neg->accepts) {
1779 if (variant->mime_type && *variant->mime_type)
1780 variant->definite = 0;
1781 return;
1782 }
1783
1784 accept_recs = (accept_rec *) neg->accepts->elts;
1785
1786 /*
1787 * Go through each of the ranges on the Accept: header,
1788 * looking for the 'best' match with this variant's
1789 * content-type. We use the best match's quality
1790 * value (from the Accept: header) for this variant's
1791 * mime_type_quality field.
1792 *
1793 * The best match is determined like this:
1794 * type/type is better than type/ * is better than * / *
1795 * if match is type/type, use the level mime param if available
1796 */
1797 for (i = 0; i < neg->accepts->nelts; ++i) {
1798
1800 int prev_mime_stars;
1801
1802 prev_mime_stars = variant->mime_stars;
1803
1804 if (!mime_match(type, variant)) {
1805 continue; /* didn't match the content type at all */
1806 }
1807 else {
1808 /* did match - see if there were less or more stars than
1809 * in previous match
1810 */
1811 if (prev_mime_stars == variant->mime_stars) {
1812 continue; /* more stars => not as good a match */
1813 }
1814 }
1815
1816 /* If we are allowed to mess with the q-values
1817 * and have no explicit q= parameters in the accept header,
1818 * make wildcards very low, so we have a low chance
1819 * of ending up with them if there's something better.
1820 */
1821
1822 if (!neg->dont_fiddle_headers && !neg->accept_q &&
1823 variant->mime_stars == 1) {
1824 q = 0.01f;
1825 }
1826 else if (!neg->dont_fiddle_headers && !neg->accept_q &&
1827 variant->mime_stars == 2) {
1828 q = 0.02f;
1829 }
1830 else {
1831 q = type->quality;
1832 }
1833
1834 q_definite = (variant->mime_stars == 3);
1835 }
1836 variant->mime_type_quality = q;
1837 variant->definite = variant->definite && q_definite;
1838
1839}
1840
1841/* For a given variant, find the 'q' value of the charset given
1842 * on the Accept-Charset line. If no charsets are listed,
1843 * assume value of '1'.
1844 */
1846{
1847 int i;
1849 const char *charset = variant->content_charset;
1850 accept_rec *star = NULL;
1851
1852 /* if no Accept-Charset: header, leave quality alone (will
1853 * remain at the default value of 1)
1854 */
1855 if (!neg->accept_charsets) {
1856 if (charset && *charset)
1857 variant->definite = 0;
1858 return;
1859 }
1860
1861 accept_recs = (accept_rec *) neg->accept_charsets->elts;
1862
1863 if (charset == NULL || !*charset) {
1864 /* Charset of variant not known */
1865
1866 /* if not a text / * type, leave quality alone */
1867 if (!(!strncmp(variant->mime_type, "text/", 5)
1868 || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
1869 || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3)
1870 ))
1871 return;
1872
1873 /* Don't go guessing if we are in strict header mode,
1874 * e.g. when running the rvsa, as any guess won't be reflected
1875 * in the variant list or content-location headers.
1876 */
1877 if (neg->dont_fiddle_headers)
1878 return;
1879
1880 charset = "iso-8859-1"; /* The default charset for HTTP text types */
1881 }
1882
1883 /*
1884 * Go through each of the items on the Accept-Charset header,
1885 * looking for a match with this variant's charset. If none
1886 * match, charset is unacceptable, so set quality to 0.
1887 */
1888 for (i = 0; i < neg->accept_charsets->nelts; ++i) {
1889
1891
1892 if (!strcmp(type->name, charset)) {
1893 variant->charset_quality = type->quality;
1894 return;
1895 }
1896 else if (strcmp(type->name, "*") == 0) {
1897 star = type;
1898 }
1899 }
1900 /* No explicit match */
1901 if (star) {
1902 variant->charset_quality = star->quality;
1903 variant->definite = 0;
1904 return;
1905 }
1906 /* If this variant is in charset iso-8859-1, the default is 1.0 */
1907 if (strcmp(charset, "iso-8859-1") == 0) {
1908 variant->charset_quality = 1.0f;
1909 }
1910 else {
1911 variant->charset_quality = 0.0f;
1912 }
1913}
1914
1915
1916/* is_identity_encoding is included for back-compat, but does anyone
1917 * use 7bit, 8bin or binary in their var files??
1918 */
1919
1920static int is_identity_encoding(const char *enc)
1921{
1922 return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
1923 || !strcmp(enc, "binary"));
1924}
1925
1926/*
1927 * set_encoding_quality determines whether the encoding for a particular
1928 * variant is acceptable for the user-agent.
1929 *
1930 * The rules for encoding are that if the user-agent does not supply
1931 * any Accept-Encoding header, then all encodings are allowed but a
1932 * variant with no encoding should be preferred.
1933 * If there is an empty Accept-Encoding header, then no encodings are
1934 * acceptable. If there is a non-empty Accept-Encoding header, then
1935 * any of the listed encodings are acceptable, as well as no encoding
1936 * unless the "identity" encoding is specifically excluded.
1937 */
1939{
1941 const char *enc = variant->content_encoding;
1942 accept_rec *star = NULL;
1943 float value_if_not_found = 0.0f;
1944 int i;
1945
1946 if (!neg->accept_encodings) {
1947 /* We had no Accept-Encoding header, assume that all
1948 * encodings are acceptable with a low quality,
1949 * but we prefer no encoding if available.
1950 */
1951 if (!enc || is_identity_encoding(enc))
1952 variant->encoding_quality = 1.0f;
1953 else
1954 variant->encoding_quality = 0.5f;
1955
1956 return;
1957 }
1958
1959 if (!enc || is_identity_encoding(enc)) {
1960 enc = "identity";
1961 value_if_not_found = 0.0001f;
1962 }
1963
1964 accept_recs = (accept_rec *) neg->accept_encodings->elts;
1965
1966 /* Go through each of the encodings on the Accept-Encoding: header,
1967 * looking for a match with our encoding. x- prefixes are ignored.
1968 */
1969 if (enc[0] == 'x' && enc[1] == '-') {
1970 enc += 2;
1971 }
1972 for (i = 0; i < neg->accept_encodings->nelts; ++i) {
1973
1974 char *name = accept_recs[i].name;
1975
1976 if (name[0] == 'x' && name[1] == '-') {
1977 name += 2;
1978 }
1979
1980 if (!strcmp(name, enc)) {
1981 variant->encoding_quality = accept_recs[i].quality;
1982 return;
1983 }
1984
1985 if (strcmp(name, "*") == 0) {
1986 star = &accept_recs[i];
1987 }
1988
1989 }
1990 /* No explicit match */
1991 if (star) {
1992 variant->encoding_quality = star->quality;
1993 return;
1994 }
1995
1996 /* Encoding not found on Accept-Encoding: header, so it is
1997 * _not_ acceptable unless it is the identity (no encoding)
1998 */
1999 variant->encoding_quality = value_if_not_found;
2000}
2001
2002/*************************************************************
2003 * Possible results of the variant selection algorithm
2004 */
2006 alg_choice = 1, /* choose variant */
2007 alg_list /* list variants */
2009
2010/* Below is the 'best_match' function. It returns an int, which has
2011 * one of the two values alg_choice or alg_list, which give the result
2012 * of the variant selection algorithm. alg_list means that no best
2013 * variant was found by the algorithm, alg_choice means that a best
2014 * variant was found and should be returned. The list/choice
2015 * terminology comes from TCN (rfc2295), but is used in a more generic
2016 * way here. The best variant is returned in *pbest. best_match has
2017 * two possible algorithms for determining the best variant: the
2018 * RVSA/1.0 algorithm (from RFC2296), and the standard Apache
2019 * algorithm. These are split out into separate functions
2020 * (is_variant_better_rvsa() and is_variant_better()). Selection of
2021 * one is through the neg->use_rvsa flag.
2022 *
2023 * The call to best_match also creates full information, including
2024 * language, charset, etc quality for _every_ variant. This is needed
2025 * for generating a correct Vary header, and can be used for the
2026 * Alternates header, the human-readable list responses and 406 errors.
2027 */
2028
2029/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
2030 * v1.0) from rfc2296. This is the algorithm that goes together with
2031 * transparent content negotiation (TCN).
2032 */
2034 var_rec *best, float *p_bestq)
2035{
2036 float bestq = *p_bestq, q;
2037
2038 /* TCN does not cover negotiation on content-encoding. For now,
2039 * we ignore the encoding unless it was explicitly excluded.
2040 */
2041 if (variant->encoding_quality == 0.0f)
2042 return 0;
2043
2044 q = variant->mime_type_quality *
2045 variant->source_quality *
2046 variant->charset_quality *
2047 variant->lang_quality;
2048
2049 /* RFC 2296 calls for the result to be rounded to 5 decimal places,
2050 * but we don't do that because it serves no useful purpose other
2051 * than to ensure that a remote algorithm operates on the same
2052 * precision as ours. That is silly, since what we obviously want
2053 * is for the algorithm to operate on the best available precision
2054 * regardless of who runs it. Since the above calculation may
2055 * result in significant variance at 1e-12, rounding would be bogus.
2056 */
2057
2058#ifdef NEG_DEBUG
2060 "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2061 "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
2062 "q=%1.5f definite=%d",
2063 (variant->file_name ? variant->file_name : ""),
2064 (variant->mime_type ? variant->mime_type : ""),
2065 (variant->content_languages
2066 ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
2067 : ""),
2068 variant->source_quality,
2069 variant->mime_type_quality,
2070 variant->lang_quality,
2071 variant->charset_quality,
2072 variant->encoding_quality,
2073 q,
2074 variant->definite);
2075#endif
2076
2077 if (q <= 0.0f) {
2078 return 0;
2079 }
2080 if (q > bestq) {
2081 *p_bestq = q;
2082 return 1;
2083 }
2084 if (q == bestq) {
2085 /* If the best variant's encoding is of lesser quality than
2086 * this variant, then we prefer this variant
2087 */
2088 if (variant->encoding_quality > best->encoding_quality) {
2089 *p_bestq = q;
2090 return 1;
2091 }
2092 }
2093 return 0;
2094}
2095
2096/* Negotiation algorithm as used by previous versions of Apache
2097 * (just about).
2098 */
2099
2101 var_rec *best, float *p_bestq)
2102{
2103 float bestq = *p_bestq, q;
2104 int levcmp;
2105
2106 /* For non-transparent negotiation, server can choose how
2107 * to handle the negotiation. We'll use the following in
2108 * order: content-type, language, content-type level, charset,
2109 * content encoding, content length.
2110 *
2111 * For each check, we have three possible outcomes:
2112 * This variant is worse than current best: return 0
2113 * This variant is better than the current best:
2114 * assign this variant's q to *p_bestq, and return 1
2115 * This variant is just as desirable as the current best:
2116 * drop through to the next test.
2117 *
2118 * This code is written in this long-winded way to allow future
2119 * customisation, either by the addition of additional
2120 * checks, or to allow the order of the checks to be determined
2121 * by configuration options (e.g. we might prefer to check
2122 * language quality _before_ content type).
2123 */
2124
2125 /* First though, eliminate this variant if it is not
2126 * acceptable by type, charset, encoding or language.
2127 */
2128
2129#ifdef NEG_DEBUG
2131 "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2132 "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
2133 (variant->file_name ? variant->file_name : ""),
2134 (variant->mime_type ? variant->mime_type : ""),
2135 (variant->content_languages
2136 ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
2137 : ""),
2138 variant->source_quality,
2139 variant->mime_type_quality,
2140 variant->lang_quality,
2141 variant->lang_index,
2142 variant->charset_quality,
2143 variant->encoding_quality);
2144#endif
2145
2146 if (variant->encoding_quality == 0.0f ||
2147 variant->lang_quality == 0.0f ||
2148 variant->source_quality == 0.0f ||
2149 variant->charset_quality == 0.0f ||
2150 variant->mime_type_quality == 0.0f) {
2151 return 0; /* don't consider unacceptables */
2152 }
2153
2154 q = variant->mime_type_quality * variant->source_quality;
2155 if (q == 0.0 || q < bestq) {
2156 return 0;
2157 }
2158 if (q > bestq || !best) {
2159 *p_bestq = q;
2160 return 1;
2161 }
2162
2163 /* language */
2164 if (variant->lang_quality < best->lang_quality) {
2165 return 0;
2166 }
2167 if (variant->lang_quality > best->lang_quality) {
2168 *p_bestq = q;
2169 return 1;
2170 }
2171
2172 /* if language qualities were equal, try the LanguagePriority stuff */
2173 if (best->lang_index != -1 &&
2174 (variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
2175 return 0;
2176 }
2177 if (variant->lang_index != -1 &&
2178 (best->lang_index == -1 || variant->lang_index < best->lang_index)) {
2179 *p_bestq = q;
2180 return 1;
2181 }
2182
2183 /* content-type level (sometimes used with text/html, though we
2184 * support it on other types too)
2185 */
2187 if (levcmp == -1) {
2188 return 0;
2189 }
2190 if (levcmp == 1) {
2191 *p_bestq = q;
2192 return 1;
2193 }
2194
2195 /* charset */
2196 if (variant->charset_quality < best->charset_quality) {
2197 return 0;
2198 }
2199 /* If the best variant's charset is ISO-8859-1 and this variant has
2200 * the same charset quality, then we prefer this variant
2201 */
2202
2203 if (variant->charset_quality > best->charset_quality ||
2204 ((variant->content_charset != NULL &&
2205 *variant->content_charset != '\0' &&
2206 strcmp(variant->content_charset, "iso-8859-1") != 0) &&
2207 (best->content_charset == NULL ||
2208 *best->content_charset == '\0' ||
2209 strcmp(best->content_charset, "iso-8859-1") == 0))) {
2210 *p_bestq = q;
2211 return 1;
2212 }
2213
2214 /* Prefer the highest value for encoding_quality.
2215 */
2216 if (variant->encoding_quality < best->encoding_quality) {
2217 return 0;
2218 }
2219 if (variant->encoding_quality > best->encoding_quality) {
2220 *p_bestq = q;
2221 return 1;
2222 }
2223
2224 /* content length if all else equal */
2226 return 0;
2227 }
2228
2229 /* ok, to get here means every thing turned out equal, except
2230 * we have a shorter content length, so use this variant
2231 */
2232 *p_bestq = q;
2233 return 1;
2234}
2235
2236/* figure out, whether a variant is in a specific language
2237 * it returns also false, if the variant has no language.
2238 */
2239static int variant_has_language(var_rec *variant, const char *lang)
2240{
2241 /* fast exit */
2242 if ( !lang
2243 || !variant->content_languages) {
2244 return 0;
2245 }
2246
2247 if (ap_array_str_contains(variant->content_languages, lang)) {
2248 return 1;
2249 }
2250
2251 return 0;
2252}
2253
2254/* check for environment variables 'no-gzip' and
2255 * 'gzip-only-text/html' to get a behaviour similar
2256 * to mod_deflate
2257 */
2259{
2260 if ( is_identity_encoding(variant->content_encoding)
2261 || !strcmp(variant->content_encoding, "identity")) {
2262 return 0;
2263 }
2264
2265 return ( (discard == DISCARD_ALL_ENCODINGS)
2267 && (!variant->mime_type
2268 || strncmp(variant->mime_type, "text/html", 9))));
2269}
2270
2272{
2273 int j;
2274 var_rec *best;
2275 float bestq = 0.0f;
2277 int may_discard = 0;
2278
2279 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2280
2281 /* fetch request dependent variables
2282 * prefer-language: prefer a certain language.
2283 */
2284 const char *preferred_language = apr_table_get(neg->r->subprocess_env,
2285 "prefer-language");
2286
2287 /* no-gzip: do not send encoded documents */
2288 if (apr_table_get(neg->r->subprocess_env, "no-gzip")) {
2290 }
2291
2292 /* gzip-only-text/html: send encoded documents only
2293 * if they are text/html. (no-gzip has a higher priority).
2294 */
2295 else {
2296 const char *env_value = apr_table_get(neg->r->subprocess_env,
2297 "gzip-only-text/html");
2298
2299 if (env_value && !strcmp(env_value, "1")) {
2301 }
2302 }
2303
2305
2306 /*
2307 * Find the 'best' variant
2308 * We run the loop possibly twice: if "prefer-language"
2309 * environment variable is set but we did not find an appropriate
2310 * best variant. In that case forget the preferred language and
2311 * negotiate over all variants.
2312 */
2313
2314 do {
2315 best = NULL;
2316
2317 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2318 var_rec *variant = &avail_recs[j];
2319
2320 /* if this variant is encoded somehow and there are special
2321 * variables set, we do not negotiate it. see above.
2322 */
2323 if ( may_discard
2325 continue;
2326 }
2327
2328 /* if a language is preferred, but the current variant
2329 * is not in that language, then drop it for now
2330 */
2333 continue;
2334 }
2335
2336 /* Find all the relevant 'quality' values from the
2337 * Accept... headers, and store in the variant. This also
2338 * prepares for sending an Alternates header etc so we need to
2339 * do it even if we do not actually plan to find a best
2340 * variant.
2341 */
2343 /* accept the preferred language, even when it's not listed within
2344 * the Accept-Language header
2345 */
2346 if (preferred_language) {
2347 variant->lang_quality = 1.0f;
2348 variant->definite = 1;
2349 }
2350 else {
2352 }
2355
2356 /* Only do variant selection if we may actually choose a
2357 * variant for the client
2358 */
2359 if (neg->may_choose) {
2360
2361 /* Now find out if this variant is better than the current
2362 * best, either using the RVSA/1.0 algorithm, or Apache's
2363 * internal server-driven algorithm. Presumably other
2364 * server-driven algorithms are possible, and could be
2365 * implemented here.
2366 */
2367
2368 if (neg->use_rvsa) {
2370 best = variant;
2371 }
2372 }
2373 else {
2375 best = variant;
2376 }
2377 }
2378 }
2379 }
2380
2381 /* We now either have a best variant, or no best variant */
2382
2383 if (neg->use_rvsa) {
2384 /* calculate result for RVSA/1.0 algorithm:
2385 * only a choice response if the best variant has q>0
2386 * and is definite
2387 */
2388 algorithm_result = (best && best->definite) && (bestq > 0) ?
2390 }
2391 else {
2392 /* calculate result for Apache negotiation algorithm */
2394 }
2395
2396 /* run the loop again, if the "prefer-language" got no clear result */
2399 continue;
2400 }
2401
2402 break;
2403 } while (1);
2404
2405 /* Returning a choice response with a non-neighboring variant is a
2406 * protocol security error in TCN (see rfc2295). We do *not*
2407 * verify here that the variant and URI are neighbors, even though
2408 * we may return alg_choice. We depend on the environment (the
2409 * caller) to only declare the resource transparently negotiable if
2410 * all variants are neighbors.
2411 */
2412 *pbest = best;
2413 return algorithm_result;
2414}
2415
2416/* Sets response headers for a negotiated response.
2417 * neg->is_transparent determines whether a transparently negotiated
2418 * response or a plain `server driven negotiation' response is
2419 * created. Applicable headers are Alternates, Vary, and TCN.
2420 *
2421 * The Vary header we create is sometimes longer than is required for
2422 * the correct caching of negotiated results by HTTP/1.1 caches. For
2423 * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
2424 * the Accept: header assigns a 0 quality to .ps, then the results of
2425 * the two server-side negotiation algorithms we currently implement
2426 * will never depend on Accept-Language so we could return `Vary:
2427 * negotiate, accept' instead of the longer 'Vary: negotiate, accept,
2428 * accept-language' which the code below will return. A routine for
2429 * computing the exact minimal Vary header would be a huge pain to code
2430 * and maintain though, especially because we need to take all possible
2431 * twiddles in the server-side negotiation algorithms into account.
2432 */
2434 int alg_result)
2435{
2436 apr_table_t *hdrs;
2437 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2438 const char *sample_type = NULL;
2439 const char *sample_language = NULL;
2440 const char *sample_encoding = NULL;
2441 const char *sample_charset = NULL;
2442 char *lang;
2443 char *qstr;
2444 apr_off_t len;
2446 int max_vlist_array = (neg->avail_vars->nelts * 21);
2447 int first_variant = 1;
2448 int vary_by_type = 0;
2449 int vary_by_language = 0;
2450 int vary_by_charset = 0;
2451 int vary_by_encoding = 0;
2452 int j;
2453
2454 /* In order to avoid O(n^2) memory copies in building Alternates,
2455 * we preallocate a apr_table_t with the maximum substrings possible,
2456 * fill it with the variant list, and then concatenate the entire array.
2457 * Note that if you change the number of substrings pushed, you also
2458 * need to change the calculation of max_vlist_array above.
2459 */
2460 if (neg->send_alternates && neg->avail_vars->nelts)
2461 arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
2462 else
2463 arr = NULL;
2464
2465 /* Put headers into err_headers_out, since send_http_header()
2466 * outputs both headers_out and err_headers_out.
2467 */
2468 hdrs = r->err_headers_out;
2469
2470 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2471 var_rec *variant = &avail_recs[j];
2472
2473 if (variant->content_languages && variant->content_languages->nelts) {
2474 lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
2475 }
2476 else {
2477 lang = NULL;
2478 }
2479
2480 /* Calculate Vary by looking for any difference between variants */
2481
2482 if (first_variant) {
2483 sample_type = variant->mime_type;
2484 sample_charset = variant->content_charset;
2485 sample_language = lang;
2486 sample_encoding = variant->content_encoding;
2487 }
2488 else {
2489 if (!vary_by_type &&
2491 variant->mime_type ? variant->mime_type : "")) {
2492 vary_by_type = 1;
2493 }
2494 if (!vary_by_charset &&
2496 variant->content_charset ?
2497 variant->content_charset : "")) {
2498 vary_by_charset = 1;
2499 }
2500 if (!vary_by_language &&
2502 lang ? lang : "")) {
2503 vary_by_language = 1;
2504 }
2505 if (!vary_by_encoding &&
2507 variant->content_encoding ?
2508 variant->content_encoding : "")) {
2509 vary_by_encoding = 1;
2510 }
2511 }
2512 first_variant = 0;
2513
2514 if (!neg->send_alternates)
2515 continue;
2516
2517 /* Generate the string components for this Alternates entry */
2518
2519 *((const char **) apr_array_push(arr)) = "{\"";
2520 *((const char **) apr_array_push(arr)) = ap_escape_path_segment(r->pool, variant->file_name);
2521 *((const char **) apr_array_push(arr)) = "\" ";
2522
2523 qstr = (char *) apr_palloc(r->pool, 6);
2524 apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
2525
2526 /* Strip trailing zeros (saves those valuable network bytes) */
2527 if (qstr[4] == '0') {
2528 qstr[4] = '\0';
2529 if (qstr[3] == '0') {
2530 qstr[3] = '\0';
2531 if (qstr[2] == '0') {
2532 qstr[1] = '\0';
2533 }
2534 }
2535 }
2536 *((const char **) apr_array_push(arr)) = qstr;
2537
2538 if (variant->mime_type && *variant->mime_type) {
2539 *((const char **) apr_array_push(arr)) = " {type ";
2540 *((const char **) apr_array_push(arr)) = variant->mime_type;
2541 *((const char **) apr_array_push(arr)) = "}";
2542 }
2543 if (variant->content_charset && *variant->content_charset) {
2544 *((const char **) apr_array_push(arr)) = " {charset ";
2545 *((const char **) apr_array_push(arr)) = variant->content_charset;
2546 *((const char **) apr_array_push(arr)) = "}";
2547 }
2548 if (lang) {
2549 *((const char **) apr_array_push(arr)) = " {language ";
2550 *((const char **) apr_array_push(arr)) = lang;
2551 *((const char **) apr_array_push(arr)) = "}";
2552 }
2553 if (variant->content_encoding && *variant->content_encoding) {
2554 /* Strictly speaking, this is non-standard, but so is TCN */
2555
2556 *((const char **) apr_array_push(arr)) = " {encoding ";
2557 *((const char **) apr_array_push(arr)) = variant->content_encoding;
2558 *((const char **) apr_array_push(arr)) = "}";
2559 }
2560
2561 /* Note that the Alternates specification (in rfc2295) does
2562 * not require that we include {length x}, so we could omit it
2563 * if determining the length is too expensive. We currently
2564 * always include it though.
2565 *
2566 * If the variant is a CGI script, find_content_length would
2567 * return the length of the script, not the output it
2568 * produces, so we check for the presence of a handler and if
2569 * there is one we don't add a length.
2570 *
2571 * XXX: TODO: This check does not detect a CGI script if we
2572 * get the variant from a type map. This needs to be fixed
2573 * (without breaking things if the type map specifies a
2574 * content-length, which currently leads to the correct result).
2575 */
2576 if (!(variant->sub_req && variant->sub_req->handler)
2577 && (len = find_content_length(neg, variant)) >= 0) {
2578
2579 *((const char **) apr_array_push(arr)) = " {length ";
2580 *((const char **) apr_array_push(arr)) = apr_off_t_toa(r->pool,
2581 len);
2582 *((const char **) apr_array_push(arr)) = "}";
2583 }
2584
2585 *((const char **) apr_array_push(arr)) = "}";
2586 *((const char **) apr_array_push(arr)) = ", "; /* trimmed below */
2587 }
2588
2589 if (neg->send_alternates && neg->avail_vars->nelts) {
2590 arr->nelts--; /* remove last comma */
2591 apr_table_mergen(hdrs, "Alternates",
2592 apr_array_pstrcat(r->pool, arr, '\0'));
2593 }
2594
2595 if (neg->is_transparent || vary_by_type || vary_by_language ||
2597
2598 apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
2599 neg->is_transparent ? ", negotiate" : "",
2600 vary_by_type ? ", accept" : "",
2601 vary_by_language ? ", accept-language" : "",
2602 vary_by_charset ? ", accept-charset" : "",
2603 vary_by_encoding ? ", accept-encoding" : "", NULL));
2604 }
2605
2606 if (neg->is_transparent) { /* Create TCN response header */
2607 apr_table_setn(hdrs, "TCN",
2608 alg_result == alg_list ? "list" : "choice");
2609 }
2610}
2611
2612/**********************************************************************
2613 *
2614 * Return an HTML list of variants. This is output as part of the
2615 * choice response or 406 status body.
2616 */
2617
2619{
2621 int i;
2622 int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
2623
2624 /* In order to avoid O(n^2) memory copies in building the list,
2625 * we preallocate a apr_table_t with the maximum substrings possible,
2626 * fill it with the variant list, and then concatenate the entire array.
2627 */
2628 arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
2629
2630 *((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n";
2631
2632 for (i = 0; i < neg->avail_vars->nelts; ++i) {
2633 var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
2634 const char *filename = variant->file_name ? variant->file_name : "";
2635 apr_array_header_t *languages = variant->content_languages;
2636 const char *description = variant->description
2637 ? variant->description
2638 : "";
2639
2640 /* The format isn't very neat, and it would be nice to make
2641 * the tags human readable (eg replace 'language en' with 'English').
2642 * Note that if you change the number of substrings pushed, you also
2643 * need to change the calculation of max_vlist_array above.
2644 */
2645 *((const char **) apr_array_push(arr)) = "<li><a href=\"";
2646 *((const char **) apr_array_push(arr)) = ap_escape_path_segment(r->pool, filename);
2647 *((const char **) apr_array_push(arr)) = "\">";
2648 *((const char **) apr_array_push(arr)) = ap_escape_html(r->pool, filename);
2649 *((const char **) apr_array_push(arr)) = "</a> ";
2650 *((const char **) apr_array_push(arr)) = description;
2651
2652 if (variant->mime_type && *variant->mime_type) {
2653 *((const char **) apr_array_push(arr)) = ", type ";
2654 *((const char **) apr_array_push(arr)) = variant->mime_type;
2655 }
2656 if (languages && languages->nelts) {
2657 *((const char **) apr_array_push(arr)) = ", language ";
2658 *((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool,
2659 languages, ',');
2660 }
2661 if (variant->content_charset && *variant->content_charset) {
2662 *((const char **) apr_array_push(arr)) = ", charset ";
2663 *((const char **) apr_array_push(arr)) = variant->content_charset;
2664 }
2665 if (variant->content_encoding) {
2666 *((const char **) apr_array_push(arr)) = ", encoding ";
2667 *((const char **) apr_array_push(arr)) = variant->content_encoding;
2668 }
2669 *((const char **) apr_array_push(arr)) = "</li>\n";
2670 }
2671 *((const char **) apr_array_push(arr)) = "</ul>\n";
2672
2673 return apr_array_pstrcat(r->pool, arr, '\0');
2674}
2675
2677{
2678 if (r->main == NULL) {
2679 apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
2680 }
2681 else {
2682 apr_table_setn(r->main->notes, "variant-list",
2684 }
2685}
2686
2687/* Called if we got a "Choice" response from the variant selection algorithm.
2688 * It checks the result of the chosen variant to see if it
2689 * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
2690 * Otherwise, add the appropriate headers to the current response.
2691 */
2692
2695{
2696 request_rec *sub_req;
2697 const char *sub_vary;
2698
2699 if (!variant->sub_req) {
2700 int status;
2701
2702 sub_req = ap_sub_req_lookup_file(variant->file_name, r, r->output_filters);
2703 status = sub_req->status;
2704
2705 if (status != HTTP_OK &&
2706 !apr_table_get(sub_req->err_headers_out, "TCN")) {
2707 ap_destroy_sub_req(sub_req);
2708 return status;
2709 }
2710 variant->sub_req = sub_req;
2711 }
2712 else {
2713 sub_req = variant->sub_req;
2714 }
2715
2716 /* The variant selection algorithm told us to return a "Choice"
2717 * response. This is the normal variant response, with
2718 * some extra headers. First, ensure that the chosen
2719 * variant did or will not itself engage in transparent negotiation.
2720 * If not, set the appropriate headers, and fall through to
2721 * the normal variant handling
2722 */
2723
2724 /* This catches the error that a transparent type map selects a
2725 * transparent multiviews resource as the best variant.
2726 *
2727 * XXX: We do not signal an error if a transparent type map
2728 * selects a _non_transparent multiviews resource as the best
2729 * variant, because we can generate a legal negotiation response
2730 * in this case. In this case, the vlist_validator of the
2731 * nontransparent subrequest will be lost however. This could
2732 * lead to cases in which a change in the set of variants or the
2733 * negotiation algorithm of the nontransparent resource is never
2734 * propagated up to a HTTP/1.1 cache which interprets Vary. To be
2735 * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
2736 * for this type of recursive negotiation too.
2737 */
2738 if (neg->is_transparent &&
2739 apr_table_get(sub_req->err_headers_out, "TCN")) {
2741 }
2742
2743 /* This catches the error that a transparent type map recursively
2744 * selects, as the best variant, another type map which itself
2745 * causes transparent negotiation to be done.
2746 *
2747 * XXX: Actually, we catch this error by catching all cases of
2748 * type map recursion. There are some borderline recursive type
2749 * map arrangements which would not produce transparent
2750 * negotiation protocol errors or lack of cache propagation
2751 * problems, but such arrangements are very hard to detect at this
2752 * point in the control flow, so we do not bother to single them
2753 * out.
2754 *
2755 * Recursive type maps imply a recursive arrangement of negotiated
2756 * resources which is visible to outside clients, and this is not
2757 * supported by the transparent negotiation caching protocols, so
2758 * if we are to have generic support for recursive type maps, we
2759 * have to create some configuration setting which makes all type
2760 * maps non-transparent when recursion is enabled. Also, if we
2761 * want recursive type map support which ensures propagation of
2762 * type map changes into HTTP/1.1 caches that handle Vary, we
2763 * would have to extend the current mechanism for generating
2764 * variant list validators.
2765 */
2766 if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
2768 }
2769
2770 /* This adds an appropriate Variant-Vary header if the subrequest
2771 * is a multiviews resource.
2772 *
2773 * XXX: TODO: Note that this does _not_ handle any Vary header
2774 * returned by a CGI if sub_req is a CGI script, because we don't
2775 * see that Vary header yet at this point in the control flow.
2776 * This won't cause any cache consistency problems _unless_ the
2777 * CGI script also returns a Cache-Control header marking the
2778 * response as cacheable. This needs to be fixed, also there are
2779 * problems if a CGI returns an Etag header which also need to be
2780 * fixed.
2781 */
2782 if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
2783 apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
2784
2785 /* Move the subreq Vary header into the main request to
2786 * prevent having two Vary headers in the response, which
2787 * would be legal but strange.
2788 */
2790 apr_table_unset(sub_req->err_headers_out, "Vary");
2791 }
2792
2793 apr_table_setn(r->err_headers_out, "Content-Location",
2794 ap_escape_path_segment(r->pool, variant->file_name));
2795
2796 set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */
2797
2798 /* Still to do by caller: add Expires */
2799
2800 return 0;
2801}
2802
2803/****************************************************************
2804 *
2805 * Executive...
2806 */
2807
2810{
2811 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2812 int alg_result; /* result of variant selection algorithm */
2813 int res;
2814 int j;
2815
2816 /* Decide if resource is transparently negotiable */
2817
2818 /* GET or HEAD? (HEAD has same method number as GET) */
2819 if (r->method_number == M_GET) {
2820
2821 /* maybe this should be configurable, see also the comment
2822 * about recursive type maps in setup_choice_response()
2823 */
2824 neg->is_transparent = 1;
2825
2826 /* We can't be transparent if we are a map file in the middle
2827 * of the request URI.
2828 */
2829 if (r->path_info && *r->path_info)
2830 neg->is_transparent = 0;
2831
2832 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2833 var_rec *variant = &avail_recs[j];
2834
2835 /* We can't be transparent, because of internal
2836 * assumptions in best_match(), if there is a
2837 * non-neighboring variant. We can have a non-neighboring
2838 * variant when processing a type map.
2839 */
2840 if (ap_strchr_c(variant->file_name, '/'))
2841 neg->is_transparent = 0;
2842
2843 /* We can't be transparent, because of the behavior
2844 * of variant typemap bodies.
2845 */
2846 if (variant->body) {
2847 neg->is_transparent = 0;
2848 }
2849 }
2850 }
2851
2852 if (neg->is_transparent) {
2854 }
2855 else { /* configure negotiation on non-transparent resource */
2856 neg->may_choose = 1;
2857 }
2858
2860
2862
2863 /* alg_result is one of
2864 * alg_choice: a best variant is chosen
2865 * alg_list: no best variant is chosen
2866 */
2867
2868 if (alg_result == alg_list) {
2869 /* send a list response or HTTP_NOT_ACCEPTABLE error response */
2870
2871 neg->send_alternates = 1; /* always include Alternates header */
2874
2875 if (neg->is_transparent && neg->ua_supports_trans) {
2876 /* XXX todo: expires? cachability? */
2877
2878 /* Some HTTP/1.0 clients are known to choke when they get
2879 * a 300 (multiple choices) response without a Location
2880 * header. However the 300 code response we are about
2881 * to generate will only reach 1.0 clients which support
2882 * transparent negotiation, and they should be OK. The
2883 * response should never reach older 1.0 clients, even if
2884 * we have CacheNegotiatedDocs enabled, because no 1.0
2885 * proxy cache (we know of) will cache and return 300
2886 * responses (they certainly won't if they conform to the
2887 * HTTP/1.0 specification).
2888 */
2889 return HTTP_MULTIPLE_CHOICES;
2890 }
2891
2892 if (!*bestp) {
2894 "no acceptable variant: %s", r->filename);
2895 return HTTP_NOT_ACCEPTABLE;
2896 }
2897 }
2898
2899 /* Variant selection chose a variant */
2900
2901 /* XXX todo: merge the two cases in the if statement below */
2902 if (neg->is_transparent) {
2903
2904 if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
2905 return res; /* return if error */
2906 }
2907 }
2908 else {
2910 }
2911
2912 /* Make sure caching works - Vary should handle HTTP/1.1, but for
2913 * HTTP/1.0, we can't allow caching at all.
2914 */
2915
2916 /* XXX: Note that we only set r->no_cache to 1, which causes
2917 * Expires: <now> to be added, when responding to a HTTP/1.0
2918 * client. If we return the response to a 1.1 client, we do not
2919 * add Expires <now>, because doing so would degrade 1.1 cache
2920 * performance by preventing re-use of the response without prior
2921 * revalidation. On the other hand, if the 1.1 client is a proxy
2922 * which was itself contacted by a 1.0 client, or a proxy cache
2923 * which can be contacted later by 1.0 clients, then we currently
2924 * rely on this 1.1 proxy to add the Expires: <now> when it
2925 * forwards the response.
2926 *
2927 * XXX: TODO: Find out if the 1.1 spec requires proxies and
2928 * tunnels to add Expires: <now> when forwarding the response to
2929 * 1.0 clients. I (kh) recall it is rather vague on this point.
2930 * Testing actual 1.1 proxy implementations would also be nice. If
2931 * Expires: <now> is not added by proxies then we need to always
2932 * include Expires: <now> ourselves to ensure correct caching, but
2933 * this would degrade HTTP/1.1 cache efficiency unless we also add
2934 * Cache-Control: max-age=N, which we currently don't.
2935 *
2936 * Roy: No, we are not going to screw over HTTP future just to
2937 * ensure that people who can't be bothered to upgrade their
2938 * clients will always receive perfect server-side negotiation.
2939 * Hell, those clients are sending bogus accept headers anyway.
2940 *
2941 * Manual setting of cache-control/expires always overrides this
2942 * automated kluge, on purpose.
2943 */
2944
2946 && (r->proto_num < HTTP_VERSION(1,1)))
2947 && neg->count_multiviews_variants != 1) {
2948 r->no_cache = 1;
2949 }
2950
2951 return OK;
2952}
2953
2955{
2957 apr_file_t *map;
2958 var_rec *best;
2959 int res;
2960 char *udir;
2961 const char *new_req;
2962
2963 if (strcmp(r->handler, MAP_FILE_MAGIC_TYPE) && strcmp(r->handler, "type-map")) {
2964 return DECLINED;
2965 }
2966
2968 if ((res = read_type_map(&map, neg, r))) {
2969 return res;
2970 }
2971
2972 res = do_negotiation(r, neg, &best, 0);
2973 if (res != 0) {
2974 return res;
2975 }
2976
2977 if (best->body)
2978 {
2979 conn_rec *c = r->connection;
2981 apr_bucket *e;
2982
2984 M_POST, -1);
2985 /* XXX: ?
2986 * if (r->method_number == M_OPTIONS) {
2987 * return ap_send_http_options(r);
2988 *}
2989 */
2990 if (r->method_number != M_GET && r->method_number != M_POST) {
2992 }
2993
2994 /* ### These may be implemented by adding some 'extra' info
2995 * of the file offset onto the etag
2996 * ap_update_mtime(r, r->finfo.mtime);
2997 * ap_set_last_modified(r);
2998 * ap_set_etag(r);
2999 */
3001 ap_set_content_length(r, best->bytes);
3002
3003 /* set MIME type and charset as negotiated */
3004 if (best->mime_type && *best->mime_type) {
3005 if (best->content_charset && *best->content_charset) {
3007 best->mime_type,
3008 "; charset=",
3009 best->content_charset,
3010 NULL), 1);
3011 }
3012 else {
3013 ap_set_content_type_ex(r, apr_pstrdup(r->pool, best->mime_type), 1);
3014 }
3015 }
3016
3017 /* set Content-language(s) as negotiated */
3018 if (best->content_languages && best->content_languages->nelts) {
3020 best->content_languages);
3021 }
3022
3023 /* set Content-Encoding as negotiated */
3024 if (best->content_encoding && *best->content_encoding) {
3026 best->content_encoding);
3027 }
3028
3029 if ((res = ap_meets_conditions(r)) != OK) {
3030 return res;
3031 }
3032
3033 if ((res = ap_discard_request_body(r)) != OK) {
3034 return res;
3035 }
3036 bb = apr_brigade_create(r->pool, c->bucket_alloc);
3037
3038 apr_brigade_insert_file(bb, map, best->body, best->bytes, r->pool);
3039
3040 e = apr_bucket_eos_create(c->bucket_alloc);
3042
3043 return ap_pass_brigade_fchk(r, bb, NULL);
3044 }
3045
3046 if (r->path_info && *r->path_info) {
3047 /* remove any path_info from the end of the uri before trying
3048 * to change the filename. r->path_info from the original
3049 * request is passed along on the redirect.
3050 */
3051 r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
3052 }
3055 if (r->args) {
3056 if (r->path_info) {
3057 new_req = apr_pstrcat(r->pool, udir, best->file_name,
3058 r->path_info, "?", r->args, NULL);
3059 }
3060 else {
3061 new_req = apr_pstrcat(r->pool, udir, best->file_name,
3062 "?", r->args, NULL);
3063 }
3064 }
3065 else {
3066 new_req = apr_pstrcat(r->pool, udir, best->file_name,
3067 r->path_info, NULL);
3068 }
3070 return OK;
3071}
3072
3074{
3077 request_rec *sub_req;
3078 int res;
3079 int j;
3080
3081 if (r->finfo.filetype != APR_NOFILE
3082 || !(ap_allow_options(r) & OPT_MULTI)) {
3083 return DECLINED;
3084 }
3085
3087
3088 if ((res = read_types_multi(neg))) {
3090 /* free all allocated memory from subrequests */
3091 avail_recs = (var_rec *) neg->avail_vars->elts;
3092 for (j = 0; j < neg->avail_vars->nelts; ++j) {
3093 var_rec *variant = &avail_recs[j];
3094 if (variant->sub_req) {
3095 ap_destroy_sub_req(variant->sub_req);
3096 }
3097 }
3098 return res;
3099 }
3100 if (neg->avail_vars->nelts == 0) {
3101 return DECLINED;
3102 }
3103
3105 (r->method_number != M_GET) || r->args ||
3106 (r->path_info && *r->path_info));
3107 if (res != 0)
3108 goto return_from_multi;
3109
3110 if (!(sub_req = best->sub_req)) {
3111 /* We got this out of a map file, so we don't actually have
3112 * a sub_req structure yet. Get one now.
3113 */
3114
3115 sub_req = ap_sub_req_lookup_file(best->file_name, r, r->output_filters);
3116 if (sub_req->status != HTTP_OK) {
3117 res = sub_req->status;
3118 ap_destroy_sub_req(sub_req);
3119 goto return_from_multi;
3120 }
3121 }
3122 if (sub_req->args == NULL) {
3123 sub_req->args = r->args;
3124 }
3125
3126 /* now do a "fast redirect" ... promotes the sub_req into the main req */
3127 ap_internal_fast_redirect(sub_req, r);
3128
3129 /* give no advise for time on this subrequest. Perhaps we
3130 * should tally the last mtime among all variants, and date
3131 * the most recent, but that could confuse the proxies.
3132 */
3133 r->mtime = 0;
3134
3135 /* clean up all but our favorite variant, since that sub_req
3136 * is now merged into the main request!
3137 */
3138 avail_recs = (var_rec *) neg->avail_vars->elts;
3139 for (j = 0; j < neg->avail_vars->nelts; ++j) {
3140 var_rec *variant = &avail_recs[j];
3141 if (variant != best && variant->sub_req) {
3142 ap_destroy_sub_req(variant->sub_req);
3143 }
3144 }
3145 return OK;
3146}
3147
3148/**********************************************************************
3149 * There is a problem with content-encoding, as some clients send and
3150 * expect an x- token (e.g. x-gzip) while others expect the plain token
3151 * (i.e. gzip). To try and deal with this as best as possible we do
3152 * the following: if the client sent an Accept-Encoding header and it
3153 * contains a plain token corresponding to the content encoding of the
3154 * response, then set content encoding using the plain token. Else if
3155 * the A-E header contains the x- token use the x- token in the C-E
3156 * header. Else don't do anything.
3157 *
3158 * Note that if no A-E header was sent, or it does not contain a token
3159 * compatible with the final content encoding, then the token in the
3160 * C-E header will be whatever was specified in the AddEncoding
3161 * directive.
3162 */
3164{
3165 const char *enc = r->content_encoding;
3166 char *x_enc = NULL;
3167 apr_array_header_t *accept_encodings;
3169 int i;
3170
3171 if (!enc || !*enc) {
3172 return DECLINED;
3173 }
3174
3175 if (enc[0] == 'x' && enc[1] == '-') {
3176 enc += 2;
3177 }
3178
3179 if ((accept_encodings = do_header_line(r->pool,
3180 apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) {
3181 return DECLINED;
3182 }
3183
3184 accept_recs = (accept_rec *) accept_encodings->elts;
3185
3186 for (i = 0; i < accept_encodings->nelts; ++i) {
3187 char *name = accept_recs[i].name;
3188
3189 if (!strcmp(name, enc)) {
3191 return OK;
3192 }
3193
3194 if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) {
3195 x_enc = name;
3196 }
3197 }
3198
3199 if (x_enc) {
3201 return OK;
3202 }
3203
3204 return DECLINED;
3205}
3206
3213
3215{
3217 create_neg_dir_config, /* dir config creator */
3218 merge_neg_dir_configs, /* dir merger --- default is to override */
3219 NULL, /* server config */
3220 NULL, /* merge server config */
3221 negotiation_cmds, /* command apr_table_t */
3222 register_hooks /* register hooks */
3223};
Symbol export macros and hook functions.
int n
Definition ap_regex.h:278
const char apr_size_t len
Definition ap_regex.h:187
const char * string
Definition ap_regex.h:171
APR File I/O Handling.
APR general purpose library routines.
APR Strings library.
APR Standard Headers Support.
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
#define AP_INIT_ITERATE(directive, func, mconfig, where, help)
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
#define ap_set_module_config(v, m, val)
request_rec * r
#define MAX_STRING_LEN
Definition httpd.h:300
#define DECLINED
Definition httpd.h:457
#define HTTP_VERSION(major, minor)
Definition httpd.h:269
#define OK
Definition httpd.h:456
apr_status_t ap_pass_brigade_fchk(request_rec *r, apr_bucket_brigade *bucket, const char *fmt,...) __attribute__((format(printf
#define OPT_MULTI
Definition http_core.h:86
int ap_allow_options(request_rec *r)
Definition core.c:771
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_STARTUP
Definition http_log.h:105
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
void ap_set_accept_ranges(request_rec *r)
int ap_meets_conditions(request_rec *r)
void ap_set_content_length(request_rec *r, apr_off_t length)
Definition protocol.c:160
void ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
int ap_discard_request_body(request_rec *r)
char * ap_make_etag(request_rec *r, int force_weak)
Definition http_etag.c:353
void ap_internal_fast_redirect(request_rec *sub_req, request_rec *r)
request_rec * ap_sub_req_lookup_dirent(const apr_finfo_t *finfo, const request_rec *r, int subtype, ap_filter_t *next_filter)
Definition request.c:2304
#define AP_SUBREQ_MERGE_ARGS
request_rec * ap_sub_req_lookup_file(const char *new_file, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2433
void ap_hook_fixups(ap_HOOK_fixups_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:87
void ap_allow_standard_methods(request_rec *r, int reset,...)
#define REPLACE_ALLOW
void ap_hook_type_checker(ap_HOOK_type_checker_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:89
void ap_destroy_sub_req(request_rec *r)
Definition request.c:2547
void ap_internal_redirect(const char *new_uri, request_rec *r)
void ap_update_mtime(request_rec *r, apr_time_t dependency_mtime)
Definition request.c:2557
int ap_find_path_info(const char *uri, const char *path_info)
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
#define APR_EOF
Definition apr_errno.h:461
#define APR_STATUS_IS_ENOTDIR(s)
Definition apr_errno.h:1249
#define APR_STATUS_IS_ENOENT(s)
Definition apr_errno.h:1246
#define APR_BRIGADE_INSERT_TAIL(b, e)
apr_bucket * e
apr_bucket apr_bucket_brigade * a
apr_pool_t apr_dbd_t apr_dbd_results_t ** res
Definition apr_dbd.h:287
#define APR_HOOK_FIRST
Definition apr_hooks.h:301
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
apr_text_header * hdr
Definition apr_xml.h:77
#define RSRC_CONF
#define OR_FILEINFO
#define HTTP_MULTIPLE_CHOICES
Definition httpd.h:500
#define HTTP_OK
Definition httpd.h:490
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_METHOD_NOT_ALLOWED
Definition httpd.h:513
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_NOT_FOUND
Definition httpd.h:512
#define HTTP_NOT_ACCEPTABLE
Definition httpd.h:514
#define HTTP_VARIANT_ALSO_VARIES
Definition httpd.h:541
#define M_OPTIONS
Definition httpd.h:597
#define M_POST
Definition httpd.h:594
#define M_GET
Definition httpd.h:592
#define STANDARD20_MODULE_STUFF
#define ap_strstr(s, c)
Definition httpd.h:2359
char * ap_make_full_path(apr_pool_t *a, const char *dir, const char *f)
Definition util.c:2346
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
char * ap_escape_path_segment(apr_pool_t *p, const char *s)
Definition util.c:2068
#define ap_strchr_c(s, c)
Definition httpd.h:2353
char * ap_get_token(apr_pool_t *p, const char **accept_line, int accept_white)
Definition util.c:1687
char * ap_get_list_item(apr_pool_t *p, const char **field)
Definition util.c:1314
char * ap_make_dirstr_parent(apr_pool_t *p, const char *s)
Definition util.c:692
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define ap_escape_html(p, s)
Definition httpd.h:1860
void ap_str_tolower(char *s)
Definition util.c:2410
int ap_parse_strict_length(apr_off_t *len, const char *str)
Definition util.c:2683
int ap_array_str_contains(const apr_array_header_t *array, const char *s)
Definition util.c:3446
int ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
Definition util.c:229
apr_size_t size
#define apr_isspace(c)
Definition apr_lib.h:225
#define apr_isdigit(c)
Definition apr_lib.h:209
#define apr_tolower(c)
Definition apr_lib.h:231
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
@ APR_REG
@ APR_DIR
@ APR_NOFILE
int type
char * buffer
#define APR_READ
Definition apr_file_io.h:93
#define APR_BUFFERED
#define APR_OS_DEFAULT
#define APR_CUR
#define APR_FINFO_DIRENT
#define APR_FINFO_TYPE
#define APR_FINFO_SIZE
apr_array_header_t ** result
int strcasecmp(const char *a, const char *b)
int strncasecmp(const char *a, const char *b, size_t n)
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_pool_t * b
Definition apr_pools.h:529
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_size_t const char * filename
Definition apr_shm.h:72
const char char ** end
const char * s
Definition apr_strings.h:95
const apr_array_header_t * arr
Definition apr_tables.h:187
apr_cmdtype_e cmd
int int status
#define INCLUDES_MAGIC_TYPE3
Definition httpd.h:709
#define INCLUDES_MAGIC_TYPE
Definition httpd.h:707
#define CGI_MAGIC_TYPE
Definition httpd.h:705
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static void strip_paren_comments(char *hdr)
static char * make_variant_list(request_rec *r, negotiation_state *neg)
static int setup_choice_response(request_rec *r, negotiation_state *neg, var_rec *variant)
#define DISCARD_ALL_ENCODINGS
static int best_match(negotiation_state *neg, var_rec **pbest)
static const char * set_language_priority(cmd_parms *cmd, void *n_, const char *lang)
#define FLP_FALLBACK
static int mime_match(accept_rec *accept_r, var_rec *avail)
static int is_variant_better(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq)
algorithm_results
@ alg_list
@ alg_choice
#define FLP_UNDEF
#define FLP_NONE
static void clean_var_rec(var_rec *mime_info)
#define FLP_PREFER
static int read_types_multi(negotiation_state *neg)
static void * create_neg_dir_config(apr_pool_t *p, char *dummy)
static const command_rec negotiation_cmds[]
static int read_type_map(apr_file_t **map, negotiation_state *neg, request_rec *rr)
static void set_charset_quality(negotiation_state *neg, var_rec *variant)
static void maybe_add_default_accepts(negotiation_state *neg, int prefer_scripts)
static int is_identity_encoding(const char *enc)
#define MAP_FILE_MAGIC_TYPE
#define DISCARD_ALL_BUT_HTML
static void store_variant_list(request_rec *r, negotiation_state *neg)
#define FLP_DEFAULT
static void set_accept_quality(negotiation_state *neg, var_rec *variant)
static float atoq(const char *string)
static int variantsortf(var_rec *a, var_rec *b)
static int handle_multi(request_rec *r)
static void * merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
static char * lcase_header_name_return_body(char *header, request_rec *r)
static apr_array_header_t * do_header_line(apr_pool_t *p, const char *accept_line)
static int find_lang_index(apr_array_header_t *accept_langs, char *lang)
static const char * get_entry(apr_pool_t *p, accept_rec *result, const char *accept_line)
static void set_language_quality(negotiation_state *neg, var_rec *variant)
static void set_encoding_quality(negotiation_state *neg, var_rec *variant)
static void register_hooks(apr_pool_t *p)
static int fix_encoding(request_rec *r)
static int do_cache_negotiated_docs(server_rec *s)
static negotiation_state * parse_accept_headers(request_rec *r)
static enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
static int handle_map_file(request_rec *r)
static apr_array_header_t * do_languages_line(apr_pool_t *p, const char **lang_line)
header_state
@ header_sep
@ header_seen
@ header_eof
static void parse_negotiate_header(request_rec *r, negotiation_state *neg)
static void set_mime_fields(var_rec *var, accept_rec *mime_info)
static int level_cmp(var_rec *var1, var_rec *var2)
static void set_neg_headers(request_rec *r, negotiation_state *neg, int alg_result)
static void set_default_lang_quality(negotiation_state *neg)
static apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag, apr_file_t *map)
static const char * cache_negotiated_docs(cmd_parms *cmd, void *dummy, int arg)
static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq)
static int discard_variant_by_env(var_rec *variant, int discard)
static const char * set_force_priority(cmd_parms *cmd, void *n_, const char *w)
static int variant_has_language(var_rec *variant, const char *lang)
static apr_off_t find_content_length(negotiation_state *neg, var_rec *variant)
static void set_vlist_validator(request_rec *r, request_rec *vlistr)
static int do_negotiation(request_rec *r, negotiation_state *neg, var_rec **bestp, int prefer_scripts)
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
apr_pool_t * pool
apr_filetype_e filetype
apr_off_t size
Structure to store things which are per connection.
Definition httpd.h:1152
apr_array_header_t * language_priority
apr_array_header_t * accept_charsets
apr_array_header_t * accepts
apr_array_header_t * avail_vars
neg_dir_config * conf
apr_array_header_t * accept_encodings
apr_array_header_t * accept_langs
A structure that represents the current request.
Definition httpd.h:845
apr_array_header_t * content_languages
Definition httpd.h:999
int status
Definition httpd.h:891
char * uri
Definition httpd.h:1016
const char * content_type
Definition httpd.h:992
struct ap_filter_t * output_filters
Definition httpd.h:1070
apr_time_t mtime
Definition httpd.h:933
const char * handler
Definition httpd.h:994
apr_table_t * notes
Definition httpd.h:985
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
int proxyreq
Definition httpd.h:873
conn_rec * connection
Definition httpd.h:849
int no_cache
Definition httpd.h:1082
apr_table_t * err_headers_out
Definition httpd.h:981
apr_finfo_t finfo
Definition httpd.h:1094
int proto_num
Definition httpd.h:877
apr_table_t * headers_in
Definition httpd.h:976
request_rec * main
Definition httpd.h:860
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
char * vlist_validator
Definition httpd.h:1002
char * path_info
Definition httpd.h:1024
char * args
Definition httpd.h:1026
const char * content_encoding
Definition httpd.h:997
A structure to store information for each virtual server.
Definition httpd.h:1322
const char * content_encoding
apr_off_t bytes
const char * content_charset
float mime_type_quality
const char * mime_type
const char * description
float lang_quality
float encoding_quality
apr_off_t body
float source_quality
request_rec * sub_req
float level_matched
const char * file_name
apr_array_header_t * content_languages
float charset_quality
apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted, apr_dir_t *thedir)
Definition dir.c:142
apr_status_t apr_dir_close(apr_dir_t *thedir)
Definition dir.c:109
apr_status_t apr_dir_open(apr_dir_t **new, const char *dirname, apr_pool_t *pool)
Definition dir.c:75
#define var
Apache script tools.
typedef int(WSAAPI *apr_winapi_fpt_WSAPoll)(IN OUT LPWSAPOLLFD fdArray