mod_negotiation.c revision 5c2600a36b3e58b821885c7caefa5a1ae2a0eff4
return "Cannot combine ForceLanguagePriority options with None";
return "Cannot combine ForceLanguagePriority options None and Prefer";
return "Cannot combine ForceLanguagePriority options None and Fallback";
"Either 'on' or 'off' (default)"),
"space-delimited list of MIME language abbreviations"),
"Force LanguagePriority elections, either None, or Fallback and/or Prefer"),
* Record of available info on a media type specified by the client * (we also use 'em for encodings and languages) char *
name;
/* MUST be lowercase */ char *
charset;
/* for content-type only */ * Record of available info on a particular variant * Note that a few of these fields are updated by the actual negotiation * level_matched --- initialized to zero. Set to the value of level * if the client actually accepts this media type at that * level (and *not* if it got in on a wildcard). See level_cmp * mime_stars -- initialized to zero. Set to the number of stars * present in the best matching Accept header element. * definite -- initialized to 1. Set to 0 if there is a match which * makes the variant non-definite according to the rules const char *
mime_type;
/* MUST be lowercase */ const char *
file_name;
/* Set to 'this' (for map file body content) */ /* The next five items give the quality values for the dimensions * of negotiation for this variant. They are obtained from the * appropriate header lines, except for source_quality, which * is obtained from the variant itself (the 'qs' parameter value * from the variant's mime-type). Apart from source_quality, * these values are set when we find the quality for each variant * (see best_match()). source_quality is set from the 'qs' parameter * of the variant description or mime type: see set_mime_fields(). float lang_quality;
/* quality of this variant's language */ /* Now some special values */ float level;
/* Auxiliary to content-type... */ int lang_index;
/* Index into LanguagePriority list */ /* Above are all written-once properties of the variant. The * three fields below are changed during negotiation: /* Something to carry around the state of negotiation (and to keep * all of this thread-safe)... int accept_q;
/* 1 if an Accept item has a q= param */ /* the array pointers below are NULL if the corresponding accept * headers are not present int may_choose;
/* 1 if we may choose a variant for the client */ int use_rvsa;
/* 1 if we must use RVSA/1.0 negotiation algo */ /* A few functions to manipulate var_recs. * Cleaning out the fields... /* Initializing the relevant fields of a variant record from the * accept_info read out of its content-type, one way or another. /* Create a variant list validator in r using info from vlistr. */ /* Calculating the variant list validator is similar to * calculating an etag for the source of the variant list * information, so we use ap_make_etag(). Note that this * validator can be 'weak' in extreme case. /* ap_set_etag will later take r->vlist_validator into account * when creating the etag header /***************************************************************** * Parsing (lists of) media types and their parameters, as seen in * HTTPD header lines and elsewhere. * Get a single mime type entry --- one media type and parameters; * enter the values we recognize into the argument accept_rec * Note that this handles what I gather is the "old format", * without any compatibility kludges --- if the token after the * MIME type begins with a semicolon, we know we're looking at parms, * otherwise, we know we aren't. (So why all the pissing and moaning * in the CERN server code? I must be missing something). * you'll *get* case insensitive. /* KLUDGE!!! Default HTML to level 2.0 unless the browser * *explicitly* says something else. /* Look for 'var = value' --- and make sure the var is in lcase. */ continue;
/* No '='; just ignore it. */ *
cp++ =
'\0';
/* Delimit var */ *
end =
'\0';
/* strip ending quote or return */ && (
parm[
1] ==
'\0' || (
parm[
1] ==
's' &&
parm[
2] ==
'\0'))) {
/***************************************************************** * Dealing with header lines ... * Accept, Accept-Charset, Accept-Language and Accept-Encoding * are handled by do_header_line() - they all have the same * basic structure of a list of items of the format * name; q=N; charset=TEXT * where charset is only valid in Accept. /* Given the text of the Content-Languages: line from the var map file, * return an array containing the languages of this variant /***************************************************************** * Handling header lines from clients... /* calculate new->accept_q value */ /* This is possibly overkill for some servers, heck, we have * Make this configurable? /* First, default to no TCN, no Alternates, and the original Apache * negotiation algorithm with fiddles for broken browser configs. * To save network bandwidth, we do not configure to send an * Alternates header to the user agent by default. User * agents that want an Alternates header for agent-driven * negotiation will have to request it by sending an * appropriate Negotiate header. /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they * do not support transparent content negotiation, so for Lynx we * ignore the negotiate header when its contents are exactly "trans". * If future versions of Lynx ever need to say 'negotiate: trans', * they can send the equivalent 'negotiate: trans, trans' instead * to avoid triggering the workaround below. neg->
may_choose = 0;
/* An empty Negotiate would require 300 response */ /* The user agent supports transparent negotiation */ /* Send-alternates could be configurable, but note * that it must be 1 if we have 'vlist' in the /* we may use the RVSA/1.0 algorithm, configure for it */ else if (
tok[0] ==
'*') {
/* we may use any variant selection algorithm, configure * to use the Apache algorithm /* We disable header fiddles on the assumption that a * client sending Negotiate knows how to send correct * headers which don't need fiddling. "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d " "send_alternates=%d, may_choose=%d",
/* Sometimes clients will give us no Accept info at all; this routine sets * up the standard default for that case, and also arranges for us to be * willing to run a CGI script if we find one. (In fact, we set up to * dramatically prefer CGI scripts in cases where that's appropriate, * e.g., POST or when URI includes query args or extra path info). /***************************************************************** * Parsing type-map files, in Roy's meta/http format augmented with /* Reading RFC822-style header lines, ignoring #-comments and * handling continuations. /* Get a noncommented line */ /* If blank, just return it --- this ends information on this variant */ /* If non-blank, go looking for header lines, but note that we still * have to treat comments specially... /* We need to shortcut the rest of this block following the Body: * tag - we will not look for continutation after this line. /* Leading whitespace. POSSIBLE continuation line * Also, possibly blank --- if so, we ungetc() the final newline * so that we will pick up the blank line the next time 'round. /* Line beginning with something other than whitespace */ /* We are at the first character following a body:tag\n entry * Suck in the body, then backspace to the first char after the * closing tag entry. If we fail to read, find the tag or back * up then we have a hosed file, so give up already /* Skip all the trailing cruft after the end tag to the next line */ /* Give the caller back the actual body's offset and length */ /* Stripping out RFC822 comments */ /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */ /* Nope, it isn't correct. Fails to handle backslash escape as well. */ /* Getting to a header body from the header */ for ( ; *
cp && *
cp !=
':' ; ++
cp) {
"Syntax error in type map, no ':' in %s for header %s",
"Syntax error in type map --- no header body: %s for %s",
/* We are not using multiviews */ "Syntax error in type map, no end tag '%s'" "found in %s for Body: content.",
/* Sort function used by read_types_multi. */ /* First key is the source quality, sort in descending order. */ /* XXX: note that we currently implement no method of setting the * source quality for multiviews variants, so we are always comparing /* Second key is the variant name */ /***************************************************************** * Same as read_type_map, except we use a filtered directory listing /* XXX this should be more general, and quit using 'specials' */ /* Do we have a match? */ /* Ok, something's here. Maybe nothing useful. Remember that * we tried, if we completely fail, so we can reject the request! /* Yep. See if it's something which we have access to, and * which has a known type and encoding (as opposed to something * which we'll be slapping default_type on later). /* BLECH --- don't multi-resolve non-ordinary files */ /* If it has a handler, we'll pretend it's a CGI script, * since that's a good indication of the sort of thing it * mod_mime will _always_ provide us the base name in the * ap-mime-exception-list, if it processed anything. If * this list is empty, give up immediately, there was * nothing interesting. For example, looking at the files * readme.txt and readme.foo, we will throw away .foo if * it's an insignificant file (e.g. did not identify a * language, charset, encoding, content type or handler,) "ap-mime-exceptions-list");
/* Each unregonized bit better match our base name, in sequence. * because that would introduce too much CPU consumption. Better that * we don't attempt a many-to-many match here. /* Something you don't know is, something you don't know... * ###: be warned, the _default_ content type is already * picked up here! If we failed the subrequest, or don't * know what we are serving, then continue. /* If it's a map file, we use that instead of the map /* Have reasonable variant --- gather notes. */ /* We found some file names that matched. None could be served. * Rather than fall out to autoindex or some other mapper, this "Negotiation: discovered file(s) matching request: %s" " (None could be negotiated).",
/* Sort the variants into a canonical order. The negotiation * result sometimes depends on the order of the variants. By * sorting the variants into a canonical order, rather than using * the order in which readdir() happens to return them, we ensure * that the negotiation result will be consistent over filesystem /***************************************************************** * And now for the code you've been waiting for... actually * finding a match to the client's requirements. /* Matching MIME types ... the star/star and foo/star commenting conventions * are implemented here. (You know what I mean by star/star, but just * try mentioning those three characters in a C comment). Using strcmp() * is legit, because everything has already been smashed to lowercase. * Note also that if we get an exact match on the media type, we update * level_matched for use in level_cmp below... * We also give a value for mime_stars, which is used later. It should /* This code implements a piece of the tie-breaking algorithm between * variants of equal quality. This piece is the treatment of variants * of the same base media type, but different levels. What we want to * return is the variant at the highest level that the client explicitly * If all the variants available are at a higher level than that, or if * the client didn't say anything specific about this media type at all * and these variants just got in on a wildcard, we prefer the lowest * level, on grounds that that's the one that the client is least likely * (This is all motivated by treatment of levels in HTML --- we only * want to give level 3 to browsers that explicitly ask for it; browsers * that don't, including HTTP/0.9 browsers that only get the implicit * "Accept: * / *" [space added to avoid confusing cpp --- no, that * syntax doesn't really work] should get HTML2 if available). * (Note that this code only comes into play when we are choosing among * variants of equal quality, where the draft standard gives us a fair * bit of leeway about what to do. It ain't specified by the standard; * rather, it is a choice made by this server about what to do in cases * where the standard does not specify a unique course of action). /* Levels are only comparable between matching media types */ /* The result of the above if statements is that, if we get to * here, both variants have the same mime_type or both are /* Take highest level that matched, if either did match. */ /* Neither matched. Take lowest level, if there's a difference. */ /* Finding languages. The main entry point is set_language_quality() * which is called for each variant. It sets two elements in the * language_quality - the 'q' value of the 'best' matching language * from Accept-Language: header (HTTP/1.1) * lang_index - Non-negotiated language priority, using * position of language on the Accept-Language: * header, if present, else LanguagePriority * When we do the variant checking for best variant, we use language * quality first, and if a tie, language_index next (this only applies * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0 * algorithm, lang_index is never used. * set_language_quality() calls find_lang_index() and find_default_index() /* set_default_lang_quality() sets the quality we apply to variants * which have no language assigned to them. If none of the variants * have a language, we are not negotiating on language, so all are * acceptable, and we set the default q value to 1.0. However if * some of the variants have languages, we set this default to 0.0001. * The value of this default will be applied to all variants with * no explicit language -- which will have the effect of making them * acceptable, but only if no variants with an explicit language * are acceptable. The default q value set here is assigned to variants * with no language type in set_language_quality(). * Note that if using the RVSA/1.0 algorithm, we don't use this /* Set the language_quality value in the variant record. Also * assigns lang_index for ForceLanguagePriority. * To find the language_quality value, we look for the 'q' value * of the 'best' matching language on the Accept-Language * header. The 'best' match is the language on Accept-Language * header which matches the language of this variant either fully, * or as far as the prefix marker (-). If two or more languages * match, use the longest string from the Accept-Language header * When a variant has multiple languages, we find the 'best' * match for each variant language tag as above, then select the * one with the highest q value. Because both the accept-header * and variant can have multiple languages, we now have a hairy * loop-within-a-loop here. * If the variant has no language and we have no Accept-Language * items, leave the quality at 1.0 and return. * If the variant has no language, we use the default as set by * set_default_lang_quality() (1.0 if we are not negotiating on * language, 0.001 if we are). * Following the setting of the language quality, we drop through to * set the old 'lang_index'. This is set based on either the order * of the languages on the Accept-Language header, or the * order on the LanguagePriority directive. This is only used * in the negotiation if the language qualities tie. /* This variant has no content-language, so use the default * quality factor for variants with no content-language * (previously set by set_default_lang_quality()). * Leave the factor alone (it remains at 1.0) when we may not fiddle return;
/* no accept-language header */ /* Variant has one (or more) languages. Look for the best * match. We do this by going through each language on the * variant description looking for a match on the * Accept-Language header. The best match is the longest * matching language on the header. The final result is the * best q value from all the languages on the variant /* no accept-language header makes the variant indefinite */ else {
/* There is an accept-language with 0 or more items */ /* lang is the variant's language-tag, which is the one * we are allowed to use the prefix of in HTTP/1.1 /* now find the best (i.e. longest) matching * Accept-Language header language. We put the best match * for this tag in bestthistag. We cannot update the * overall best (based on q value) because the best match * for this tag is the longest language item on the accept * header, not necessarily the highest q. /* Find language. We match if either the variant * language tag exactly matches the language range * from the accept header, or a prefix of the variant * language tag up to a '-' character matches the * whole of the language range in the Accept-Language * header. Note that HTTP/1.x allows any number of * '-' characters in a tag or range, currently only * tags with zero or one '-' characters are defined * for general use (see rfc1766). * We only use language range in the Accept-Language * header the best match for the variant language tag * if it is longer than the previous best match. /* The next bit is a fiddle. Some browsers might * be configured to send more specific language * ranges than desirable. For example, an * Accept-Language of en-US should never match * variants with languages en or en-GB. But US * English speakers might pick en-US as their * language choice. So this fiddle checks if the * language range has a prefix, and if so, it * matches variants which match that prefix with a * priority of 0.001. So a request for en-US would * match variants of types en and en-GB, but at * much lower priority than matches of en-US * directly, or of any other language listed on * the Accept-Language header. Note that this * fiddle does not handle multi-level prefixes. /* Finished looking at Accept-Language headers, the best * (longest) match is in bestthistag, or NULL if no match /* See if the tag matches on a * in the Accept-Language * header. If so, record this fact for later use /* If one of the language tags of the variant matched on *, we * need to see if its q is better than that of any non-* match * on any other tag of the variant. If so the * match takes * precedence and the overall match is not definite. /* Handle the ForceDefaultLanguage overrides, based on the best match * to LanguagePriority order. The best match is the lowest index of * any LanguagePriority match. /* lang is the variant's language-tag, which is the one * we are allowed to use the prefix of in HTTP/1.1 /* If we wish to fallback or * we use our own LanguagePriority index. /* Determining the content length --- if the map didn't tell us, * we have to do a stat() and remember for next time. /* For a given variant, find the best matching Accept: header * and assign the Accept: header's quality value to the * mime_type_quality field of the variant, for later use in * determining the best matching variant. /* if no Accept: header, leave quality alone (will * remain at the default value of 1) * XXX: This if is currently never true because of the effect of * maybe_add_default_accepts(). * Go through each of the ranges on the Accept: header, * looking for the 'best' match with this variant's * content-type. We use the best match's quality * value (from the Accept: header) for this variant's * mime_type_quality field. * The best match is determined like this: * type/type is better than type/ * is better than * / * * if match is type/type, use the level mime param if available continue;
/* didn't match the content type at all */ /* did match - see if there were less or more stars than continue;
/* more stars => not as good a match */ /* If we are allowed to mess with the q-values * and have no explicit q= parameters in the accept header, * make wildcards very low, so we have a low chance * of ending up with them if there's something better. /* For a given variant, find the 'q' value of the charset given * on the Accept-Charset line. If no charsets are listed, /* if no Accept-Charset: header, leave quality alone (will * remain at the default value of 1) /* Charset of variant not known */ /* if not a text / * type, leave quality alone */ /* Don't go guessing if we are in strict header mode, * e.g. when running the rvsa, as any guess won't be reflected * in the variant list or content-location headers. charset =
"iso-8859-1";
/* The default charset for HTTP text types */ * Go through each of the items on the Accept-Charset header, * looking for a match with this variant's charset. If none * match, charset is unacceptable, so set quality to 0. /* If this variant is in charset iso-8859-1, the default is 1.0 */ /* is_identity_encoding is included for back-compat, but does anyone * use 7bit, 8bin or binary in their var files?? * set_encoding_quality determines whether the encoding for a particular * variant is acceptable for the user-agent. * The rules for encoding are that if the user-agent does not supply * any Accept-Encoding header, then all encodings are allowed but a * variant with no encoding should be preferred. * If there is an empty Accept-Encoding header, then no encodings are * acceptable. If there is a non-empty Accept-Encoding header, then * any of the listed encodings are acceptable, as well as no encoding * unless the "identity" encoding is specifically excluded. /* We had no Accept-Encoding header, assume that all * encodings are acceptable with a low quality, * but we prefer no encoding if available. /* Go through each of the encodings on the Accept-Encoding: header, * looking for a match with our encoding. x- prefixes are ignored. if (
enc[0] ==
'x' &&
enc[
1] ==
'-') {
/* Encoding not found on Accept-Encoding: header, so it is * _not_ acceptable unless it is the identity (no encoding) /************************************************************* * Possible results of the variant selection algorithm /* Below is the 'best_match' function. It returns an int, which has * one of the two values alg_choice or alg_list, which give the result * of the variant selection algorithm. alg_list means that no best * variant was found by the algorithm, alg_choice means that a best * variant was found and should be returned. The list/choice * terminology comes from TCN (rfc2295), but is used in a more generic * way here. The best variant is returned in *pbest. best_match has * two possible algorithms for determining the best variant: the * RVSA/1.0 algorithm (from RFC2296), and the standard Apache * algorithm. These are split out into separate functions * (is_variant_better_rvsa() and is_variant_better()). Selection of * one is through the neg->use_rvsa flag. * The call to best_match also creates full information, including * language, charset, etc quality for _every_ variant. This is needed * for generating a correct Vary header, and can be used for the * Alternates header, the human-readable list responses and 406 errors. /* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm * v1.0) from rfc2296. This is the algorithm that goes together with * transparent content negotiation (TCN). /* TCN does not cover negotiation on content-encoding. For now, * we ignore the encoding unless it was explicitly excluded. /* RFC 2296 calls for the result to be rounded to 5 decimal places, * but we don't do that because it serves no useful purpose other * than to ensure that a remote algorithm operates on the same * precision as ours. That is silly, since what we obviously want * is for the algorithm to operate on the best available precision * regardless of who runs it. Since the above calculation may * result in significant variance at 1e-12, rounding would be bogus. "Variant: file=%s type=%s lang=%s sourceq=%1.3f " "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f " /* If the best variant's encoding is of lesser quality than * this variant, then we prefer this variant /* Negotiation algorithm as used by previous versions of Apache /* For non-transparent negotiation, server can choose how * to handle the negotiation. We'll use the following in * order: content-type, language, content-type level, charset, * content encoding, content length. * For each check, we have three possible outcomes: * This variant is worse than current best: return 0 * This variant is better than the current best: * assign this variant's q to *p_bestq, and return 1 * This variant is just as desirable as the current best: * drop through to the next test. * This code is written in this long-winded way to allow future * customisation, either by the addition of additional * checks, or to allow the order of the checks to be determined * by configuration options (e.g. we might prefer to check * language quality _before_ content type). /* First though, eliminate this variant if it is not * acceptable by type, charset, encoding or language. "Variant: file=%s type=%s lang=%s sourceq=%1.3f " "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
return 0;
/* don't consider unacceptables */ if (q ==
0.0 || q <
bestq) {
/* if language qualities were equal, try the LanguagePriority stuff */ /* content-type level (sometimes used with text/html, though we * support it on other types too) /* If the best variant's charset is ISO-8859-1 and this variant has * the same charset quality, then we prefer this variant /* Prefer the highest value for encoding_quality. /* content length if all else equal */ /* ok, to get here means every thing turned out equal, except * we have a shorter content length, so use this variant * Find the 'best' variant /* Find all the relevant 'quality' values from the * Accept... headers, and store in the variant. This also * prepares for sending an Alternates header etc so we need to * do it even if we do not actually plan to find a best /* Only do variant selection if we may actually choose a /* Now find out if this variant is better than the current * best, either using the RVSA/1.0 algorithm, or Apache's * internal server-driven algorithm. Presumably other * server-driven algorithms are possible, and could be /* We now either have a best variant, or no best variant */ /* calculate result for RVSA/1.0 algorithm: * only a choice response if the best variant has q>0 /* calculate result for Apache negotiation algorithm */ /* Returning a choice response with a non-neighboring variant is a * protocol security error in TCN (see rfc2295). We do *not* * verify here that the variant and URI are neighbors, even though * we may return alg_choice. We depend on the environment (the * caller) to only declare the resource transparently negotiable if * all variants are neighbors. /* Sets response headers for a negotiated response. * neg->is_transparent determines whether a transparently negotiated * response or a plain `server driven negotiation' response is * created. Applicable headers are Alternates, Vary, and TCN. * The Vary header we create is sometimes longer than is required for * the correct caching of negotiated results by HTTP/1.1 caches. For * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if * the Accept: header assigns a 0 quality to .ps, then the results of * the two server-side negotiation algorithms we currently implement * will never depend on Accept-Language so we could return `Vary: * negotiate, accept' instead of the longer 'Vary: negotiate, accept, * accept-language' which the code below will return. A routine for * computing the exact minimal Vary header would be a huge pain to code * and maintain though, especially because we need to take all possible * twiddles in the server-side negotiation algorithms into account. /* In order to avoid O(n^2) memory copies in building Alternates, * we preallocate a apr_table_t with the maximum substrings possible, * fill it with the variant list, and then concatenate the entire array. * Note that if you change the number of substrings pushed, you also * need to change the calculation of max_vlist_array above. /* Put headers into err_headers_out, since send_http_header() * outputs both headers_out and err_headers_out. /* Calculate Vary by looking for any difference between variants */ /* Generate the string components for this Alternates entry */ /* Strip trailing zeros (saves those valuable network bytes) */ /* Strictly speaking, this is non-standard, but so is TCN */ /* Note that the Alternates specification (in rfc2295) does * not require that we include {length x}, so we could omit it * if determining the length is too expensive. We currently * always include it though. 22 bytes is enough for 2^64. * If the variant is a CGI script, find_content_length would * return the length of the script, not the output it * produces, so we check for the presence of a handler and if * there is one we don't add a length. * XXX: TODO: This check does not detect a CGI script if we * get the variant from a type map. This needs to be fixed * (without breaking things if the type map specifies a * content-length, which currently leads to the correct result). /********************************************************************** * Return an HTML list of variants. This is output as part of the * choice response or 406 status body. /* In order to avoid O(n^2) memory copies in building the list, * we preallocate a apr_table_t with the maximum substrings possible, * fill it with the variant list, and then concatenate the entire array. /* The format isn't very neat, and it would be nice to make * the tags human readable (eg replace 'language en' with 'English'). * Note that if you change the number of substrings pushed, you also * need to change the calculation of max_vlist_array above. /* Called if we got a "Choice" response from the variant selection algorithm. * It checks the result of the chosen variant to see if it * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES). * Otherwise, add the appropriate headers to the current response. /* The variant selection algorithm told us to return a "Choice" * response. This is the normal variant response, with * some extra headers. First, ensure that the chosen * variant did or will not itself engage in transparent negotiation. * If not, set the appropriate headers, and fall through to * the normal variant handling /* This catches the error that a transparent type map selects a * transparent multiviews resource as the best variant. * XXX: We do not signal an error if a transparent type map * selects a _non_transparent multiviews resource as the best * variant, because we can generate a legal negotiation response * in this case. In this case, the vlist_validator of the * nontransparent subrequest will be lost however. This could * lead to cases in which a change in the set of variants or the * negotiation algorithm of the nontransparent resource is never * propagated up to a HTTP/1.1 cache which interprets Vary. To be * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES * for this type of recursive negotiation too. /* This catches the error that a transparent type map recursively * selects, as the best variant, another type map which itself * causes transparent negotiation to be done. * XXX: Actually, we catch this error by catching all cases of * type map recursion. There are some borderline recursive type * map arrangements which would not produce transparent * negotiation protocol errors or lack of cache propagation * problems, but such arrangements are very hard to detect at this * point in the control flow, so we do not bother to single them * Recursive type maps imply a recursive arrangement of negotiated * resources which is visible to outside clients, and this is not * supported by the transparent negotiation caching protocols, so * if we are to have generic support for recursive type maps, we * have to create some configuration setting which makes all type * maps non-transparent when recursion is enabled. Also, if we * want recursive type map support which ensures propagation of * type map changes into HTTP/1.1 caches that handle Vary, we * would have to extend the current mechanism for generating * variant list validators. /* This adds an appropriate Variant-Vary header if the subrequest * is a multiviews resource. * XXX: TODO: Note that this does _not_ handle any Vary header * returned by a CGI if sub_req is a CGI script, because we don't * see that Vary header yet at this point in the control flow. * This won't cause any cache consistency problems _unless_ the * CGI script also returns a Cache-Control header marking the * response as cachable. This needs to be fixed, also there are * problems if a CGI returns an Etag header which also need to be /* Move the subreq Vary header into the main request to * prevent having two Vary headers in the response, which * would be legal but strange. /* Still to do by caller: add Expires */ /**************************************************************** int alg_result;
/* result of variant selection algorithm */ /* Decide if resource is transparently negotiable */ /* GET or HEAD? (HEAD has same method number as GET) */ /* maybe this should be configurable, see also the comment * about recursive type maps in setup_choice_response() /* We can't be transparent if we are a map file in the middle /* We can't be transparent, because of internal * assumptions in best_match(), if there is a * non-neighboring variant. We can have a non-neighboring * variant when processing a type map. else {
/* configure negotiation on non-transparent resource */ * alg_choice: a best variant is chosen * alg_list: no best variant is chosen /* send a list response or HTTP_NOT_ACCEPTABLE error response */ /* XXX todo: expires? cachability? */ /* Some HTTP/1.0 clients are known to choke when they get * a 300 (multiple choices) response without a Location * header. However the 300 code response we are are about * to generate will only reach 1.0 clients which support * transparent negotiation, and they should be OK. The * response should never reach older 1.0 clients, even if * we have CacheNegotiatedDocs enabled, because no 1.0 * proxy cache (we know of) will cache and return 300 * responses (they certainly won't if they conform to the * HTTP/1.0 specification). "no acceptable variant: %s", r->
filename);
/* Variant selection chose a variant */ /* XXX todo: merge the two cases in the if statement below */ return res;
/* return if error */ /* Make sure caching works - Vary should handle HTTP/1.1, but for * HTTP/1.0, we can't allow caching at all. /* XXX: Note that we only set r->no_cache to 1, which causes * Expires: <now> to be added, when responding to a HTTP/1.0 * client. If we return the response to a 1.1 client, we do not * add Expires <now>, because doing so would degrade 1.1 cache * performance by preventing re-use of the response without prior * revalidation. On the other hand, if the 1.1 client is a proxy * which was itself contacted by a 1.0 client, or a proxy cache * which can be contacted later by 1.0 clients, then we currently * rely on this 1.1 proxy to add the Expires: <now> when it * XXX: TODO: Find out if the 1.1 spec requires proxies and * tunnels to add Expires: <now> when forwarding the response to * 1.0 clients. I (kh) recall it is rather vague on this point. * Testing actual 1.1 proxy implementations would also be nice. If * Expires: <now> is not added by proxies then we need to always * include Expires: <now> ourselves to ensure correct caching, but * this would degrade HTTP/1.1 cache efficiency unless we also add * Cache-Control: max-age=N, which we currently don't. * Roy: No, we are not going to screw over HTTP future just to * ensure that people who can't be bothered to upgrade their * clients will always receive perfect server-side negotiation. * Hell, those clients are sending bogus accept headers anyway. * automated kluge, on purpose. /*if (r->method_number == M_OPTIONS) { * return ap_send_http_options(r); /* ### These may be implemented by adding some 'extra' info * of the file offset onto the etag * ap_update_mtime(r, r->finfo.mtime); * ap_set_last_modified(r); /* XXX This looks entirely bogus. Why can't negotiated content * have additional path_info? Just because I choose a given * script based on content type doesn't mean I don't want it * to run the same set of actions (think three languages of * viewcvs.cgi.xx, all serving the same repository.) /* free all allocated memory from subrequests */ /* We got this out of a map file, so we don't actually have * a sub_req structure yet. Get one now. /* now do a "fast redirect" ... promotes the sub_req into the main req */ /* give no advise for time on this subrequest. Perhaps we * should tally the last mtime amoung all variants, and date * the most recent, but that could confuse the proxies. /* clean up all but our favorite variant, since that sub_req * is now merged into the main request! /********************************************************************** * There is a problem with content-encoding, as some clients send and * expect an x- token (e.g. x-gzip) while others expect the plain token * (i.e. gzip). To try and deal with this as best as possible we do * the following: if the client sent an Accept-Encoding header and it * contains a plain token corresponding to the content encoding of the * response, then set content encoding using the plain token. Else if * the A-E header contains the x- token use the x- token in the C-E * header. Else don't do anything. * Note that if no A-E header was sent, or it does not contain a token * compatible with the final content encoding, then the token in the * C-E header will be whatever was specified in the AddEncoding if (
enc[0] ==
'x' &&
enc[
1] ==
'-') {
NULL,
/* server config */ NULL,
/* merge server config */