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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * mod_negotiation.c: keeps track of MIME types the client is willing to
19 * accept, and contains code to handle type arbitration.
25 #include "apr_strings.h"
26 #include "apr_file_io.h"
29 #define APR_WANT_STRFUNC
32 #include "ap_config.h"
34 #include "http_config.h"
35 #include "http_request.h"
36 #include "http_protocol.h"
37 #include "http_core.h"
39 #include "util_script.h"
42 #define MAP_FILE_MAGIC_TYPE "application/x-type-map"
44 /* Commands --- configuring document caching on a per (virtual?)
49 int forcelangpriority;
50 apr_array_header_t *language_priority;
53 /* forcelangpriority flags
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 */
60 #define FLP_DEFAULT FLP_PREFER
62 module AP_MODULE_DECLARE_DATA negotiation_module;
64 static void *create_neg_dir_config(apr_pool_t *p, char *dummy)
66 neg_dir_config *new = (neg_dir_config *) apr_palloc(p,
67 sizeof(neg_dir_config));
69 new->forcelangpriority = FLP_UNDEF;
70 new->language_priority = NULL;
74 static void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
76 neg_dir_config *base = (neg_dir_config *) basev;
77 neg_dir_config *add = (neg_dir_config *) addv;
78 neg_dir_config *new = (neg_dir_config *) apr_palloc(p,
79 sizeof(neg_dir_config));
81 /* give priority to the config in the subdirectory */
82 new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF)
83 ? add->forcelangpriority
84 : base->forcelangpriority;
85 new->language_priority = add->language_priority
86 ? add->language_priority
87 : base->language_priority;
91 static const char *set_language_priority(cmd_parms *cmd, void *n_,
94 neg_dir_config *n = n_;
97 if (!n->language_priority)
98 n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *));
100 langp = (const char **) apr_array_push(n->language_priority);
105 static const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w)
107 neg_dir_config *n = n_;
109 if (!strcasecmp(w, "None")) {
110 if (n->forcelangpriority & ~FLP_NONE) {
111 return "Cannot combine ForceLanguagePriority options with None";
113 n->forcelangpriority = FLP_NONE;
115 else if (!strcasecmp(w, "Prefer")) {
116 if (n->forcelangpriority & FLP_NONE) {
117 return "Cannot combine ForceLanguagePriority options None and "
120 n->forcelangpriority |= FLP_PREFER;
122 else if (!strcasecmp(w, "Fallback")) {
123 if (n->forcelangpriority & FLP_NONE) {
124 return "Cannot combine ForceLanguagePriority options None and "
127 n->forcelangpriority |= FLP_FALLBACK;
130 return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ",
137 static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
140 ap_set_module_config(cmd->server->module_config, &negotiation_module,
141 (arg ? "Cache" : NULL));
145 static int do_cache_negotiated_docs(server_rec *s)
147 return (ap_get_module_config(s->module_config,
148 &negotiation_module) != NULL);
151 static const command_rec negotiation_cmds[] =
153 AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF,
154 "Either 'on' or 'off' (default)"),
155 AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL,
157 "space-delimited list of MIME language abbreviations"),
158 AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL,
160 "Force LanguagePriority elections, either None, or "
161 "Fallback and/or Prefer"),
166 * Record of available info on a media type specified by the client
167 * (we also use 'em for encodings and languages)
170 typedef struct accept_rec {
171 char *name; /* MUST be lowercase */
174 char *charset; /* for content-type only */
178 * Record of available info on a particular variant
180 * Note that a few of these fields are updated by the actual negotiation
183 * level_matched --- initialized to zero. Set to the value of level
184 * if the client actually accepts this media type at that
185 * level (and *not* if it got in on a wildcard). See level_cmp
187 * mime_stars -- initialized to zero. Set to the number of stars
188 * present in the best matching Accept header element.
189 * 1 for star/star, 2 for type/star and 3 for
192 * definite -- initialized to 1. Set to 0 if there is a match which
193 * makes the variant non-definite according to the rules
197 typedef struct var_rec {
198 request_rec *sub_req; /* May be NULL (is, for map files) */
199 const char *mime_type; /* MUST be lowercase */
200 const char *file_name; /* Set to 'this' (for map file body content) */
201 apr_off_t body; /* Only for map file body content */
202 const char *content_encoding;
203 apr_array_header_t *content_languages; /* list of lang. for this variant */
204 const char *content_charset;
205 const char *description;
207 /* The next five items give the quality values for the dimensions
208 * of negotiation for this variant. They are obtained from the
209 * appropriate header lines, except for source_quality, which
210 * is obtained from the variant itself (the 'qs' parameter value
211 * from the variant's mime-type). Apart from source_quality,
212 * these values are set when we find the quality for each variant
213 * (see best_match()). source_quality is set from the 'qs' parameter
214 * of the variant description or mime type: see set_mime_fields().
216 float lang_quality; /* quality of this variant's language */
217 float encoding_quality; /* ditto encoding */
218 float charset_quality; /* ditto charset */
219 float mime_type_quality; /* ditto media type */
220 float source_quality; /* source quality for this variant */
222 /* Now some special values */
223 float level; /* Auxiliary to content-type... */
224 apr_off_t bytes; /* content length, if known */
225 int lang_index; /* Index into LanguagePriority list */
226 int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
228 /* Above are all written-once properties of the variant. The
229 * three fields below are changed during negotiation:
237 /* Something to carry around the state of negotiation (and to keep
238 * all of this thread-safe)...
244 neg_dir_config *conf;
246 int accept_q; /* 1 if an Accept item has a q= param */
247 float default_lang_quality; /* fiddle lang q for variants with no lang */
249 /* the array pointers below are NULL if the corresponding accept
250 * headers are not present
252 apr_array_header_t *accepts; /* accept_recs */
253 apr_array_header_t *accept_encodings; /* accept_recs */
254 apr_array_header_t *accept_charsets; /* accept_recs */
255 apr_array_header_t *accept_langs; /* accept_recs */
257 apr_array_header_t *avail_vars; /* available variants */
259 int count_multiviews_variants; /* number of variants found on disk */
261 int is_transparent; /* 1 if this resource is trans. negotiable */
263 int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */
264 int ua_supports_trans; /* 1 if ua supports trans negotiation */
265 int send_alternates; /* 1 if we want to send an Alternates header */
266 int may_choose; /* 1 if we may choose a variant for the client */
267 int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */
270 /* A few functions to manipulate var_recs.
271 * Cleaning out the fields...
274 static void clean_var_rec(var_rec *mime_info)
276 mime_info->sub_req = NULL;
277 mime_info->mime_type = "";
278 mime_info->file_name = "";
280 mime_info->content_encoding = NULL;
281 mime_info->content_languages = NULL;
282 mime_info->content_charset = "";
283 mime_info->description = "";
285 mime_info->is_pseudo_html = 0;
286 mime_info->level = 0.0f;
287 mime_info->level_matched = 0.0f;
288 mime_info->bytes = -1;
289 mime_info->lang_index = -1;
290 mime_info->mime_stars = 0;
291 mime_info->definite = 1;
293 mime_info->charset_quality = 1.0f;
294 mime_info->encoding_quality = 1.0f;
295 mime_info->lang_quality = 1.0f;
296 mime_info->mime_type_quality = 1.0f;
297 mime_info->source_quality = 0.0f;
300 /* Initializing the relevant fields of a variant record from the
301 * accept_info read out of its content-type, one way or another.
304 static void set_mime_fields(var_rec *var, accept_rec *mime_info)
306 var->mime_type = mime_info->name;
307 var->source_quality = mime_info->quality;
308 var->level = mime_info->level;
309 var->content_charset = mime_info->charset;
311 var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
312 || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE)
313 || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));
316 /* Create a variant list validator in r using info from vlistr. */
318 static void set_vlist_validator(request_rec *r, request_rec *vlistr)
320 /* Calculating the variant list validator is similar to
321 * calculating an etag for the source of the variant list
322 * information, so we use ap_make_etag(). Note that this
323 * validator can be 'weak' in extreme case.
325 ap_update_mtime(vlistr, vlistr->finfo.mtime);
326 r->vlist_validator = ap_make_etag(vlistr, 0);
328 /* ap_set_etag will later take r->vlist_validator into account
329 * when creating the etag header
334 /*****************************************************************
336 * Parsing (lists of) media types and their parameters, as seen in
337 * HTTPD header lines and elsewhere.
341 * Get a single mime type entry --- one media type and parameters;
342 * enter the values we recognize into the argument accept_rec
345 static const char *get_entry(apr_pool_t *p, accept_rec *result,
346 const char *accept_line)
348 result->quality = 1.0f;
349 result->level = 0.0f;
350 result->charset = "";
353 * Note that this handles what I gather is the "old format",
355 * Accept: text/html text/plain moo/zot
357 * without any compatibility kludges --- if the token after the
358 * MIME type begins with a semicolon, we know we're looking at parms,
359 * otherwise, we know we aren't. (So why all the pissing and moaning
360 * in the CERN server code? I must be missing something).
363 result->name = ap_get_token(p, &accept_line, 0);
364 ap_str_tolower(result->name); /* You want case insensitive,
365 * you'll *get* case insensitive.
368 /* KLUDGE!!! Default HTML to level 2.0 unless the browser
369 * *explicitly* says something else.
372 if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
373 result->level = 2.0f;
375 else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
376 result->level = 2.0f;
378 else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
379 result->level = 3.0f;
382 while (*accept_line == ';') {
390 parm = ap_get_token(p, &accept_line, 1);
392 /* Look for 'var = value' --- and make sure the var is in lcase. */
394 for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
395 *cp = apr_tolower(*cp);
399 continue; /* No '='; just ignore it. */
402 *cp++ = '\0'; /* Delimit var */
403 while (*cp && (apr_isspace(*cp) || *cp == '=')) {
410 (*end && *end != '\n' && *end != '\r' && *end != '\"');
414 for (end = cp; (*end && !apr_isspace(*end)); end++);
417 *end = '\0'; /* strip ending quote or return */
422 && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
423 result->quality = (float)atof(cp);
425 else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) {
426 result->level = (float)atof(cp);
428 else if (!strcmp(parm, "charset")) {
429 result->charset = cp;
433 if (*accept_line == ',') {
440 /*****************************************************************
442 * Dealing with header lines ...
444 * Accept, Accept-Charset, Accept-Language and Accept-Encoding
445 * are handled by do_header_line() - they all have the same
446 * basic structure of a list of items of the format
447 * name; q=N; charset=TEXT
449 * where charset is only valid in Accept.
452 static apr_array_header_t *do_header_line(apr_pool_t *p,
453 const char *accept_line)
455 apr_array_header_t *accept_recs;
461 accept_recs = apr_array_make(p, 40, sizeof(accept_rec));
463 while (*accept_line) {
464 accept_rec *new = (accept_rec *) apr_array_push(accept_recs);
465 accept_line = get_entry(p, new, accept_line);
471 /* Given the text of the Content-Languages: line from the var map file,
472 * return an array containing the languages of this variant
475 static apr_array_header_t *do_languages_line(apr_pool_t *p,
476 const char **lang_line)
478 apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *));
484 while (**lang_line) {
485 char **new = (char **) apr_array_push(lang_recs);
486 *new = ap_get_token(p, lang_line, 0);
487 ap_str_tolower(*new);
488 if (**lang_line == ',' || **lang_line == ';') {
496 /*****************************************************************
498 * Handling header lines from clients...
501 static negotiation_state *parse_accept_headers(request_rec *r)
503 negotiation_state *new =
504 (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state));
506 apr_table_t *hdrs = r->headers_in;
511 new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config,
512 &negotiation_module);
514 new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
516 new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
518 /* calculate new->accept_q value */
520 elts = (accept_rec *) new->accepts->elts;
522 for (i = 0; i < new->accepts->nelts; ++i) {
523 if (elts[i].quality < 1.0) {
529 new->accept_encodings =
530 do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
532 do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
533 new->accept_charsets =
534 do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
536 /* This is possibly overkill for some servers, heck, we have
537 * only 33 index.html variants in docs/docroot (today).
538 * Make this configurable?
540 new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
546 static void parse_negotiate_header(request_rec *r, negotiation_state *neg)
548 const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
551 /* First, default to no TCN, no Alternates, and the original Apache
552 * negotiation algorithm with fiddles for broken browser configs.
554 * To save network bandwidth, we do not configure to send an
555 * Alternates header to the user agent by default. User
556 * agents that want an Alternates header for agent-driven
557 * negotiation will have to request it by sending an
558 * appropriate Negotiate header.
560 neg->ua_supports_trans = 0;
561 neg->send_alternates = 0;
564 neg->dont_fiddle_headers = 0;
569 if (strcmp(negotiate, "trans") == 0) {
570 /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
571 * do not support transparent content negotiation, so for Lynx we
572 * ignore the negotiate header when its contents are exactly "trans".
573 * If future versions of Lynx ever need to say 'negotiate: trans',
574 * they can send the equivalent 'negotiate: trans, trans' instead
575 * to avoid triggering the workaround below.
577 const char *ua = apr_table_get(r->headers_in, "User-Agent");
579 if (ua && (strncmp(ua, "Lynx", 4) == 0))
583 neg->may_choose = 0; /* An empty Negotiate would require 300 response */
585 while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
587 if (strcmp(tok, "trans") == 0 ||
588 strcmp(tok, "vlist") == 0 ||
589 strcmp(tok, "guess-small") == 0 ||
590 apr_isdigit(tok[0]) ||
591 strcmp(tok, "*") == 0) {
593 /* The user agent supports transparent negotiation */
594 neg->ua_supports_trans = 1;
596 /* Send-alternates could be configurable, but note
597 * that it must be 1 if we have 'vlist' in the
600 neg->send_alternates = 1;
602 if (strcmp(tok, "1.0") == 0) {
603 /* we may use the RVSA/1.0 algorithm, configure for it */
606 neg->dont_fiddle_headers = 1;
608 else if (tok[0] == '*') {
609 /* we may use any variant selection algorithm, configure
610 * to use the Apache algorithm
614 /* We disable header fiddles on the assumption that a
615 * client sending Negotiate knows how to send correct
616 * headers which don't need fiddling.
618 neg->dont_fiddle_headers = 1;
624 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
625 "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
626 "send_alternates=%d, may_choose=%d",
627 neg->dont_fiddle_headers, neg->use_rvsa,
628 neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
633 /* Sometimes clients will give us no Accept info at all; this routine sets
634 * up the standard default for that case, and also arranges for us to be
635 * willing to run a CGI script if we find one. (In fact, we set up to
636 * dramatically prefer CGI scripts in cases where that's appropriate,
637 * e.g., POST or when URI includes query args or extra path info).
639 static void maybe_add_default_accepts(negotiation_state *neg,
642 accept_rec *new_accept;
645 neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec));
647 new_accept = (accept_rec *) apr_array_push(neg->accepts);
649 new_accept->name = "*/*";
650 new_accept->quality = 1.0f;
651 new_accept->level = 0.0f;
654 new_accept = (accept_rec *) apr_array_push(neg->accepts);
656 new_accept->name = CGI_MAGIC_TYPE;
658 new_accept->quality = 0;
661 new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
663 new_accept->level = 0.0f;
666 /*****************************************************************
668 * Parsing type-map files, in Roy's meta/http format augmented with
672 /* Reading RFC822-style header lines, ignoring #-comments and
673 * handling continuations.
677 header_eof, header_seen, header_sep
680 static enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
682 char *buf_end = buffer + len;
686 /* Get a noncommented line */
689 if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) {
692 } while (buffer[0] == '#');
694 /* If blank, just return it --- this ends information on this variant */
696 for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) {
704 /* If non-blank, go looking for header lines, but note that we still
705 * have to treat comments specially...
710 /* We need to shortcut the rest of this block following the Body:
711 * tag - we will not look for continutation after this line.
713 if (!strncasecmp(buffer, "Body:", 5))
716 while (apr_file_getc(&c, map) != APR_EOF) {
719 while (apr_file_getc(&c, map) != APR_EOF && c != '\n') {
723 else if (apr_isspace(c)) {
724 /* Leading whitespace. POSSIBLE continuation line
725 * Also, possibly blank --- if so, we ungetc() the final newline
726 * so that we will pick up the blank line the next time 'round.
729 while (c != '\n' && apr_isspace(c)) {
730 if(apr_file_getc(&c, map) != APR_SUCCESS)
734 apr_file_ungetc(c, map);
737 return header_seen; /* Blank line */
742 while ( cp < buf_end - 2
743 && (apr_file_getc(&c, map)) != APR_EOF
753 /* Line beginning with something other than whitespace */
755 apr_file_ungetc(c, map);
763 static apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag,
771 taglen = strlen(tag);
774 /* We are at the first character following a body:tag\n entry
775 * Suck in the body, then backspace to the first char after the
776 * closing tag entry. If we fail to read, find the tag or back
777 * up then we have a hosed file, so give up already
779 if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
783 /* put a copy of the tag *after* the data read from the file
784 * so that strstr() will find something with no reliance on
787 memcpy(buffer + *len, tag, taglen);
788 endbody = strstr(buffer, tag);
789 if (endbody == buffer + *len) {
792 bodylen = endbody - buffer;
793 endbody += strlen(tag);
794 /* Skip all the trailing cruft after the end tag to the next line */
796 if (*endbody == '\n') {
803 pos = -(apr_off_t)(*len - (endbody - buffer));
804 if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
808 /* Give the caller back the actual body's file offset and length */
810 return pos - (endbody - buffer);
814 /* Stripping out RFC822 comments */
816 static void strip_paren_comments(char *hdr)
818 /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
819 /* Nope, it isn't correct. Fails to handle backslash escape as well. */
823 hdr = strchr(hdr, '"');
829 else if (*hdr == '(') {
830 while (*hdr && *hdr != ')') {
844 /* Getting to a header body from the header */
846 static char *lcase_header_name_return_body(char *header, request_rec *r)
850 for ( ; *cp && *cp != ':' ; ++cp) {
851 *cp = apr_tolower(*cp);
855 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
856 "Syntax error in type map, no ':' in %s for header %s",
857 r->filename, header);
863 } while (*cp && apr_isspace(*cp));
866 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
867 "Syntax error in type map --- no header body: %s for %s",
868 r->filename, header);
875 static int read_type_map(apr_file_t **map, negotiation_state *neg,
878 request_rec *r = neg->r;
879 apr_file_t *map_ = NULL;
881 char buffer[MAX_STRING_LEN];
882 enum header_state hstate;
883 struct var_rec mime_info;
889 /* We are not using multiviews */
890 neg->count_multiviews_variants = 0;
892 if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED,
893 APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) {
894 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
895 "cannot access type map file: %s", rr->filename);
896 return HTTP_FORBIDDEN;
899 clean_var_rec(&mime_info);
903 hstate = get_header_line(buffer, MAX_STRING_LEN, *map);
905 if (hstate == header_seen) {
906 char *body1 = lcase_header_name_return_body(buffer, neg->r);
910 return HTTP_INTERNAL_SERVER_ERROR;
913 strip_paren_comments(body1);
916 if (!strncmp(buffer, "uri:", 4)) {
917 mime_info.file_name = ap_get_token(neg->pool, &body, 0);
919 else if (!strncmp(buffer, "content-type:", 13)) {
920 struct accept_rec accept_info;
922 get_entry(neg->pool, &accept_info, body);
923 set_mime_fields(&mime_info, &accept_info);
926 else if (!strncmp(buffer, "content-length:", 15)) {
927 mime_info.bytes = apr_atoi64((char *)body);
930 else if (!strncmp(buffer, "content-language:", 17)) {
931 mime_info.content_languages = do_languages_line(neg->pool,
935 else if (!strncmp(buffer, "content-encoding:", 17)) {
936 mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
939 else if (!strncmp(buffer, "description:", 12)) {
940 char *desc = apr_pstrdup(neg->pool, body);
943 for (cp = desc; *cp; ++cp) {
944 if (*cp=='\n') *cp=' ';
946 if (cp>desc) *(cp-1)=0;
947 mime_info.description = desc;
949 else if (!strncmp(buffer, "body:", 5)) {
950 char *tag = apr_pstrdup(neg->pool, body);
951 char *eol = strchr(tag, '\0');
952 apr_size_t len = MAX_STRING_LEN;
953 while (--eol >= tag && apr_isspace(*eol))
955 if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) {
956 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
957 "Syntax error in type map, no end tag '%s'"
958 "found in %s for Body: content.",
962 mime_info.bytes = len;
963 mime_info.file_name = apr_filename_of_pathname(rr->filename);
967 if (*mime_info.file_name && has_content) {
968 void *new_var = apr_array_push(neg->avail_vars);
970 memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
973 clean_var_rec(&mime_info);
976 } while (hstate != header_eof);
979 apr_file_close(map_);
981 set_vlist_validator(r, rr);
987 /* Sort function used by read_types_multi. */
988 static int variantsortf(var_rec *a, var_rec *b) {
990 /* First key is the source quality, sort in descending order. */
992 /* XXX: note that we currently implement no method of setting the
993 * source quality for multiviews variants, so we are always comparing
996 if (a->source_quality < b->source_quality)
998 if (a->source_quality > b->source_quality)
1001 /* Second key is the variant name */
1002 return strcmp(a->file_name, b->file_name);
1005 /*****************************************************************
1007 * Same as read_type_map, except we use a filtered directory listing
1011 static int read_types_multi(negotiation_state *neg)
1013 request_rec *r = neg->r;
1019 apr_status_t status;
1020 struct var_rec mime_info;
1021 struct accept_rec accept_info;
1025 clean_var_rec(&mime_info);
1027 if (r->proxyreq || !r->filename
1028 || !ap_os_is_path_absolute(neg->pool, r->filename)) {
1032 /* Only absolute paths here */
1033 if (!(filp = strrchr(r->filename, '/'))) {
1037 prefix_len = strlen(filp);
1039 if ((status = apr_dir_open(&dirp, neg->dir_name,
1040 neg->pool)) != APR_SUCCESS) {
1041 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
1042 "cannot read directory for multi: %s", neg->dir_name);
1043 return HTTP_FORBIDDEN;
1046 while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
1047 apr_array_header_t *exception_list;
1048 request_rec *sub_req;
1050 /* Do we have a match? */
1051 #ifdef CASE_BLIND_FILESYSTEM
1052 if (strncasecmp(dirent.name, filp, prefix_len)) {
1054 if (strncmp(dirent.name, filp, prefix_len)) {
1058 if (dirent.name[prefix_len] != '.') {
1062 /* Don't negotiate directories and other unusual files
1063 * Really shouldn't see anything but DIR/LNK/REG here,
1064 * and we aught to discover if the LNK was interesting.
1066 * Of course, this only helps platforms that capture the
1067 * the filetype in apr_dir_read(), which most can once
1068 * they are optimized with some magic [it's known to the
1069 * dirent, not associated to the inode, on most FS's.]
1071 if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR))
1074 /* Ok, something's here. Maybe nothing useful. Remember that
1075 * we tried, if we completely fail, so we can reject the request!
1079 /* See if it's something which we have access to, and which
1080 * has a known type and encoding (as opposed to something
1081 * which we'll be slapping default_type on later).
1083 sub_req = ap_sub_req_lookup_dirent(&dirent, r, AP_SUBREQ_MERGE_ARGS,
1086 /* Double check, we still don't multi-resolve non-ordinary files
1088 if (sub_req->finfo.filetype != APR_REG)
1091 /* If it has a handler, we'll pretend it's a CGI script,
1092 * since that's a good indication of the sort of thing it
1095 if (sub_req->handler && !sub_req->content_type) {
1096 ap_set_content_type(sub_req, CGI_MAGIC_TYPE);
1100 * mod_mime will _always_ provide us the base name in the
1101 * ap-mime-exception-list, if it processed anything. If
1102 * this list is empty, give up immediately, there was
1103 * nothing interesting. For example, looking at the files
1104 * readme.txt and readme.foo, we will throw away .foo if
1105 * it's an insignificant file (e.g. did not identify a
1106 * language, charset, encoding, content type or handler,)
1109 (apr_array_header_t *)apr_table_get(sub_req->notes,
1110 "ap-mime-exceptions-list");
1112 if (!exception_list) {
1113 ap_destroy_sub_req(sub_req);
1117 /* Each unregonized bit better match our base name, in sequence.
1118 * A test of index.html.foo will match index.foo or index.html.foo,
1119 * but it will never transpose the segments and allow index.foo.html
1120 * because that would introduce too much CPU consumption. Better that
1121 * we don't attempt a many-to-many match here.
1124 int nexcept = exception_list->nelts;
1125 char **cur_except = (char**)exception_list->elts;
1126 char *segstart = filp, *segend, saveend;
1128 while (*segstart && nexcept) {
1129 if (!(segend = strchr(segstart, '.')))
1130 segend = strchr(segstart, '\0');
1134 #ifdef CASE_BLIND_FILESYSTEM
1135 if (strcasecmp(segstart, *cur_except) == 0) {
1137 if (strcmp(segstart, *cur_except) == 0) {
1147 segstart = segend + 1;
1151 /* Something you don't know is, something you don't know...
1153 ap_destroy_sub_req(sub_req);
1159 * ###: be warned, the _default_ content type is already
1160 * picked up here! If we failed the subrequest, or don't
1161 * know what we are serving, then continue.
1163 if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
1164 ap_destroy_sub_req(sub_req);
1168 /* If it's a map file, we use that instead of the map
1171 if (((sub_req->content_type) &&
1172 !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
1173 ((sub_req->handler) &&
1174 !strcmp(sub_req->handler, "type-map"))) {
1176 apr_dir_close(dirp);
1177 neg->avail_vars->nelts = 0;
1178 if (sub_req->status != HTTP_OK) {
1179 return sub_req->status;
1181 return read_type_map(NULL, neg, sub_req);
1184 /* Have reasonable variant --- gather notes. */
1186 mime_info.sub_req = sub_req;
1187 mime_info.file_name = apr_pstrdup(neg->pool, dirent.name);
1188 if (sub_req->content_encoding) {
1189 mime_info.content_encoding = sub_req->content_encoding;
1191 if (sub_req->content_languages) {
1192 mime_info.content_languages = sub_req->content_languages;
1195 get_entry(neg->pool, &accept_info, sub_req->content_type);
1196 set_mime_fields(&mime_info, &accept_info);
1198 new_var = apr_array_push(neg->avail_vars);
1199 memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
1201 neg->count_multiviews_variants++;
1203 clean_var_rec(&mime_info);
1206 apr_dir_close(dirp);
1208 /* We found some file names that matched. None could be served.
1209 * Rather than fall out to autoindex or some other mapper, this
1212 if (anymatch && !neg->avail_vars->nelts) {
1213 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1214 "Negotiation: discovered file(s) matching request: %s"
1215 " (None could be negotiated).",
1217 return HTTP_NOT_FOUND;
1220 set_vlist_validator(r, r);
1222 /* Sort the variants into a canonical order. The negotiation
1223 * result sometimes depends on the order of the variants. By
1224 * sorting the variants into a canonical order, rather than using
1225 * the order in which readdir() happens to return them, we ensure
1226 * that the negotiation result will be consistent over filesystem
1227 * backup/restores and over all mirror sites.
1230 qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
1231 sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
1237 /*****************************************************************
1238 * And now for the code you've been waiting for... actually
1239 * finding a match to the client's requirements.
1242 /* Matching MIME types ... the star/star and foo/star commenting conventions
1243 * are implemented here. (You know what I mean by star/star, but just
1244 * try mentioning those three characters in a C comment). Using strcmp()
1245 * is legit, because everything has already been smashed to lowercase.
1247 * Note also that if we get an exact match on the media type, we update
1248 * level_matched for use in level_cmp below...
1250 * We also give a value for mime_stars, which is used later. It should
1251 * be 1 for star/star, 2 for type/star and 3 for type/subtype.
1254 static int mime_match(accept_rec *accept_r, var_rec *avail)
1256 const char *accept_type = accept_r->name;
1257 const char *avail_type = avail->mime_type;
1258 int len = strlen(accept_type);
1260 if (accept_type[0] == '*') { /* Anything matches star/star */
1261 if (avail->mime_stars < 1) {
1262 avail->mime_stars = 1;
1266 else if ((accept_type[len - 1] == '*') &&
1267 !strncmp(accept_type, avail_type, len - 2)) {
1268 if (avail->mime_stars < 2) {
1269 avail->mime_stars = 2;
1273 else if (!strcmp(accept_type, avail_type)
1274 || (!strcmp(accept_type, "text/html")
1275 && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE)
1276 || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) {
1277 if (accept_r->level >= avail->level) {
1278 avail->level_matched = avail->level;
1279 avail->mime_stars = 3;
1287 /* This code implements a piece of the tie-breaking algorithm between
1288 * variants of equal quality. This piece is the treatment of variants
1289 * of the same base media type, but different levels. What we want to
1290 * return is the variant at the highest level that the client explicitly
1291 * claimed to accept.
1293 * If all the variants available are at a higher level than that, or if
1294 * the client didn't say anything specific about this media type at all
1295 * and these variants just got in on a wildcard, we prefer the lowest
1296 * level, on grounds that that's the one that the client is least likely
1299 * (This is all motivated by treatment of levels in HTML --- we only
1300 * want to give level 3 to browsers that explicitly ask for it; browsers
1301 * that don't, including HTTP/0.9 browsers that only get the implicit
1302 * "Accept: * / *" [space added to avoid confusing cpp --- no, that
1303 * syntax doesn't really work] should get HTML2 if available).
1305 * (Note that this code only comes into play when we are choosing among
1306 * variants of equal quality, where the draft standard gives us a fair
1307 * bit of leeway about what to do. It ain't specified by the standard;
1308 * rather, it is a choice made by this server about what to do in cases
1309 * where the standard does not specify a unique course of action).
1312 static int level_cmp(var_rec *var1, var_rec *var2)
1314 /* Levels are only comparable between matching media types */
1316 if (var1->is_pseudo_html && !var2->is_pseudo_html) {
1320 if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
1323 /* The result of the above if statements is that, if we get to
1324 * here, both variants have the same mime_type or both are
1328 /* Take highest level that matched, if either did match. */
1330 if (var1->level_matched > var2->level_matched) {
1333 if (var1->level_matched < var2->level_matched) {
1337 /* Neither matched. Take lowest level, if there's a difference. */
1339 if (var1->level < var2->level) {
1342 if (var1->level > var2->level) {
1351 /* Finding languages. The main entry point is set_language_quality()
1352 * which is called for each variant. It sets two elements in the
1354 * language_quality - the 'q' value of the 'best' matching language
1355 * from Accept-Language: header (HTTP/1.1)
1356 * lang_index - Non-negotiated language priority, using
1357 * position of language on the Accept-Language:
1358 * header, if present, else LanguagePriority
1361 * When we do the variant checking for best variant, we use language
1362 * quality first, and if a tie, language_index next (this only applies
1363 * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
1364 * algorithm, lang_index is never used.
1366 * set_language_quality() calls find_lang_index() and find_default_index()
1367 * to set lang_index.
1370 static int find_lang_index(apr_array_header_t *accept_langs, char *lang)
1375 if (!lang || !accept_langs) {
1379 alang = (const char **) accept_langs->elts;
1381 for (i = 0; i < accept_langs->nelts; ++i) {
1382 if (!strncmp(lang, *alang, strlen(*alang))) {
1385 alang += (accept_langs->elt_size / sizeof(char*));
1391 /* set_default_lang_quality() sets the quality we apply to variants
1392 * which have no language assigned to them. If none of the variants
1393 * have a language, we are not negotiating on language, so all are
1394 * acceptable, and we set the default q value to 1.0. However if
1395 * some of the variants have languages, we set this default to 0.0001.
1396 * The value of this default will be applied to all variants with
1397 * no explicit language -- which will have the effect of making them
1398 * acceptable, but only if no variants with an explicit language
1399 * are acceptable. The default q value set here is assigned to variants
1400 * with no language type in set_language_quality().
1402 * Note that if using the RVSA/1.0 algorithm, we don't use this
1406 static void set_default_lang_quality(negotiation_state *neg)
1408 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
1411 if (!neg->dont_fiddle_headers) {
1412 for (j = 0; j < neg->avail_vars->nelts; ++j) {
1413 var_rec *variant = &avail_recs[j];
1414 if (variant->content_languages &&
1415 variant->content_languages->nelts) {
1416 neg->default_lang_quality = 0.0001f;
1422 neg->default_lang_quality = 1.0f;
1425 /* Set the language_quality value in the variant record. Also
1426 * assigns lang_index for ForceLanguagePriority.
1428 * To find the language_quality value, we look for the 'q' value
1429 * of the 'best' matching language on the Accept-Language
1430 * header. The 'best' match is the language on Accept-Language
1431 * header which matches the language of this variant either fully,
1432 * or as far as the prefix marker (-). If two or more languages
1433 * match, use the longest string from the Accept-Language header
1434 * (see HTTP/1.1 [14.4])
1436 * When a variant has multiple languages, we find the 'best'
1437 * match for each variant language tag as above, then select the
1438 * one with the highest q value. Because both the accept-header
1439 * and variant can have multiple languages, we now have a hairy
1440 * loop-within-a-loop here.
1442 * If the variant has no language and we have no Accept-Language
1443 * items, leave the quality at 1.0 and return.
1445 * If the variant has no language, we use the default as set by
1446 * set_default_lang_quality() (1.0 if we are not negotiating on
1447 * language, 0.001 if we are).
1449 * Following the setting of the language quality, we drop through to
1450 * set the old 'lang_index'. This is set based on either the order
1451 * of the languages on the Accept-Language header, or the
1452 * order on the LanguagePriority directive. This is only used
1453 * in the negotiation if the language qualities tie.
1456 static void set_language_quality(negotiation_state *neg, var_rec *variant)
1458 int forcepriority = neg->conf->forcelangpriority;
1459 if (forcepriority == FLP_UNDEF) {
1460 forcepriority = FLP_DEFAULT;
1463 if (!variant->content_languages || !variant->content_languages->nelts) {
1464 /* This variant has no content-language, so use the default
1465 * quality factor for variants with no content-language
1466 * (previously set by set_default_lang_quality()).
1467 * Leave the factor alone (it remains at 1.0) when we may not fiddle
1470 if (!neg->dont_fiddle_headers) {
1471 variant->lang_quality = neg->default_lang_quality;
1473 if (!neg->accept_langs) {
1474 return; /* no accept-language header */
1479 /* Variant has one (or more) languages. Look for the best
1480 * match. We do this by going through each language on the
1481 * variant description looking for a match on the
1482 * Accept-Language header. The best match is the longest
1483 * matching language on the header. The final result is the
1484 * best q value from all the languages on the variant
1488 if (!neg->accept_langs) {
1489 /* no accept-language header makes the variant indefinite */
1490 variant->definite = 0;
1492 else { /* There is an accept-language with 0 or more items */
1493 accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
1494 accept_rec *best = NULL, *star = NULL;
1495 accept_rec *bestthistag;
1497 float fiddle_q = 0.0f;
1498 int any_match_on_star = 0;
1500 apr_size_t alen, longest_lang_range_len;
1502 for (j = 0; j < variant->content_languages->nelts; ++j) {
1505 longest_lang_range_len = 0;
1508 /* lang is the variant's language-tag, which is the one
1509 * we are allowed to use the prefix of in HTTP/1.1
1511 lang = ((char **) (variant->content_languages->elts))[j];
1513 /* now find the best (i.e. longest) matching
1514 * Accept-Language header language. We put the best match
1515 * for this tag in bestthistag. We cannot update the
1516 * overall best (based on q value) because the best match
1517 * for this tag is the longest language item on the accept
1518 * header, not necessarily the highest q.
1520 for (i = 0; i < neg->accept_langs->nelts; ++i) {
1521 if (!strcmp(accs[i].name, "*")) {
1527 /* Find language. We match if either the variant
1528 * language tag exactly matches the language range
1529 * from the accept header, or a prefix of the variant
1530 * language tag up to a '-' character matches the
1531 * whole of the language range in the Accept-Language
1532 * header. Note that HTTP/1.x allows any number of
1533 * '-' characters in a tag or range, currently only
1534 * tags with zero or one '-' characters are defined
1535 * for general use (see rfc1766).
1537 * We only use language range in the Accept-Language
1538 * header the best match for the variant language tag
1539 * if it is longer than the previous best match.
1542 alen = strlen(accs[i].name);
1544 if ((strlen(lang) >= alen) &&
1545 !strncmp(lang, accs[i].name, alen) &&
1546 ((lang[alen] == 0) || (lang[alen] == '-')) ) {
1548 if (alen > longest_lang_range_len) {
1549 longest_lang_range_len = alen;
1550 bestthistag = &accs[i];
1554 if (!bestthistag && !neg->dont_fiddle_headers) {
1555 /* The next bit is a fiddle. Some browsers might
1556 * be configured to send more specific language
1557 * ranges than desirable. For example, an
1558 * Accept-Language of en-US should never match
1559 * variants with languages en or en-GB. But US
1560 * English speakers might pick en-US as their
1561 * language choice. So this fiddle checks if the
1562 * language range has a prefix, and if so, it
1563 * matches variants which match that prefix with a
1564 * priority of 0.001. So a request for en-US would
1565 * match variants of types en and en-GB, but at
1566 * much lower priority than matches of en-US
1567 * directly, or of any other language listed on
1568 * the Accept-Language header. Note that this
1569 * fiddle does not handle multi-level prefixes.
1571 if ((p = strchr(accs[i].name, '-'))) {
1572 int plen = p - accs[i].name;
1574 if (!strncmp(lang, accs[i].name, plen)) {
1580 /* Finished looking at Accept-Language headers, the best
1581 * (longest) match is in bestthistag, or NULL if no match
1584 (bestthistag && bestthistag->quality > best->quality)) {
1588 /* See if the tag matches on a * in the Accept-Language
1589 * header. If so, record this fact for later use
1591 if (!bestthistag && star) {
1592 any_match_on_star = 1;
1596 /* If one of the language tags of the variant matched on *, we
1597 * need to see if its q is better than that of any non-* match
1598 * on any other tag of the variant. If so the * match takes
1599 * precedence and the overall match is not definite.
1601 if ( any_match_on_star &&
1602 ((best && star->quality > best->quality) ||
1605 variant->definite = 0;
1608 variant->lang_quality = best ? best->quality : fiddle_q;
1612 /* Handle the ForceDefaultLanguage overrides, based on the best match
1613 * to LanguagePriority order. The best match is the lowest index of
1614 * any LanguagePriority match.
1616 if (((forcepriority & FLP_PREFER)
1617 && (variant->lang_index < 0))
1618 || ((forcepriority & FLP_FALLBACK)
1619 && !variant->lang_quality))
1624 for (j = 0; j < variant->content_languages->nelts; ++j)
1626 /* lang is the variant's language-tag, which is the one
1627 * we are allowed to use the prefix of in HTTP/1.1
1629 char *lang = ((char **) (variant->content_languages->elts))[j];
1632 /* If we wish to fallback or
1633 * we use our own LanguagePriority index.
1635 idx = find_lang_index(neg->conf->language_priority, lang);
1636 if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
1642 if (variant->lang_quality) {
1643 if (forcepriority & FLP_PREFER) {
1644 variant->lang_index = bestidx;
1648 if (forcepriority & FLP_FALLBACK) {
1649 variant->lang_index = bestidx;
1650 variant->lang_quality = .0001f;
1651 variant->definite = 0;
1659 /* Determining the content length --- if the map didn't tell us,
1660 * we have to do a stat() and remember for next time.
1663 static apr_off_t find_content_length(negotiation_state *neg, var_rec *variant)
1667 if (variant->bytes < 0) {
1668 if ( variant->sub_req
1669 && (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) {
1670 variant->bytes = variant->sub_req->finfo.size;
1673 char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
1674 variant->file_name);
1676 if (apr_stat(&statb, fullname,
1677 APR_FINFO_SIZE, neg->pool) == APR_SUCCESS) {
1678 variant->bytes = statb.size;
1683 return variant->bytes;
1686 /* For a given variant, find the best matching Accept: header
1687 * and assign the Accept: header's quality value to the
1688 * mime_type_quality field of the variant, for later use in
1689 * determining the best matching variant.
1692 static void set_accept_quality(negotiation_state *neg, var_rec *variant)
1695 accept_rec *accept_recs;
1699 /* if no Accept: header, leave quality alone (will
1700 * remain at the default value of 1)
1702 * XXX: This if is currently never true because of the effect of
1703 * maybe_add_default_accepts().
1705 if (!neg->accepts) {
1706 if (variant->mime_type && *variant->mime_type)
1707 variant->definite = 0;
1711 accept_recs = (accept_rec *) neg->accepts->elts;
1714 * Go through each of the ranges on the Accept: header,
1715 * looking for the 'best' match with this variant's
1716 * content-type. We use the best match's quality
1717 * value (from the Accept: header) for this variant's
1718 * mime_type_quality field.
1720 * The best match is determined like this:
1721 * type/type is better than type/ * is better than * / *
1722 * if match is type/type, use the level mime param if available
1724 for (i = 0; i < neg->accepts->nelts; ++i) {
1726 accept_rec *type = &accept_recs[i];
1727 int prev_mime_stars;
1729 prev_mime_stars = variant->mime_stars;
1731 if (!mime_match(type, variant)) {
1732 continue; /* didn't match the content type at all */
1735 /* did match - see if there were less or more stars than
1738 if (prev_mime_stars == variant->mime_stars) {
1739 continue; /* more stars => not as good a match */
1743 /* If we are allowed to mess with the q-values
1744 * and have no explicit q= parameters in the accept header,
1745 * make wildcards very low, so we have a low chance
1746 * of ending up with them if there's something better.
1749 if (!neg->dont_fiddle_headers && !neg->accept_q &&
1750 variant->mime_stars == 1) {
1753 else if (!neg->dont_fiddle_headers && !neg->accept_q &&
1754 variant->mime_stars == 2) {
1761 q_definite = (variant->mime_stars == 3);
1763 variant->mime_type_quality = q;
1764 variant->definite = variant->definite && q_definite;
1768 /* For a given variant, find the 'q' value of the charset given
1769 * on the Accept-Charset line. If no charsets are listed,
1770 * assume value of '1'.
1772 static void set_charset_quality(negotiation_state *neg, var_rec *variant)
1775 accept_rec *accept_recs;
1776 const char *charset = variant->content_charset;
1777 accept_rec *star = NULL;
1779 /* if no Accept-Charset: header, leave quality alone (will
1780 * remain at the default value of 1)
1782 if (!neg->accept_charsets) {
1783 if (charset && *charset)
1784 variant->definite = 0;
1788 accept_recs = (accept_rec *) neg->accept_charsets->elts;
1790 if (charset == NULL || !*charset) {
1791 /* Charset of variant not known */
1793 /* if not a text / * type, leave quality alone */
1794 if (!(!strncmp(variant->mime_type, "text/", 5)
1795 || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
1796 || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3)
1800 /* Don't go guessing if we are in strict header mode,
1801 * e.g. when running the rvsa, as any guess won't be reflected
1802 * in the variant list or content-location headers.
1804 if (neg->dont_fiddle_headers)
1807 charset = "iso-8859-1"; /* The default charset for HTTP text types */
1811 * Go through each of the items on the Accept-Charset header,
1812 * looking for a match with this variant's charset. If none
1813 * match, charset is unacceptable, so set quality to 0.
1815 for (i = 0; i < neg->accept_charsets->nelts; ++i) {
1817 accept_rec *type = &accept_recs[i];
1819 if (!strcmp(type->name, charset)) {
1820 variant->charset_quality = type->quality;
1823 else if (strcmp(type->name, "*") == 0) {
1827 /* No explicit match */
1829 variant->charset_quality = star->quality;
1830 variant->definite = 0;
1833 /* If this variant is in charset iso-8859-1, the default is 1.0 */
1834 if (strcmp(charset, "iso-8859-1") == 0) {
1835 variant->charset_quality = 1.0f;
1838 variant->charset_quality = 0.0f;
1843 /* is_identity_encoding is included for back-compat, but does anyone
1844 * use 7bit, 8bin or binary in their var files??
1847 static int is_identity_encoding(const char *enc)
1849 return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
1850 || !strcmp(enc, "binary"));
1854 * set_encoding_quality determines whether the encoding for a particular
1855 * variant is acceptable for the user-agent.
1857 * The rules for encoding are that if the user-agent does not supply
1858 * any Accept-Encoding header, then all encodings are allowed but a
1859 * variant with no encoding should be preferred.
1860 * If there is an empty Accept-Encoding header, then no encodings are
1861 * acceptable. If there is a non-empty Accept-Encoding header, then
1862 * any of the listed encodings are acceptable, as well as no encoding
1863 * unless the "identity" encoding is specifically excluded.
1865 static void set_encoding_quality(negotiation_state *neg, var_rec *variant)
1867 accept_rec *accept_recs;
1868 const char *enc = variant->content_encoding;
1869 accept_rec *star = NULL;
1870 float value_if_not_found = 0.0f;
1873 if (!neg->accept_encodings) {
1874 /* We had no Accept-Encoding header, assume that all
1875 * encodings are acceptable with a low quality,
1876 * but we prefer no encoding if available.
1878 if (!enc || is_identity_encoding(enc))
1879 variant->encoding_quality = 1.0f;
1881 variant->encoding_quality = 0.5f;
1886 if (!enc || is_identity_encoding(enc)) {
1888 value_if_not_found = 0.0001f;
1891 accept_recs = (accept_rec *) neg->accept_encodings->elts;
1893 /* Go through each of the encodings on the Accept-Encoding: header,
1894 * looking for a match with our encoding. x- prefixes are ignored.
1896 if (enc[0] == 'x' && enc[1] == '-') {
1899 for (i = 0; i < neg->accept_encodings->nelts; ++i) {
1901 char *name = accept_recs[i].name;
1903 if (name[0] == 'x' && name[1] == '-') {
1907 if (!strcmp(name, enc)) {
1908 variant->encoding_quality = accept_recs[i].quality;
1912 if (strcmp(name, "*") == 0) {
1913 star = &accept_recs[i];
1917 /* No explicit match */
1919 variant->encoding_quality = star->quality;
1923 /* Encoding not found on Accept-Encoding: header, so it is
1924 * _not_ acceptable unless it is the identity (no encoding)
1926 variant->encoding_quality = value_if_not_found;
1929 /*************************************************************
1930 * Possible results of the variant selection algorithm
1932 enum algorithm_results {
1933 alg_choice = 1, /* choose variant */
1934 alg_list /* list variants */
1937 /* Below is the 'best_match' function. It returns an int, which has
1938 * one of the two values alg_choice or alg_list, which give the result
1939 * of the variant selection algorithm. alg_list means that no best
1940 * variant was found by the algorithm, alg_choice means that a best
1941 * variant was found and should be returned. The list/choice
1942 * terminology comes from TCN (rfc2295), but is used in a more generic
1943 * way here. The best variant is returned in *pbest. best_match has
1944 * two possible algorithms for determining the best variant: the
1945 * RVSA/1.0 algorithm (from RFC2296), and the standard Apache
1946 * algorithm. These are split out into separate functions
1947 * (is_variant_better_rvsa() and is_variant_better()). Selection of
1948 * one is through the neg->use_rvsa flag.
1950 * The call to best_match also creates full information, including
1951 * language, charset, etc quality for _every_ variant. This is needed
1952 * for generating a correct Vary header, and can be used for the
1953 * Alternates header, the human-readable list responses and 406 errors.
1956 /* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
1957 * v1.0) from rfc2296. This is the algorithm that goes together with
1958 * transparent content negotiation (TCN).
1960 static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant,
1961 var_rec *best, float *p_bestq)
1963 float bestq = *p_bestq, q;
1965 /* TCN does not cover negotiation on content-encoding. For now,
1966 * we ignore the encoding unless it was explicitly excluded.
1968 if (variant->encoding_quality == 0.0f)
1971 q = variant->mime_type_quality *
1972 variant->source_quality *
1973 variant->charset_quality *
1974 variant->lang_quality;
1976 /* RFC 2296 calls for the result to be rounded to 5 decimal places,
1977 * but we don't do that because it serves no useful purpose other
1978 * than to ensure that a remote algorithm operates on the same
1979 * precision as ours. That is silly, since what we obviously want
1980 * is for the algorithm to operate on the best available precision
1981 * regardless of who runs it. Since the above calculation may
1982 * result in significant variance at 1e-12, rounding would be bogus.
1986 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1987 "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
1988 "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
1989 "q=%1.5f definite=%d",
1990 (variant->file_name ? variant->file_name : ""),
1991 (variant->mime_type ? variant->mime_type : ""),
1992 (variant->content_languages
1993 ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
1995 variant->source_quality,
1996 variant->mime_type_quality,
1997 variant->lang_quality,
1998 variant->charset_quality,
1999 variant->encoding_quality,
2012 /* If the best variant's encoding is of lesser quality than
2013 * this variant, then we prefer this variant
2015 if (variant->encoding_quality > best->encoding_quality) {
2023 /* Negotiation algorithm as used by previous versions of Apache
2027 static int is_variant_better(negotiation_state *neg, var_rec *variant,
2028 var_rec *best, float *p_bestq)
2030 float bestq = *p_bestq, q;
2033 /* For non-transparent negotiation, server can choose how
2034 * to handle the negotiation. We'll use the following in
2035 * order: content-type, language, content-type level, charset,
2036 * content encoding, content length.
2038 * For each check, we have three possible outcomes:
2039 * This variant is worse than current best: return 0
2040 * This variant is better than the current best:
2041 * assign this variant's q to *p_bestq, and return 1
2042 * This variant is just as desirable as the current best:
2043 * drop through to the next test.
2045 * This code is written in this long-winded way to allow future
2046 * customisation, either by the addition of additional
2047 * checks, or to allow the order of the checks to be determined
2048 * by configuration options (e.g. we might prefer to check
2049 * language quality _before_ content type).
2052 /* First though, eliminate this variant if it is not
2053 * acceptable by type, charset, encoding or language.
2057 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
2058 "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2059 "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
2060 (variant->file_name ? variant->file_name : ""),
2061 (variant->mime_type ? variant->mime_type : ""),
2062 (variant->content_languages
2063 ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
2065 variant->source_quality,
2066 variant->mime_type_quality,
2067 variant->lang_quality,
2068 variant->lang_index,
2069 variant->charset_quality,
2070 variant->encoding_quality);
2073 if (variant->encoding_quality == 0.0f ||
2074 variant->lang_quality == 0.0f ||
2075 variant->source_quality == 0.0f ||
2076 variant->charset_quality == 0.0f ||
2077 variant->mime_type_quality == 0.0f) {
2078 return 0; /* don't consider unacceptables */
2081 q = variant->mime_type_quality * variant->source_quality;
2082 if (q == 0.0 || q < bestq) {
2085 if (q > bestq || !best) {
2091 if (variant->lang_quality < best->lang_quality) {
2094 if (variant->lang_quality > best->lang_quality) {
2099 /* if language qualities were equal, try the LanguagePriority stuff */
2100 if (best->lang_index != -1 &&
2101 (variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
2104 if (variant->lang_index != -1 &&
2105 (best->lang_index == -1 || variant->lang_index < best->lang_index)) {
2110 /* content-type level (sometimes used with text/html, though we
2111 * support it on other types too)
2113 levcmp = level_cmp(variant, best);
2123 if (variant->charset_quality < best->charset_quality) {
2126 /* If the best variant's charset is ISO-8859-1 and this variant has
2127 * the same charset quality, then we prefer this variant
2130 if (variant->charset_quality > best->charset_quality ||
2131 ((variant->content_charset != NULL &&
2132 *variant->content_charset != '\0' &&
2133 strcmp(variant->content_charset, "iso-8859-1") != 0) &&
2134 (best->content_charset == NULL ||
2135 *best->content_charset == '\0' ||
2136 strcmp(best->content_charset, "iso-8859-1") == 0))) {
2141 /* Prefer the highest value for encoding_quality.
2143 if (variant->encoding_quality < best->encoding_quality) {
2146 if (variant->encoding_quality > best->encoding_quality) {
2151 /* content length if all else equal */
2152 if (find_content_length(neg, variant) >= find_content_length(neg, best)) {
2156 /* ok, to get here means every thing turned out equal, except
2157 * we have a shorter content length, so use this variant
2163 /* figure out, whether a variant is in a specific language
2164 * it returns also false, if the variant has no language.
2166 static int variant_has_language(var_rec *variant, const char *lang)
2172 || !variant->content_languages
2173 || !(max = variant->content_languages->nelts)) {
2177 for (j = 0; j < max; ++j) {
2179 ((char **) (variant->content_languages->elts))[j])) {
2187 static int best_match(negotiation_state *neg, var_rec **pbest)
2192 enum algorithm_results algorithm_result;
2194 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2196 const char *preferred_language = apr_table_get(neg->r->subprocess_env,
2199 set_default_lang_quality(neg);
2202 * Find the 'best' variant
2203 * We run the loop possibly twice: if "prefer-language"
2204 * environment variable is set but we did not find an appropriate
2205 * best variant. In that case forget the preferred language and
2206 * negotiate over all variants.
2212 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2213 var_rec *variant = &avail_recs[j];
2215 /* if a language is preferred, but the current variant
2216 * is not in that language, then drop it for now
2218 if ( preferred_language
2219 && !variant_has_language(variant, preferred_language)) {
2223 /* Find all the relevant 'quality' values from the
2224 * Accept... headers, and store in the variant. This also
2225 * prepares for sending an Alternates header etc so we need to
2226 * do it even if we do not actually plan to find a best
2229 set_accept_quality(neg, variant);
2230 /* accept the preferred language, even when it's not listed within
2231 * the Accept-Language header
2233 if (preferred_language) {
2234 variant->lang_quality = 1.0f;
2235 variant->definite = 1;
2238 set_language_quality(neg, variant);
2240 set_encoding_quality(neg, variant);
2241 set_charset_quality(neg, variant);
2243 /* Only do variant selection if we may actually choose a
2244 * variant for the client
2246 if (neg->may_choose) {
2248 /* Now find out if this variant is better than the current
2249 * best, either using the RVSA/1.0 algorithm, or Apache's
2250 * internal server-driven algorithm. Presumably other
2251 * server-driven algorithms are possible, and could be
2255 if (neg->use_rvsa) {
2256 if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
2261 if (is_variant_better(neg, variant, best, &bestq)) {
2268 /* We now either have a best variant, or no best variant */
2270 if (neg->use_rvsa) {
2271 /* calculate result for RVSA/1.0 algorithm:
2272 * only a choice response if the best variant has q>0
2275 algorithm_result = (best && best->definite) && (bestq > 0) ?
2276 alg_choice : alg_list;
2279 /* calculate result for Apache negotiation algorithm */
2280 algorithm_result = bestq > 0 ? alg_choice : alg_list;
2283 /* run the loop again, if the "prefer-language" got no clear result */
2284 if (preferred_language && (!best || algorithm_result != alg_choice)) {
2285 preferred_language = NULL;
2292 /* Returning a choice response with a non-neighboring variant is a
2293 * protocol security error in TCN (see rfc2295). We do *not*
2294 * verify here that the variant and URI are neighbors, even though
2295 * we may return alg_choice. We depend on the environment (the
2296 * caller) to only declare the resource transparently negotiable if
2297 * all variants are neighbors.
2300 return algorithm_result;
2303 /* Sets response headers for a negotiated response.
2304 * neg->is_transparent determines whether a transparently negotiated
2305 * response or a plain `server driven negotiation' response is
2306 * created. Applicable headers are Alternates, Vary, and TCN.
2308 * The Vary header we create is sometimes longer than is required for
2309 * the correct caching of negotiated results by HTTP/1.1 caches. For
2310 * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
2311 * the Accept: header assigns a 0 quality to .ps, then the results of
2312 * the two server-side negotiation algorithms we currently implement
2313 * will never depend on Accept-Language so we could return `Vary:
2314 * negotiate, accept' instead of the longer 'Vary: negotiate, accept,
2315 * accept-language' which the code below will return. A routine for
2316 * computing the exact minimal Vary header would be a huge pain to code
2317 * and maintain though, especially because we need to take all possible
2318 * twiddles in the server-side negotiation algorithms into account.
2320 static void set_neg_headers(request_rec *r, negotiation_state *neg,
2324 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2325 const char *sample_type = NULL;
2326 const char *sample_language = NULL;
2327 const char *sample_encoding = NULL;
2328 const char *sample_charset = NULL;
2333 apr_array_header_t *arr;
2334 int max_vlist_array = (neg->avail_vars->nelts * 21);
2335 int first_variant = 1;
2336 int vary_by_type = 0;
2337 int vary_by_language = 0;
2338 int vary_by_charset = 0;
2339 int vary_by_encoding = 0;
2342 /* In order to avoid O(n^2) memory copies in building Alternates,
2343 * we preallocate a apr_table_t with the maximum substrings possible,
2344 * fill it with the variant list, and then concatenate the entire array.
2345 * Note that if you change the number of substrings pushed, you also
2346 * need to change the calculation of max_vlist_array above.
2348 if (neg->send_alternates && neg->avail_vars->nelts)
2349 arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
2353 /* Put headers into err_headers_out, since send_http_header()
2354 * outputs both headers_out and err_headers_out.
2356 hdrs = r->err_headers_out;
2358 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2359 var_rec *variant = &avail_recs[j];
2361 if (variant->content_languages && variant->content_languages->nelts) {
2362 lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
2368 /* Calculate Vary by looking for any difference between variants */
2370 if (first_variant) {
2371 sample_type = variant->mime_type;
2372 sample_charset = variant->content_charset;
2373 sample_language = lang;
2374 sample_encoding = variant->content_encoding;
2377 if (!vary_by_type &&
2378 strcmp(sample_type ? sample_type : "",
2379 variant->mime_type ? variant->mime_type : "")) {
2382 if (!vary_by_charset &&
2383 strcmp(sample_charset ? sample_charset : "",
2384 variant->content_charset ?
2385 variant->content_charset : "")) {
2386 vary_by_charset = 1;
2388 if (!vary_by_language &&
2389 strcmp(sample_language ? sample_language : "",
2390 lang ? lang : "")) {
2391 vary_by_language = 1;
2393 if (!vary_by_encoding &&
2394 strcmp(sample_encoding ? sample_encoding : "",
2395 variant->content_encoding ?
2396 variant->content_encoding : "")) {
2397 vary_by_encoding = 1;
2402 if (!neg->send_alternates)
2405 /* Generate the string components for this Alternates entry */
2407 *((const char **) apr_array_push(arr)) = "{\"";
2408 *((const char **) apr_array_push(arr)) = variant->file_name;
2409 *((const char **) apr_array_push(arr)) = "\" ";
2411 qstr = (char *) apr_palloc(r->pool, 6);
2412 apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
2414 /* Strip trailing zeros (saves those valuable network bytes) */
2415 if (qstr[4] == '0') {
2417 if (qstr[3] == '0') {
2419 if (qstr[2] == '0') {
2424 *((const char **) apr_array_push(arr)) = qstr;
2426 if (variant->mime_type && *variant->mime_type) {
2427 *((const char **) apr_array_push(arr)) = " {type ";
2428 *((const char **) apr_array_push(arr)) = variant->mime_type;
2429 *((const char **) apr_array_push(arr)) = "}";
2431 if (variant->content_charset && *variant->content_charset) {
2432 *((const char **) apr_array_push(arr)) = " {charset ";
2433 *((const char **) apr_array_push(arr)) = variant->content_charset;
2434 *((const char **) apr_array_push(arr)) = "}";
2437 *((const char **) apr_array_push(arr)) = " {language ";
2438 *((const char **) apr_array_push(arr)) = lang;
2439 *((const char **) apr_array_push(arr)) = "}";
2441 if (variant->content_encoding && *variant->content_encoding) {
2442 /* Strictly speaking, this is non-standard, but so is TCN */
2444 *((const char **) apr_array_push(arr)) = " {encoding ";
2445 *((const char **) apr_array_push(arr)) = variant->content_encoding;
2446 *((const char **) apr_array_push(arr)) = "}";
2449 /* Note that the Alternates specification (in rfc2295) does
2450 * not require that we include {length x}, so we could omit it
2451 * if determining the length is too expensive. We currently
2452 * always include it though. 22 bytes is enough for 2^64.
2454 * If the variant is a CGI script, find_content_length would
2455 * return the length of the script, not the output it
2456 * produces, so we check for the presence of a handler and if
2457 * there is one we don't add a length.
2459 * XXX: TODO: This check does not detect a CGI script if we
2460 * get the variant from a type map. This needs to be fixed
2461 * (without breaking things if the type map specifies a
2462 * content-length, which currently leads to the correct result).
2464 if (!(variant->sub_req && variant->sub_req->handler)
2465 && (len = find_content_length(neg, variant)) >= 0) {
2467 lenstr = (char *) apr_palloc(r->pool, 22);
2468 apr_snprintf(lenstr, 22, "%" APR_OFF_T_FMT, len);
2469 *((const char **) apr_array_push(arr)) = " {length ";
2470 *((const char **) apr_array_push(arr)) = lenstr;
2471 *((const char **) apr_array_push(arr)) = "}";
2474 *((const char **) apr_array_push(arr)) = "}";
2475 *((const char **) apr_array_push(arr)) = ", "; /* trimmed below */
2478 if (neg->send_alternates && neg->avail_vars->nelts) {
2479 arr->nelts--; /* remove last comma */
2480 apr_table_mergen(hdrs, "Alternates",
2481 apr_array_pstrcat(r->pool, arr, '\0'));
2484 if (neg->is_transparent || vary_by_type || vary_by_language ||
2485 vary_by_language || vary_by_charset || vary_by_encoding) {
2487 apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
2488 neg->is_transparent ? ", negotiate" : "",
2489 vary_by_type ? ", accept" : "",
2490 vary_by_language ? ", accept-language" : "",
2491 vary_by_charset ? ", accept-charset" : "",
2492 vary_by_encoding ? ", accept-encoding" : "", NULL));
2495 if (neg->is_transparent) { /* Create TCN response header */
2496 apr_table_setn(hdrs, "TCN",
2497 alg_result == alg_list ? "list" : "choice");
2501 /**********************************************************************
2503 * Return an HTML list of variants. This is output as part of the
2504 * choice response or 406 status body.
2507 static char *make_variant_list(request_rec *r, negotiation_state *neg)
2509 apr_array_header_t *arr;
2511 int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
2513 /* In order to avoid O(n^2) memory copies in building the list,
2514 * we preallocate a apr_table_t with the maximum substrings possible,
2515 * fill it with the variant list, and then concatenate the entire array.
2517 arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
2519 *((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n";
2521 for (i = 0; i < neg->avail_vars->nelts; ++i) {
2522 var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
2523 const char *filename = variant->file_name ? variant->file_name : "";
2524 apr_array_header_t *languages = variant->content_languages;
2525 const char *description = variant->description
2526 ? variant->description
2529 /* The format isn't very neat, and it would be nice to make
2530 * the tags human readable (eg replace 'language en' with 'English').
2531 * Note that if you change the number of substrings pushed, you also
2532 * need to change the calculation of max_vlist_array above.
2534 *((const char **) apr_array_push(arr)) = "<li><a href=\"";
2535 *((const char **) apr_array_push(arr)) = filename;
2536 *((const char **) apr_array_push(arr)) = "\">";
2537 *((const char **) apr_array_push(arr)) = filename;
2538 *((const char **) apr_array_push(arr)) = "</a> ";
2539 *((const char **) apr_array_push(arr)) = description;
2541 if (variant->mime_type && *variant->mime_type) {
2542 *((const char **) apr_array_push(arr)) = ", type ";
2543 *((const char **) apr_array_push(arr)) = variant->mime_type;
2545 if (languages && languages->nelts) {
2546 *((const char **) apr_array_push(arr)) = ", language ";
2547 *((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool,
2550 if (variant->content_charset && *variant->content_charset) {
2551 *((const char **) apr_array_push(arr)) = ", charset ";
2552 *((const char **) apr_array_push(arr)) = variant->content_charset;
2554 if (variant->content_encoding) {
2555 *((const char **) apr_array_push(arr)) = ", encoding ";
2556 *((const char **) apr_array_push(arr)) = variant->content_encoding;
2558 *((const char **) apr_array_push(arr)) = "</li>\n";
2560 *((const char **) apr_array_push(arr)) = "</ul>\n";
2562 return apr_array_pstrcat(r->pool, arr, '\0');
2565 static void store_variant_list(request_rec *r, negotiation_state *neg)
2567 if (r->main == NULL) {
2568 apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
2571 apr_table_setn(r->main->notes, "variant-list",
2572 make_variant_list(r->main, neg));
2576 /* Called if we got a "Choice" response from the variant selection algorithm.
2577 * It checks the result of the chosen variant to see if it
2578 * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
2579 * Otherwise, add the appropriate headers to the current response.
2582 static int setup_choice_response(request_rec *r, negotiation_state *neg,
2585 request_rec *sub_req;
2586 const char *sub_vary;
2588 if (!variant->sub_req) {
2591 sub_req = ap_sub_req_lookup_file(variant->file_name, r, NULL);
2592 status = sub_req->status;
2594 if (status != HTTP_OK &&
2595 !apr_table_get(sub_req->err_headers_out, "TCN")) {
2596 ap_destroy_sub_req(sub_req);
2599 variant->sub_req = sub_req;
2602 sub_req = variant->sub_req;
2605 /* The variant selection algorithm told us to return a "Choice"
2606 * response. This is the normal variant response, with
2607 * some extra headers. First, ensure that the chosen
2608 * variant did or will not itself engage in transparent negotiation.
2609 * If not, set the appropriate headers, and fall through to
2610 * the normal variant handling
2613 /* This catches the error that a transparent type map selects a
2614 * transparent multiviews resource as the best variant.
2616 * XXX: We do not signal an error if a transparent type map
2617 * selects a _non_transparent multiviews resource as the best
2618 * variant, because we can generate a legal negotiation response
2619 * in this case. In this case, the vlist_validator of the
2620 * nontransparent subrequest will be lost however. This could
2621 * lead to cases in which a change in the set of variants or the
2622 * negotiation algorithm of the nontransparent resource is never
2623 * propagated up to a HTTP/1.1 cache which interprets Vary. To be
2624 * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
2625 * for this type of recursive negotiation too.
2627 if (neg->is_transparent &&
2628 apr_table_get(sub_req->err_headers_out, "TCN")) {
2629 return HTTP_VARIANT_ALSO_VARIES;
2632 /* This catches the error that a transparent type map recursively
2633 * selects, as the best variant, another type map which itself
2634 * causes transparent negotiation to be done.
2636 * XXX: Actually, we catch this error by catching all cases of
2637 * type map recursion. There are some borderline recursive type
2638 * map arrangements which would not produce transparent
2639 * negotiation protocol errors or lack of cache propagation
2640 * problems, but such arrangements are very hard to detect at this
2641 * point in the control flow, so we do not bother to single them
2644 * Recursive type maps imply a recursive arrangement of negotiated
2645 * resources which is visible to outside clients, and this is not
2646 * supported by the transparent negotiation caching protocols, so
2647 * if we are to have generic support for recursive type maps, we
2648 * have to create some configuration setting which makes all type
2649 * maps non-transparent when recursion is enabled. Also, if we
2650 * want recursive type map support which ensures propagation of
2651 * type map changes into HTTP/1.1 caches that handle Vary, we
2652 * would have to extend the current mechanism for generating
2653 * variant list validators.
2655 if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
2656 return HTTP_VARIANT_ALSO_VARIES;
2659 /* This adds an appropriate Variant-Vary header if the subrequest
2660 * is a multiviews resource.
2662 * XXX: TODO: Note that this does _not_ handle any Vary header
2663 * returned by a CGI if sub_req is a CGI script, because we don't
2664 * see that Vary header yet at this point in the control flow.
2665 * This won't cause any cache consistency problems _unless_ the
2666 * CGI script also returns a Cache-Control header marking the
2667 * response as cachable. This needs to be fixed, also there are
2668 * problems if a CGI returns an Etag header which also need to be
2671 if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
2672 apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
2674 /* Move the subreq Vary header into the main request to
2675 * prevent having two Vary headers in the response, which
2676 * would be legal but strange.
2678 apr_table_setn(r->err_headers_out, "Vary", sub_vary);
2679 apr_table_unset(sub_req->err_headers_out, "Vary");
2682 apr_table_setn(r->err_headers_out, "Content-Location",
2683 apr_pstrdup(r->pool, variant->file_name));
2685 set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */
2687 /* Still to do by caller: add Expires */
2692 /****************************************************************
2697 static int do_negotiation(request_rec *r, negotiation_state *neg,
2698 var_rec **bestp, int prefer_scripts)
2700 var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2701 int alg_result; /* result of variant selection algorithm */
2705 /* Decide if resource is transparently negotiable */
2707 /* GET or HEAD? (HEAD has same method number as GET) */
2708 if (r->method_number == M_GET) {
2710 /* maybe this should be configurable, see also the comment
2711 * about recursive type maps in setup_choice_response()
2713 neg->is_transparent = 1;
2715 /* We can't be transparent if we are a map file in the middle
2716 * of the request URI.
2718 if (r->path_info && *r->path_info)
2719 neg->is_transparent = 0;
2721 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2722 var_rec *variant = &avail_recs[j];
2724 /* We can't be transparent, because of internal
2725 * assumptions in best_match(), if there is a
2726 * non-neighboring variant. We can have a non-neighboring
2727 * variant when processing a type map.
2729 if (ap_strchr_c(variant->file_name, '/'))
2730 neg->is_transparent = 0;
2732 /* We can't be transparent, because of the behavior
2733 * of variant typemap bodies.
2735 if (variant->body) {
2736 neg->is_transparent = 0;
2741 if (neg->is_transparent) {
2742 parse_negotiate_header(r, neg);
2744 else { /* configure negotiation on non-transparent resource */
2745 neg->may_choose = 1;
2748 maybe_add_default_accepts(neg, prefer_scripts);
2750 alg_result = best_match(neg, bestp);
2752 /* alg_result is one of
2753 * alg_choice: a best variant is chosen
2754 * alg_list: no best variant is chosen
2757 if (alg_result == alg_list) {
2758 /* send a list response or HTTP_NOT_ACCEPTABLE error response */
2760 neg->send_alternates = 1; /* always include Alternates header */
2761 set_neg_headers(r, neg, alg_result);
2762 store_variant_list(r, neg);
2764 if (neg->is_transparent && neg->ua_supports_trans) {
2765 /* XXX todo: expires? cachability? */
2767 /* Some HTTP/1.0 clients are known to choke when they get
2768 * a 300 (multiple choices) response without a Location
2769 * header. However the 300 code response we are are about
2770 * to generate will only reach 1.0 clients which support
2771 * transparent negotiation, and they should be OK. The
2772 * response should never reach older 1.0 clients, even if
2773 * we have CacheNegotiatedDocs enabled, because no 1.0
2774 * proxy cache (we know of) will cache and return 300
2775 * responses (they certainly won't if they conform to the
2776 * HTTP/1.0 specification).
2778 return HTTP_MULTIPLE_CHOICES;
2782 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
2783 "no acceptable variant: %s", r->filename);
2784 return HTTP_NOT_ACCEPTABLE;
2788 /* Variant selection chose a variant */
2790 /* XXX todo: merge the two cases in the if statement below */
2791 if (neg->is_transparent) {
2793 if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
2794 return res; /* return if error */
2798 set_neg_headers(r, neg, alg_result);
2801 /* Make sure caching works - Vary should handle HTTP/1.1, but for
2802 * HTTP/1.0, we can't allow caching at all.
2805 /* XXX: Note that we only set r->no_cache to 1, which causes
2806 * Expires: <now> to be added, when responding to a HTTP/1.0
2807 * client. If we return the response to a 1.1 client, we do not
2808 * add Expires <now>, because doing so would degrade 1.1 cache
2809 * performance by preventing re-use of the response without prior
2810 * revalidation. On the other hand, if the 1.1 client is a proxy
2811 * which was itself contacted by a 1.0 client, or a proxy cache
2812 * which can be contacted later by 1.0 clients, then we currently
2813 * rely on this 1.1 proxy to add the Expires: <now> when it
2814 * forwards the response.
2816 * XXX: TODO: Find out if the 1.1 spec requires proxies and
2817 * tunnels to add Expires: <now> when forwarding the response to
2818 * 1.0 clients. I (kh) recall it is rather vague on this point.
2819 * Testing actual 1.1 proxy implementations would also be nice. If
2820 * Expires: <now> is not added by proxies then we need to always
2821 * include Expires: <now> ourselves to ensure correct caching, but
2822 * this would degrade HTTP/1.1 cache efficiency unless we also add
2823 * Cache-Control: max-age=N, which we currently don't.
2825 * Roy: No, we are not going to screw over HTTP future just to
2826 * ensure that people who can't be bothered to upgrade their
2827 * clients will always receive perfect server-side negotiation.
2828 * Hell, those clients are sending bogus accept headers anyway.
2830 * Manual setting of cache-control/expires always overrides this
2831 * automated kluge, on purpose.
2834 if ((!do_cache_negotiated_docs(r->server)
2835 && (r->proto_num < HTTP_VERSION(1,1)))
2836 && neg->count_multiviews_variants != 1) {
2843 static int handle_map_file(request_rec *r)
2845 negotiation_state *neg;
2851 if(strcmp(r->handler,MAP_FILE_MAGIC_TYPE) && strcmp(r->handler,"type-map"))
2854 neg = parse_accept_headers(r);
2855 if ((res = read_type_map(&map, neg, r))) {
2859 res = do_negotiation(r, neg, &best, 0);
2860 if (res != 0) return res;
2864 conn_rec *c = r->connection;
2865 apr_bucket_brigade *bb;
2868 ap_allow_standard_methods(r, REPLACE_ALLOW, M_GET, M_OPTIONS,
2871 * if (r->method_number == M_OPTIONS) {
2872 * return ap_send_http_options(r);
2875 if (r->method_number != M_GET && r->method_number != M_POST) {
2876 return HTTP_METHOD_NOT_ALLOWED;
2879 /* ### These may be implemented by adding some 'extra' info
2880 * of the file offset onto the etag
2881 * ap_update_mtime(r, r->finfo.mtime);
2882 * ap_set_last_modified(r);
2885 apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
2886 ap_set_content_length(r, best->bytes);
2888 /* set MIME type and charset as negotiated */
2889 if (best->mime_type && *best->mime_type) {
2890 if (best->content_charset && *best->content_charset) {
2891 ap_set_content_type(r, apr_pstrcat(r->pool,
2894 best->content_charset,
2898 ap_set_content_type(r, apr_pstrdup(r->pool, best->mime_type));
2902 /* set Content-language(s) as negotiated */
2903 if (best->content_languages && best->content_languages->nelts) {
2904 r->content_languages = apr_array_copy(r->pool,
2905 best->content_languages);
2908 /* set Content-Encoding as negotiated */
2909 if (best->content_encoding && *best->content_encoding) {
2910 r->content_encoding = apr_pstrdup(r->pool,
2911 best->content_encoding);
2914 if ((res = ap_meets_conditions(r)) != OK) {
2918 if ((res = ap_discard_request_body(r)) != OK) {
2921 bb = apr_brigade_create(r->pool, c->bucket_alloc);
2922 e = apr_bucket_file_create(map, best->body,
2923 (apr_size_t)best->bytes, r->pool,
2925 APR_BRIGADE_INSERT_TAIL(bb, e);
2926 e = apr_bucket_eos_create(c->bucket_alloc);
2927 APR_BRIGADE_INSERT_TAIL(bb, e);
2929 return ap_pass_brigade(r->output_filters, bb);
2932 if (r->path_info && *r->path_info) {
2933 /* remove any path_info from the end of the uri before trying
2934 * to change the filename. r->path_info from the original
2935 * request is passed along on the redirect.
2937 r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
2939 udir = ap_make_dirstr_parent(r->pool, r->uri);
2940 udir = ap_escape_uri(r->pool, udir);
2941 ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name,
2942 r->path_info, NULL), r);
2946 static int handle_multi(request_rec *r)
2948 negotiation_state *neg;
2949 var_rec *best, *avail_recs;
2950 request_rec *sub_req;
2954 if (r->finfo.filetype != APR_NOFILE
2955 || !(ap_allow_options(r) & OPT_MULTI)) {
2959 neg = parse_accept_headers(r);
2961 if ((res = read_types_multi(neg))) {
2963 /* free all allocated memory from subrequests */
2964 avail_recs = (var_rec *) neg->avail_vars->elts;
2965 for (j = 0; j < neg->avail_vars->nelts; ++j) {
2966 var_rec *variant = &avail_recs[j];
2967 if (variant->sub_req) {
2968 ap_destroy_sub_req(variant->sub_req);
2973 if (neg->avail_vars->nelts == 0) {
2977 res = do_negotiation(r, neg, &best,
2978 (r->method_number != M_GET) || r->args ||
2979 (r->path_info && *r->path_info));
2981 goto return_from_multi;
2983 if (!(sub_req = best->sub_req)) {
2984 /* We got this out of a map file, so we don't actually have
2985 * a sub_req structure yet. Get one now.
2988 sub_req = ap_sub_req_lookup_file(best->file_name, r, NULL);
2989 if (sub_req->status != HTTP_OK) {
2990 res = sub_req->status;
2991 ap_destroy_sub_req(sub_req);
2992 goto return_from_multi;
2995 if (sub_req->args == NULL) {
2996 sub_req->args = r->args;
2999 /* now do a "fast redirect" ... promotes the sub_req into the main req */
3000 ap_internal_fast_redirect(sub_req, r);
3002 /* give no advise for time on this subrequest. Perhaps we
3003 * should tally the last mtime amoung all variants, and date
3004 * the most recent, but that could confuse the proxies.
3008 /* clean up all but our favorite variant, since that sub_req
3009 * is now merged into the main request!
3011 avail_recs = (var_rec *) neg->avail_vars->elts;
3012 for (j = 0; j < neg->avail_vars->nelts; ++j) {
3013 var_rec *variant = &avail_recs[j];
3014 if (variant != best && variant->sub_req) {
3015 ap_destroy_sub_req(variant->sub_req);
3021 /**********************************************************************
3022 * There is a problem with content-encoding, as some clients send and
3023 * expect an x- token (e.g. x-gzip) while others expect the plain token
3024 * (i.e. gzip). To try and deal with this as best as possible we do
3025 * the following: if the client sent an Accept-Encoding header and it
3026 * contains a plain token corresponding to the content encoding of the
3027 * response, then set content encoding using the plain token. Else if
3028 * the A-E header contains the x- token use the x- token in the C-E
3029 * header. Else don't do anything.
3031 * Note that if no A-E header was sent, or it does not contain a token
3032 * compatible with the final content encoding, then the token in the
3033 * C-E header will be whatever was specified in the AddEncoding
3036 static int fix_encoding(request_rec *r)
3038 const char *enc = r->content_encoding;
3040 apr_array_header_t *accept_encodings;
3041 accept_rec *accept_recs;
3044 if (!enc || !*enc) {
3048 if (enc[0] == 'x' && enc[1] == '-') {
3052 if ((accept_encodings = do_header_line(r->pool,
3053 apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) {
3057 accept_recs = (accept_rec *) accept_encodings->elts;
3059 for (i = 0; i < accept_encodings->nelts; ++i) {
3060 char *name = accept_recs[i].name;
3062 if (!strcmp(name, enc)) {
3063 r->content_encoding = name;
3067 if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) {
3073 r->content_encoding = x_enc;
3080 static void register_hooks(apr_pool_t *p)
3082 ap_hook_fixups(fix_encoding,NULL,NULL,APR_HOOK_MIDDLE);
3083 ap_hook_type_checker(handle_multi,NULL,NULL,APR_HOOK_FIRST);
3084 ap_hook_handler(handle_map_file,NULL,NULL,APR_HOOK_MIDDLE);
3087 module AP_MODULE_DECLARE_DATA negotiation_module =
3089 STANDARD20_MODULE_STUFF,
3090 create_neg_dir_config, /* dir config creator */
3091 merge_neg_dir_configs, /* dir merger --- default is to override */
3092 NULL, /* server config */
3093 NULL, /* merge server config */
3094 negotiation_cmds, /* command apr_table_t */
3095 register_hooks /* register hooks */