mod_negotiation.c revision acbdd2dae7bde98990ed3f8ef29e1bf9350bdb9e
842ae4bd224140319ae7feec1872b93dfd491143fielding/* ====================================================================
842ae4bd224140319ae7feec1872b93dfd491143fielding * The Apache Software License, Version 1.1
842ae4bd224140319ae7feec1872b93dfd491143fielding * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
842ae4bd224140319ae7feec1872b93dfd491143fielding * reserved.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Redistribution and use in source and binary forms, with or without
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * modification, are permitted provided that the following conditions
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * 1. Redistributions of source code must retain the above copyright
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * notice, this list of conditions and the following disclaimer.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * 2. Redistributions in binary form must reproduce the above copyright
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * notice, this list of conditions and the following disclaimer in
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * the documentation and/or other materials provided with the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * distribution.
e8f95a682820a599fe41b22977010636be5c2717jim * 3. The end-user documentation included with the redistribution,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * if any, must include the following acknowledgment:
e8f95a682820a599fe41b22977010636be5c2717jim * "This product includes software developed by the
1747d30b98aa1bdbc43994c02cd46ab4cb9319e4fielding * Apache Software Foundation (http://www.apache.org/)."
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Alternately, this acknowledgment may appear in the software itself,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * if and wherever such third-party acknowledgments normally appear.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * 4. The names "Apache" and "Apache Software Foundation" must
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * not be used to endorse or promote products derived from this
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * software without prior written permission. For written
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * permission, please contact apache@apache.org.
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick * 5. Products derived from this software may not be called "Apache",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * nor may "Apache" appear in their name, without prior written
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * permission of the Apache Software Foundation.
5c0419d51818eb02045cf923a9fe456127a44c60wrowe * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
5c0419d51818eb02045cf923a9fe456127a44c60wrowe * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * SUCH DAMAGE.
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * ====================================================================
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * This software consists of voluntary contributions made by many
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * individuals on behalf of the Apache Software Foundation. For more
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * information on the Apache Software Foundation, please see
0f60998368b493f90120180a93fc2e1e74490872covener * Portions of this software are based upon public domain software
0f60998368b493f90120180a93fc2e1e74490872covener * originally written at the National Center for Supercomputing Applications,
0f60998368b493f90120180a93fc2e1e74490872covener * University of Illinois, Urbana-Champaign.
0f60998368b493f90120180a93fc2e1e74490872covener * mod_negotiation.c: keeps track of MIME types the client is willing to
0f60998368b493f90120180a93fc2e1e74490872covener * accept, and contains code to handle type arbitration.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define MAP_FILE_MAGIC_TYPE "application/x-type-map"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Commands --- configuring document caching on a per (virtual?)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * server basis...
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholestypedef struct {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* forcelangpriority flags
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define FLP_PREFER 2 /* Use language_priority rather than MC */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define FLP_FALLBACK 4 /* Use language_priority rather than NA */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void *create_neg_dir_config(apr_pool_t *p, char *dummy)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config));
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin /* give priority to the config in the subdirectory */
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF)
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowestatic const char *set_language_priority(cmd_parms *cmd, void *n_,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes const char *lang)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes const char **langp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *));
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener langp = (const char **) apr_array_push(n->language_priority);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w)
fa123db15501821e36e513afa78e839775ad2800covener return "Cannot combine ForceLanguagePriority options with None";
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener return "Cannot combine ForceLanguagePriority options None and Prefer";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return "Cannot combine ForceLanguagePriority options None and Fallback";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ", w, NULL);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_set_module_config(cmd->server->module_config, &negotiation_module,
cceddc0b6c0fdaed0c73abda39975bb1d388243acovener return (ap_get_module_config(s->module_config, &negotiation_module) != NULL);
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF,
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener "Either 'on' or 'off' (default)"),
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL, OR_FILEINFO,
60215f303c7e1ce8b6d272acb5bfa5b3d99dfd34covener "space-delimited list of MIME language abbreviations"),
60215f303c7e1ce8b6d272acb5bfa5b3d99dfd34covener AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL, OR_FILEINFO,
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener "Force LanguagePriority elections, either None, or Fallback and/or Prefer"),
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * Record of available info on a media type specified by the client
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * (we also use 'em for encodings and languages)
6683642c1e0032eeeed5f99e8c14880692ef84c5sftypedef struct accept_rec {
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * Record of available info on a particular variant
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * Note that a few of these fields are updated by the actual negotiation
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * code. These are:
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * level_matched --- initialized to zero. Set to the value of level
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * if the client actually accepts this media type at that
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * level (and *not* if it got in on a wildcard). See level_cmp
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * mime_stars -- initialized to zero. Set to the number of stars
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * present in the best matching Accept header element.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * definite -- initialized to 1. Set to 0 if there is a match which
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * makes the variant non-definite according to the rules
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * in rfc2296.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovenertypedef struct var_rec {
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener request_rec *sub_req; /* May be NULL (is, for map files) */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener const char *file_name; /* Set to 'this' (for map file body content) */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener apr_off_t body; /* Only for map file body content */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener apr_array_header_t *content_languages; /* list of languages for this variant */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener /* The next five items give the quality values for the dimensions
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * of negotiation for this variant. They are obtained from the
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * appropriate header lines, except for source_quality, which
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * is obtained from the variant itself (the 'qs' parameter value
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * from the variant's mime-type). Apart from source_quality,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * these values are set when we find the quality for each variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * (see best_match()). source_quality is set from the 'qs' parameter
60215f303c7e1ce8b6d272acb5bfa5b3d99dfd34covener * of the variant description or mime type: see set_mime_fields().
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes float lang_quality; /* quality of this variant's language */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float source_quality; /* source quality for this variant */
465bb68501690d7a47bfd2a6129580047d76d8f1rederpj /* Now some special values */
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Above are all written-once properties of the variant. The
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * three fields below are changed during negotiation:
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes/* Something to carry around the state of negotiation (and to keep
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * all of this thread-safe)...
513b324e774c559b579896df131fd7c8471ed529rederpjtypedef struct {
513b324e774c559b579896df131fd7c8471ed529rederpj int accept_q; /* 1 if an Accept item has a q= param */
513b324e774c559b579896df131fd7c8471ed529rederpj float default_lang_quality; /* fiddle lang q for variants with no lang */
513b324e774c559b579896df131fd7c8471ed529rederpj /* the array pointers below are NULL if the corresponding accept
513b324e774c559b579896df131fd7c8471ed529rederpj * headers are not present
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_array_header_t *accept_charsets; /* accept_recs */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_array_header_t *accept_langs; /* accept_recs */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_array_header_t *avail_vars; /* available variants */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int count_multiviews_variants; /* number of variants found on disk */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int is_transparent; /* 1 if this resource is trans. negotiable */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int ua_supports_trans; /* 1 if ua supports trans negotiation */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int send_alternates; /* 1 if we want to send an Alternates header */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int may_choose; /* 1 if we may choose a variant for the client */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */
fa123db15501821e36e513afa78e839775ad2800covener/* A few functions to manipulate var_recs.
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * Cleaning out the fields...
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes/* Initializing the relevant fields of a variant record from the
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * accept_info read out of its content-type, one way or another.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void set_mime_fields(var_rec *var, accept_rec *mime_info)
9ad7b260be233be7d7b5576979825cac72e15498rederpj var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
707f6d077f73cc948deead8df5b40ea42c1eaa78covener/* Create a variant list validator in r using info from vlistr. */
707f6d077f73cc948deead8df5b40ea42c1eaa78covenerstatic void set_vlist_validator(request_rec *r, request_rec *vlistr)
707f6d077f73cc948deead8df5b40ea42c1eaa78covener /* Calculating the variant list validator is similar to
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * calculating an etag for the source of the variant list
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * information, so we use ap_make_etag(). Note that this
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * validator can be 'weak' in extreme case.
707f6d077f73cc948deead8df5b40ea42c1eaa78covener /* ap_set_etag will later take r->vlist_validator into account
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * when creating the etag header
707f6d077f73cc948deead8df5b40ea42c1eaa78covener/*****************************************************************
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * Parsing (lists of) media types and their parameters, as seen in
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * HTTPD header lines and elsewhere.
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * Get a single mime type entry --- one media type and parameters;
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * enter the values we recognize into the argument accept_rec
707f6d077f73cc948deead8df5b40ea42c1eaa78covenerstatic const char *get_entry(apr_pool_t *p, accept_rec *result,
7add8f7fb048534390571801b7794f71cd9e127abnicholes * Note that this handles what I gather is the "old format",
7add8f7fb048534390571801b7794f71cd9e127abnicholes * without any compatibility kludges --- if the token after the
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * MIME type begins with a semicolon, we know we're looking at parms,
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * otherwise, we know we aren't. (So why all the pissing and moaning
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * in the CERN server code? I must be missing something).
7add8f7fb048534390571801b7794f71cd9e127abnicholes ap_str_tolower(result->name); /* You want case insensitive,
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * you'll *get* case insensitive.
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes /* KLUDGE!!! Default HTML to level 2.0 unless the browser
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * *explicitly* says something else.
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
7add8f7fb048534390571801b7794f71cd9e127abnicholes else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes /* Parameters ... */
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes /* Look for 'var = value' --- and make sure the var is in lcase. */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf continue; /* No '='; just ignore it. */
7dbf29be626018bc389ef94c1846aeac4b72633bsf && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf/*****************************************************************
7dbf29be626018bc389ef94c1846aeac4b72633bsf * Dealing with header lines ...
7dbf29be626018bc389ef94c1846aeac4b72633bsf * Accept, Accept-Charset, Accept-Language and Accept-Encoding
7dbf29be626018bc389ef94c1846aeac4b72633bsf * are handled by do_header_line() - they all have the same
7dbf29be626018bc389ef94c1846aeac4b72633bsf * basic structure of a list of items of the format
7dbf29be626018bc389ef94c1846aeac4b72633bsf * name; q=N; charset=TEXT
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * where charset is only valid in Accept.
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholesstatic apr_array_header_t *do_header_line(apr_pool_t *p, const char *accept_line)
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes accept_rec *new = (accept_rec *) apr_array_push(accept_recs);
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes/* Given the text of the Content-Languages: line from the var map file,
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * return an array containing the languages of this variant
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholesstatic apr_array_header_t *do_languages_line(apr_pool_t *p, const char **lang_line)
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *));
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf/*****************************************************************
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * Handling header lines from clients...
e8f95a682820a599fe41b22977010636be5c2717jimstatic negotiation_state *parse_accept_headers(request_rec *r)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state));
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config,
9c63a05713cb83a44a1590b4af33edeebf39f118sf new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
9c63a05713cb83a44a1590b4af33edeebf39f118sf new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
9c63a05713cb83a44a1590b4af33edeebf39f118sf /* calculate new->accept_q value */
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* This is possibly overkill for some servers, heck, we have
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * only 33 index.html variants in docs/docroot (today).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Make this configurable?
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void parse_negotiate_header(request_rec *r, negotiation_state *neg)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* First, default to no TCN, no Alternates, and the original Apache
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * negotiation algorithm with fiddles for broken browser configs.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * To save network bandwidth, we do not configure to send an
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Alternates header to the user agent by default. User
e8f95a682820a599fe41b22977010636be5c2717jim * agents that want an Alternates header for agent-driven
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * negotiation will have to request it by sending an
8a03cd420b800a2428f49f4617293de9b2387b20jorton * appropriate Negotiate header.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * do not support transparent content negotiation, so for Lynx we
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * ignore the negotiate header when its contents are exactly "trans".
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * If future versions of Lynx ever need to say 'negotiate: trans',
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * they can send the equivalent 'negotiate: trans, trans' instead
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * to avoid triggering the workaround below.
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covener const char *ua = apr_table_get(r->headers_in, "User-Agent");
54d22ed1c429b903b029bbd62621f11a9e286137minfrin neg->may_choose = 0; /* An empty Negotiate would require 300 response */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* The user agent supports transparent negotiation */
e8f95a682820a599fe41b22977010636be5c2717jim /* Send-alternates could be configurable, but note
e8f95a682820a599fe41b22977010636be5c2717jim * that it must be 1 if we have 'vlist' in the
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * negotiate header.
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe /* we may use the RVSA/1.0 algorithm, configure for it */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* we may use any variant selection algorithm, configure
e8f95a682820a599fe41b22977010636be5c2717jim * to use the Apache algorithm
9ad7b260be233be7d7b5576979825cac72e15498rederpj /* We disable header fiddles on the assumption that a
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * client sending Negotiate knows how to send correct
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * headers which don't need fiddling.
560fd0658902ab57754616c172d8953e69fc4722bnicholes ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "send_alternates=%d, may_choose=%d",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
e8f95a682820a599fe41b22977010636be5c2717jim/* Sometimes clients will give us no Accept info at all; this routine sets
e8f95a682820a599fe41b22977010636be5c2717jim * up the standard default for that case, and also arranges for us to be
e8f95a682820a599fe41b22977010636be5c2717jim * willing to run a CGI script if we find one. (In fact, we set up to
e8f95a682820a599fe41b22977010636be5c2717jim * dramatically prefer CGI scripts in cases where that's appropriate,
e8f95a682820a599fe41b22977010636be5c2717jim * e.g., POST or when URI includes query args or extra path info).
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowestatic void maybe_add_default_accepts(negotiation_state *neg,
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec));
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener new_accept = (accept_rec *) apr_array_push(neg->accepts);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept = (accept_rec *) apr_array_push(neg->accepts);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/*****************************************************************
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Parsing type-map files, in Roy's meta/http format augmented with
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * #-comments.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Reading RFC822-style header lines, ignoring #-comments and
e8f95a682820a599fe41b22977010636be5c2717jim * handling continuations.
fa123db15501821e36e513afa78e839775ad2800covenerstatic enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Get a noncommented line */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If blank, just return it --- this ends information on this variant */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) {
0e05808dc59a321566303084c84b9826a4353cefrederpj /* If non-blank, go looking for header lines, but note that we still
ebe5305f8b22507374358f32b74d12fb50c05a25covener * have to treat comments specially...
ebe5305f8b22507374358f32b74d12fb50c05a25covener /* We need to shortcut the rest of this block following the Body:
ebe5305f8b22507374358f32b74d12fb50c05a25covener * tag - we will not look for continutation after this line.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener if (c == '#') {
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener /* Comment line */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener while (apr_file_getc(&c, map) != APR_EOF && c != '\n') {
0e05808dc59a321566303084c84b9826a4353cefrederpj else if (apr_isspace(c)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Leading whitespace. POSSIBLE continuation line
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * Also, possibly blank --- if so, we ungetc() the final newline
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * so that we will pick up the blank line the next time 'round.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (c == '\n') {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Continuation */
560fd0658902ab57754616c172d8953e69fc4722bnicholes while (cp < buf_end - 2 && (apr_file_getc(&c, map)) != APR_EOF && c != '\n') {
fa123db15501821e36e513afa78e839775ad2800covener /* Line beginning with something other than whitespace */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* We are at the first character following a body:tag\n entry
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Suck in the body, then backspace to the first char after the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * closing tag entry. If we fail to read, find the tag or back
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * up then we have a hosed file, so give up already
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* XXX next line can go beyond allocated storage and segfault,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * or worse yet go beyond data read but not beyond allocated
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * storage and think it found the tag
e8f95a682820a599fe41b22977010636be5c2717jim /* Skip all the trailing cruft after the end tag to the next line */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
e8f95a682820a599fe41b22977010636be5c2717jim /* Give the caller back the actual body's file offset and length */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Stripping out RFC822 comments */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Nope, it isn't correct. Fails to handle backslash escape as well. */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Getting to a header body from the header */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic char *lcase_header_name_return_body(char *header, request_rec *r)
43c3e6a4b559b76b750c245ee95e2782c15b4296jim ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Syntax error in type map, no ':' in %s for header %s",
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener "Syntax error in type map --- no header body: %s for %s",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic int read_type_map(apr_file_t **map, negotiation_state *neg, request_rec *rr)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* We are not using multiviews */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes hstate = get_header_line(buffer, MAX_STRING_LEN, *map);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *body1 = lcase_header_name_return_body(buffer, neg->r);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes const char *body;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info.file_name = ap_get_token(neg->pool, &body, 0);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!strncmp(buffer, "content-length:", 15)) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem else if (!strncmp(buffer, "content-language:", 17)) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem mime_info.content_languages = do_languages_line(neg->pool,
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Syntax error in type map, no end tag '%s'"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "found in %s for Body: content.",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Sort function used by read_types_multi. */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* First key is the source quality, sort in descending order. */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* XXX: note that we currently implement no method of setting the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * source quality for multiviews variants, so we are always comparing
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * 1.0 to 1.0 for now
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Second key is the variant name */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/*****************************************************************
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Same as read_type_map, except we use a filtered directory listing
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * as the map...
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* XXX this should be more general, and quit using 'specials' */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((status = apr_dir_open(&dirp, neg->dir_name, neg->pool)) != APR_SUCCESS) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "cannot read directory for multi: %s", neg->dir_name);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Do we have a match? */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Don't negotiate directories and other unusual files
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * Really shouldn't see anything but DIR/LNK/REG here,
9c63a05713cb83a44a1590b4af33edeebf39f118sf * and we aught to discover if the LNK was interesting.
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * Of course, this only helps platforms that capture the
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * the filetype in apr_dir_read(), which most can once
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * they are optimized with some magic [it's known to the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * dirent, not associated to the inode, on most FS's.]
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR))
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Ok, something's here. Maybe nothing useful. Remember that
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we tried, if we completely fail, so we can reject the request!
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* See if it's something which we have access to, and which
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * has a known type and encoding (as opposed to something
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * which we'll be slapping default_type on later).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes sub_req = ap_sub_req_lookup_dirent(&dirent, r, AP_SUBREQ_MERGE_ARGS, NULL);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Double check, we still don't multi-resolve non-ordinary files
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If it has a handler, we'll pretend it's a CGI script,
8869662bb1a4078297020e94ae5e928626d877c6rederpj * since that's a good indication of the sort of thing it
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * might be doing.
8869662bb1a4078297020e94ae5e928626d877c6rederpj * mod_mime will _always_ provide us the base name in the
8869662bb1a4078297020e94ae5e928626d877c6rederpj * ap-mime-exception-list, if it processed anything. If
8869662bb1a4078297020e94ae5e928626d877c6rederpj * this list is empty, give up immediately, there was
8869662bb1a4078297020e94ae5e928626d877c6rederpj * nothing interesting. For example, looking at the files
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * readme.txt and readme.foo, we will throw away .foo if
8869662bb1a4078297020e94ae5e928626d877c6rederpj * it's an insignificant file (e.g. did not identify a
8869662bb1a4078297020e94ae5e928626d877c6rederpj * language, charset, encoding, content type or handler,)
8869662bb1a4078297020e94ae5e928626d877c6rederpj "ap-mime-exceptions-list");
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Each unregonized bit better match our base name, in sequence.
8869662bb1a4078297020e94ae5e928626d877c6rederpj * A test of index.html.foo will match index.foo or index.html.foo,
8869662bb1a4078297020e94ae5e928626d877c6rederpj * but it will never transpose the segments and allow index.foo.html
8869662bb1a4078297020e94ae5e928626d877c6rederpj * because that would introduce too much CPU consumption. Better that
8869662bb1a4078297020e94ae5e928626d877c6rederpj * we don't attempt a many-to-many match here.
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Something you don't know is, something you don't know...
8869662bb1a4078297020e94ae5e928626d877c6rederpj * ###: be warned, the _default_ content type is already
8869662bb1a4078297020e94ae5e928626d877c6rederpj * picked up here! If we failed the subrequest, or don't
8869662bb1a4078297020e94ae5e928626d877c6rederpj * know what we are serving, then continue.
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* If it's a map file, we use that instead of the map
8869662bb1a4078297020e94ae5e928626d877c6rederpj * we're building...
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Have reasonable variant --- gather notes. */
8869662bb1a4078297020e94ae5e928626d877c6rederpj mime_info.file_name = apr_pstrdup(neg->pool, dirent.name);
8869662bb1a4078297020e94ae5e928626d877c6rederpj mime_info.content_encoding = sub_req->content_encoding;
8869662bb1a4078297020e94ae5e928626d877c6rederpj mime_info.content_languages = sub_req->content_languages;
8869662bb1a4078297020e94ae5e928626d877c6rederpj get_entry(neg->pool, &accept_info, sub_req->content_type);
8869662bb1a4078297020e94ae5e928626d877c6rederpj memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* We found some file names that matched. None could be served.
8869662bb1a4078297020e94ae5e928626d877c6rederpj * Rather than fall out to autoindex or some other mapper, this
8869662bb1a4078297020e94ae5e928626d877c6rederpj * request must die.
8869662bb1a4078297020e94ae5e928626d877c6rederpj ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
8869662bb1a4078297020e94ae5e928626d877c6rederpj "Negotiation: discovered file(s) matching request: %s"
8869662bb1a4078297020e94ae5e928626d877c6rederpj " (None could be negotiated).",
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Sort the variants into a canonical order. The negotiation
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * result sometimes depends on the order of the variants. By
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * sorting the variants into a canonical order, rather than using
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * the order in which readdir() happens to return them, we ensure
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * that the negotiation result will be consistent over filesystem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * backup/restores and over all mirror sites.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem/*****************************************************************
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * And now for the code you've been waiting for... actually
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * finding a match to the client's requirements.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem/* Matching MIME types ... the star/star and foo/star commenting conventions
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * are implemented here. (You know what I mean by star/star, but just
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * try mentioning those three characters in a C comment). Using strcmp()
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * is legit, because everything has already been smashed to lowercase.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * Note also that if we get an exact match on the media type, we update
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * level_matched for use in level_cmp below...
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * We also give a value for mime_stars, which is used later. It should
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * be 1 for star/star, 2 for type/star and 3 for type/subtype.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluemstatic int mime_match(accept_rec *accept_r, var_rec *avail)
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (accept_type[0] == '*') { /* Anything matches star/star */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem/* This code implements a piece of the tie-breaking algorithm between
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * variants of equal quality. This piece is the treatment of variants
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * of the same base media type, but different levels. What we want to
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * return is the variant at the highest level that the client explicitly
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * claimed to accept.
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj * If all the variants available are at a higher level than that, or if
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * the client didn't say anything specific about this media type at all
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * and these variants just got in on a wildcard, we prefer the lowest
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * level, on grounds that that's the one that the client is least likely
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * to choke on.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * (This is all motivated by treatment of levels in HTML --- we only
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * want to give level 3 to browsers that explicitly ask for it; browsers
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * that don't, including HTTP/0.9 browsers that only get the implicit
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * "Accept: * / *" [space added to avoid confusing cpp --- no, that
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * syntax doesn't really work] should get HTML2 if available).
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * (Note that this code only comes into play when we are choosing among
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * variants of equal quality, where the draft standard gives us a fair
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * bit of leeway about what to do. It ain't specified by the standard;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * rather, it is a choice made by this server about what to do in cases
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * where the standard does not specify a unique course of action).
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj /* Levels are only comparable between matching media types */
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj /* The result of the above if statements is that, if we get to
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * here, both variants have the same mime_type or both are
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * pseudo-html.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj /* Take highest level that matched, if either did match. */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Neither matched. Take lowest level, if there's a difference. */
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf/* Finding languages. The main entry point is set_language_quality()
0e05808dc59a321566303084c84b9826a4353cefrederpj * which is called for each variant. It sets two elements in the
0e05808dc59a321566303084c84b9826a4353cefrederpj * variant record:
0e05808dc59a321566303084c84b9826a4353cefrederpj * language_quality - the 'q' value of the 'best' matching language
0e05808dc59a321566303084c84b9826a4353cefrederpj * from Accept-Language: header (HTTP/1.1)
0e05808dc59a321566303084c84b9826a4353cefrederpj * lang_index - Non-negotiated language priority, using
0e05808dc59a321566303084c84b9826a4353cefrederpj * position of language on the Accept-Language:
0e05808dc59a321566303084c84b9826a4353cefrederpj * header, if present, else LanguagePriority
0e05808dc59a321566303084c84b9826a4353cefrederpj * directive order.
0e05808dc59a321566303084c84b9826a4353cefrederpj * When we do the variant checking for best variant, we use language
0e05808dc59a321566303084c84b9826a4353cefrederpj * quality first, and if a tie, language_index next (this only applies
0e05808dc59a321566303084c84b9826a4353cefrederpj * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
0e05808dc59a321566303084c84b9826a4353cefrederpj * algorithm, lang_index is never used.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * set_language_quality() calls find_lang_index() and find_default_index()
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * to set lang_index.
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpjstatic int find_lang_index(apr_array_header_t *accept_langs, char *lang)
0e05808dc59a321566303084c84b9826a4353cefrederpj const char **alang;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj/* set_default_lang_quality() sets the quality we apply to variants
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * which have no language assigned to them. If none of the variants
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * have a language, we are not negotiating on language, so all are
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * acceptable, and we set the default q value to 1.0. However if
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * some of the variants have languages, we set this default to 0.0001.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * The value of this default will be applied to all variants with
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * no explicit language -- which will have the effect of making them
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * acceptable, but only if no variants with an explicit language
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * are acceptable. The default q value set here is assigned to variants
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * with no language type in set_language_quality().
0e05808dc59a321566303084c84b9826a4353cefrederpj * Note that if using the RVSA/1.0 algorithm, we don't use this
8445dae5cc606ba8ba04efc341cc1e081d95920drpluemstatic void set_default_lang_quality(negotiation_state *neg)
0e05808dc59a321566303084c84b9826a4353cefrederpj/* Set the language_quality value in the variant record. Also
0e05808dc59a321566303084c84b9826a4353cefrederpj * assigns lang_index for ForceLanguagePriority.
0e05808dc59a321566303084c84b9826a4353cefrederpj * To find the language_quality value, we look for the 'q' value
0e05808dc59a321566303084c84b9826a4353cefrederpj * of the 'best' matching language on the Accept-Language
0e05808dc59a321566303084c84b9826a4353cefrederpj * header. The 'best' match is the language on Accept-Language
0e05808dc59a321566303084c84b9826a4353cefrederpj * header which matches the language of this variant either fully,
0e05808dc59a321566303084c84b9826a4353cefrederpj * or as far as the prefix marker (-). If two or more languages
0e05808dc59a321566303084c84b9826a4353cefrederpj * match, use the longest string from the Accept-Language header
0e05808dc59a321566303084c84b9826a4353cefrederpj * (see HTTP/1.1 [14.4])
0e05808dc59a321566303084c84b9826a4353cefrederpj * When a variant has multiple languages, we find the 'best'
0e05808dc59a321566303084c84b9826a4353cefrederpj * match for each variant language tag as above, then select the
0e05808dc59a321566303084c84b9826a4353cefrederpj * one with the highest q value. Because both the accept-header
0e05808dc59a321566303084c84b9826a4353cefrederpj * and variant can have multiple languages, we now have a hairy
0e05808dc59a321566303084c84b9826a4353cefrederpj * loop-within-a-loop here.
0e05808dc59a321566303084c84b9826a4353cefrederpj * If the variant has no language and we have no Accept-Language
0e05808dc59a321566303084c84b9826a4353cefrederpj * items, leave the quality at 1.0 and return.
0e05808dc59a321566303084c84b9826a4353cefrederpj * If the variant has no language, we use the default as set by
0e05808dc59a321566303084c84b9826a4353cefrederpj * set_default_lang_quality() (1.0 if we are not negotiating on
0e05808dc59a321566303084c84b9826a4353cefrederpj * language, 0.001 if we are).
0e05808dc59a321566303084c84b9826a4353cefrederpj * Following the setting of the language quality, we drop through to
0e05808dc59a321566303084c84b9826a4353cefrederpj * set the old 'lang_index'. This is set based on either the order
0e05808dc59a321566303084c84b9826a4353cefrederpj * of the languages on the Accept-Language header, or the
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * order on the LanguagePriority directive. This is only used
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * in the negotiation if the language qualities tie.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpjstatic void set_language_quality(negotiation_state *neg, var_rec *variant)
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (!variant->content_languages || !variant->content_languages->nelts) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj /* This variant has no content-language, so use the default
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * quality factor for variants with no content-language
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * (previously set by set_default_lang_quality()).
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * Leave the factor alone (it remains at 1.0) when we may not fiddle
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * with the headers.
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj return; /* no accept-language header */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Variant has one (or more) languages. Look for the best
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * match. We do this by going through each language on the
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * variant description looking for a match on the
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * Accept-Language header. The best match is the longest
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * matching language on the header. The final result is the
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * best q value from all the languages on the variant
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * description.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* no accept-language header makes the variant indefinite */
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf else { /* There is an accept-language with 0 or more items */
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj for (j = 0; j < variant->content_languages->nelts; ++j) {
e8f95a682820a599fe41b22977010636be5c2717jim /* lang is the variant's language-tag, which is the one
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * we are allowed to use the prefix of in HTTP/1.1
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe lang = ((char **) (variant->content_languages->elts))[j];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* now find the best (i.e. longest) matching
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes * Accept-Language header language. We put the best match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for this tag in bestthistag. We cannot update the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * overall best (based on q value) because the best match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for this tag is the longest language item on the accept
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header, not necessarily the highest q.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Find language. We match if either the variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language tag exactly matches the language range
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * from the accept header, or a prefix of the variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language tag up to a '-' character matches the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * whole of the language range in the Accept-Language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header. Note that HTTP/1.x allows any number of
e8f95a682820a599fe41b22977010636be5c2717jim * '-' characters in a tag or range, currently only
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * tags with zero or one '-' characters are defined
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for general use (see rfc1766).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * We only use language range in the Accept-Language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header the best match for the variant language tag
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * if it is longer than the previous best match.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* The next bit is a fiddle. Some browsers might
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * be configured to send more specific language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * ranges than desirable. For example, an
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Accept-Language of en-US should never match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variants with languages en or en-GB. But US
e8f95a682820a599fe41b22977010636be5c2717jim * English speakers might pick en-US as their
e8f95a682820a599fe41b22977010636be5c2717jim * language choice. So this fiddle checks if the
e8f95a682820a599fe41b22977010636be5c2717jim * language range has a prefix, and if so, it
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * matches variants which match that prefix with a
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * priority of 0.001. So a request for en-US would
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener * match variants of types en and en-GB, but at
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener * much lower priority than matches of en-US
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener * directly, or of any other language listed on
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener * the Accept-Language header. Note that this
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener * fiddle does not handle multi-level prefixes.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Finished looking at Accept-Language headers, the best
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * (longest) match is in bestthistag, or NULL if no match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes (bestthistag && bestthistag->quality > best->quality)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* See if the tag matches on a * in the Accept-Language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header. If so, record this fact for later use
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If one of the language tags of the variant matched on *, we
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * need to see if its q is better than that of any non-* match
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener * on any other tag of the variant. If so the * match takes
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener * precedence and the overall match is not definite.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->lang_quality = best ? best->quality : fiddle_q;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Handle the ForceDefaultLanguage overrides, based on the best match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * to LanguagePriority order. The best match is the lowest index of
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * any LanguagePriority match.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes for (j = 0; j < variant->content_languages->nelts; ++j)
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* lang is the variant's language-tag, which is the one
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we are allowed to use the prefix of in HTTP/1.1
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *lang = ((char **) (variant->content_languages->elts))[j];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If we wish to fallback or
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we use our own LanguagePriority index.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes idx = find_lang_index(neg->conf->language_priority, lang);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf/* Determining the content length --- if the map didn't tell us,
7dbf29be626018bc389ef94c1846aeac4b72633bsf * we have to do a stat() and remember for next time.
7dbf29be626018bc389ef94c1846aeac4b72633bsfstatic apr_off_t find_content_length(negotiation_state *neg, var_rec *variant)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (variant->sub_req && (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* For a given variant, find the best matching Accept: header
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * and assign the Accept: header's quality value to the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * mime_type_quality field of the variant, for later use in
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * determining the best matching variant.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void set_accept_quality(negotiation_state *neg, var_rec *variant)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float q = 0.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* if no Accept: header, leave quality alone (will
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * remain at the default value of 1)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * XXX: This if is currently never true because of the effect of
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * maybe_add_default_accepts().
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Go through each of the ranges on the Accept: header,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * looking for the 'best' match with this variant's
e8f95a682820a599fe41b22977010636be5c2717jim * content-type. We use the best match's quality
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * value (from the Accept: header) for this variant's
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * mime_type_quality field.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * The best match is determined like this:
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * type/type is better than type/ * is better than * / *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * if match is type/type, use the level mime param if available
e8f95a682820a599fe41b22977010636be5c2717jim continue; /* didn't match the content type at all */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* did match - see if there were less or more stars than
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * in previous match
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes continue; /* more stars => not as good a match */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* If we are allowed to mess with the q-values
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * and have no explicit q= parameters in the accept header,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * make wildcards very low, so we have a low chance
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * of ending up with them if there's something better.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!neg->dont_fiddle_headers && !neg->accept_q &&
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!neg->dont_fiddle_headers && !neg->accept_q &&
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->definite = variant->definite && q_definite;
e8f95a682820a599fe41b22977010636be5c2717jim/* For a given variant, find the 'q' value of the charset given
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * on the Accept-Charset line. If no charsets are listed,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * assume value of '1'.
8bdea88407c848c1c2693655e2f8b23abde12307bnicholesstatic void set_charset_quality(negotiation_state *neg, var_rec *variant)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* if no Accept-Charset: header, leave quality alone (will
f05787953018140838ad51456c86c965d6a86267jim * remain at the default value of 1)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_recs = (accept_rec *) neg->accept_charsets->elts;
e8f95a682820a599fe41b22977010636be5c2717jim /* Charset of variant not known */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* if not a text / * type, leave quality alone */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* Don't go guessing if we are in strict header mode,
e8f95a682820a599fe41b22977010636be5c2717jim * e.g. when running the rvsa, as any guess won't be reflected
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * in the variant list or content-location headers.
e8f95a682820a599fe41b22977010636be5c2717jim charset = "iso-8859-1"; /* The default charset for HTTP text types */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * Go through each of the items on the Accept-Charset header,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * looking for a match with this variant's charset. If none
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * match, charset is unacceptable, so set quality to 0.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes for (i = 0; i < neg->accept_charsets->nelts; ++i) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* No explicit match */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* If this variant is in charset iso-8859-1, the default is 1.0 */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes/* is_identity_encoding is included for back-compat, but does anyone
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * use 7bit, 8bin or binary in their var files??
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener * set_encoding_quality determines whether the encoding for a particular
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * variant is acceptable for the user-agent.
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes * The rules for encoding are that if the user-agent does not supply
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * any Accept-Encoding header, then all encodings are allowed but a
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * variant with no encoding should be preferred.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * If there is an empty Accept-Encoding header, then no encodings are
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * acceptable. If there is a non-empty Accept-Encoding header, then
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * any of the listed encodings are acceptable, as well as no encoding
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * unless the "identity" encoding is specifically excluded.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholesstatic void set_encoding_quality(negotiation_state *neg, var_rec *variant)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* We had no Accept-Encoding header, assume that all
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * encodings are acceptable with a low quality,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * but we prefer no encoding if available.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_recs = (accept_rec *) neg->accept_encodings->elts;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* Go through each of the encodings on the Accept-Encoding: header,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * looking for a match with our encoding. x- prefixes are ignored.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes for (i = 0; i < neg->accept_encodings->nelts; ++i) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->encoding_quality = accept_recs[i].quality;
e8f95a682820a599fe41b22977010636be5c2717jim /* No explicit match */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* Encoding not found on Accept-Encoding: header, so it is
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * _not_ acceptable unless it is the identity (no encoding)
e8f95a682820a599fe41b22977010636be5c2717jim/*************************************************************
e8f95a682820a599fe41b22977010636be5c2717jim * Possible results of the variant selection algorithm
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes/* Below is the 'best_match' function. It returns an int, which has
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * one of the two values alg_choice or alg_list, which give the result
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * of the variant selection algorithm. alg_list means that no best
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * variant was found by the algorithm, alg_choice means that a best
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes * variant was found and should be returned. The list/choice
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes * terminology comes from TCN (rfc2295), but is used in a more generic
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes * way here. The best variant is returned in *pbest. best_match has
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes * two possible algorithms for determining the best variant: the
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes * RVSA/1.0 algorithm (from RFC2296), and the standard Apache
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes * algorithm. These are split out into separate functions
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * (is_variant_better_rvsa() and is_variant_better()). Selection of
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * one is through the neg->use_rvsa flag.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * The call to best_match also creates full information, including
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * language, charset, etc quality for _every_ variant. This is needed
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * for generating a correct Vary header, and can be used for the
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * Alternates header, the human-readable list responses and 406 errors.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * v1.0) from rfc2296. This is the algorithm that goes together with
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * transparent content negotiation (TCN).
e8f95a682820a599fe41b22977010636be5c2717jimstatic int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* TCN does not cover negotiation on content-encoding. For now,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we ignore the encoding unless it was explicitly excluded.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* RFC 2296 calls for the result to be rounded to 5 decimal places,
e8f95a682820a599fe41b22977010636be5c2717jim * but we don't do that because it serves no useful purpose other
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * than to ensure that a remote algorithm operates on the same
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * precision as ours. That is silly, since what we obviously want
e8f95a682820a599fe41b22977010636be5c2717jim * is for the algorithm to operate on the best available precision
e8f95a682820a599fe41b22977010636be5c2717jim * regardless of who runs it. Since the above calculation may
43c3e6a4b559b76b750c245ee95e2782c15b4296jim * result in significant variance at 1e-12, rounding would be bogus.
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "q=%1.5f definite=%d",
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (q <= 0.0f) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If the best variant's encoding is of lesser quality than
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * this variant, then we prefer this variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (variant->encoding_quality > best->encoding_quality) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Negotiation algorithm as used by previous versions of Apache
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * (just about).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic int is_variant_better(negotiation_state *neg, var_rec *variant,
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes /* For non-transparent negotiation, server can choose how
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * to handle the negotiation. We'll use the following in
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * order: content-type, language, content-type level, charset,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * content encoding, content length.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * For each check, we have three possible outcomes:
e8f95a682820a599fe41b22977010636be5c2717jim * This variant is worse than current best: return 0
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * This variant is better than the current best:
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * assign this variant's q to *p_bestq, and return 1
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * This variant is just as desirable as the current best:
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * drop through to the next test.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * This code is written in this long-winded way to allow future
e8f95a682820a599fe41b22977010636be5c2717jim * customisation, either by the addition of additional
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * checks, or to allow the order of the checks to be determined
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * by configuration options (e.g. we might prefer to check
e8f95a682820a599fe41b22977010636be5c2717jim * language quality _before_ content type).
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes /* First though, eliminate this variant if it is not
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * acceptable by type, charset, encoding or language.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
e8f95a682820a599fe41b22977010636be5c2717jim ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes return 0; /* don't consider unacceptables */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes q = variant->mime_type_quality * variant->source_quality;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* language */
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes /* if language qualities were equal, try the LanguagePriority stuff */
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes (variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes (best->lang_index == -1 || variant->lang_index < best->lang_index)) {
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf /* content-type level (sometimes used with text/html, though we
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * support it on other types too)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* charset */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->charset_quality < best->charset_quality) {
88adce5ec0da39b41450ce1d5a77659168097e0cjorton /* If the best variant's charset is ISO-8859-1 and this variant has
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the same charset quality, then we prefer this variant
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->charset_quality > best->charset_quality ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin strcmp(variant->content_charset, "iso-8859-1") != 0) &&
54d22ed1c429b903b029bbd62621f11a9e286137minfrin strcmp(best->content_charset, "iso-8859-1") == 0))) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Prefer the highest value for encoding_quality.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->encoding_quality < best->encoding_quality) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->encoding_quality > best->encoding_quality) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* content length if all else equal */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (find_content_length(neg, variant) >= find_content_length(neg, best)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* ok, to get here means every thing turned out equal, except
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes * we have a shorter content length, so use this variant
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic int best_match(negotiation_state *neg, var_rec **pbest)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Find the 'best' variant
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Find all the relevant 'quality' values from the
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes * Accept... headers, and store in the variant. This also
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes * prepares for sending an Alternates header etc so we need to
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes * do it even if we do not actually plan to find a best
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Only do variant selection if we may actually choose a
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * variant for the client
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Now find out if this variant is better than the current
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * best, either using the RVSA/1.0 algorithm, or Apache's
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * internal server-driven algorithm. Presumably other
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * server-driven algorithms are possible, and could be
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * implemented here.
e8f95a682820a599fe41b22977010636be5c2717jim if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (is_variant_better(neg, variant, best, &bestq)) {
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes /* We now either have a best variant, or no best variant */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* calculate result for RVSA/1.0 algorithm:
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * only a choice response if the best variant has q>0
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * and is definite
54d22ed1c429b903b029bbd62621f11a9e286137minfrin algorithm_result = (best && best->definite) && (bestq > 0) ?
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* calculate result for Apache negotiation algorithm */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin algorithm_result = bestq > 0 ? alg_choice : alg_list;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Returning a choice response with a non-neighboring variant is a
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * protocol security error in TCN (see rfc2295). We do *not*
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * verify here that the variant and URI are neighbors, even though
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * we may return alg_choice. We depend on the environment (the
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * caller) to only declare the resource transparently negotiable if
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * all variants are neighbors.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin/* Sets response headers for a negotiated response.
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes * neg->is_transparent determines whether a transparently negotiated
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * response or a plain `server driven negotiation' response is
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * created. Applicable headers are Alternates, Vary, and TCN.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * The Vary header we create is sometimes longer than is required for
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the correct caching of negotiated results by HTTP/1.1 caches. For
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the Accept: header assigns a 0 quality to .ps, then the results of
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the two server-side negotiation algorithms we currently implement
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * will never depend on Accept-Language so we could return `Vary:
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * negotiate, accept' instead of the longer 'Vary: negotiate, accept,
e8f95a682820a599fe41b22977010636be5c2717jim * accept-language' which the code below will return. A routine for
e8f95a682820a599fe41b22977010636be5c2717jim * computing the exact minimal Vary header would be a huge pain to code
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * and maintain though, especially because we need to take all possible
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * twiddles in the server-side negotiation algorithms into account.
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic void set_neg_headers(request_rec *r, negotiation_state *neg,
5aa455d45abacfa675c88d4ff53fbe97c44ce545bnicholes var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin int max_vlist_array = (neg->avail_vars->nelts * 21);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* In order to avoid O(n^2) memory copies in building Alternates,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we preallocate a apr_table_t with the maximum substrings possible,
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener * fill it with the variant list, and then concatenate the entire array.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Note that if you change the number of substrings pushed, you also
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * need to change the calculation of max_vlist_array above.
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes if (neg->send_alternates && neg->avail_vars->nelts)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Put headers into err_headers_out, since send_http_header()
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * outputs both headers_out and err_headers_out.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->content_languages && variant->content_languages->nelts) {
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Calculate Vary by looking for any difference between variants */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Generate the string components for this Alternates entry */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *((const char **) apr_array_push(arr)) = variant->file_name;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Strip trailing zeros (saves those valuable network bytes) */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *((const char **) apr_array_push(arr)) = variant->mime_type;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->content_charset && *variant->content_charset) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *((const char **) apr_array_push(arr)) = " {charset ";
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *((const char **) apr_array_push(arr)) = variant->content_charset;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *((const char **) apr_array_push(arr)) = " {language ";
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->content_encoding && *variant->content_encoding) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Strictly speaking, this is non-standard, but so is TCN */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *((const char **) apr_array_push(arr)) = " {encoding ";
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *((const char **) apr_array_push(arr)) = variant->content_encoding;
e8f95a682820a599fe41b22977010636be5c2717jim /* Note that the Alternates specification (in rfc2295) does
e8f95a682820a599fe41b22977010636be5c2717jim * not require that we include {length x}, so we could omit it
141e1368614dc7564e1627671361b01b4869b491bnicholes * if determining the length is too expensive. We currently
141e1368614dc7564e1627671361b01b4869b491bnicholes * always include it though. 22 bytes is enough for 2^64.
141e1368614dc7564e1627671361b01b4869b491bnicholes * If the variant is a CGI script, find_content_length would
141e1368614dc7564e1627671361b01b4869b491bnicholes * return the length of the script, not the output it
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * produces, so we check for the presence of a handler and if
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * there is one we don't add a length.
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * XXX: TODO: This check does not detect a CGI script if we
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * get the variant from a type map. This needs to be fixed
141e1368614dc7564e1627671361b01b4869b491bnicholes * (without breaking things if the type map specifies a
796e4a7141265d8ed7036e4628161c6eafb2a789jorton * content-length, which currently leads to the correct result).
141e1368614dc7564e1627671361b01b4869b491bnicholes if (!(variant->sub_req && variant->sub_req->handler)
141e1368614dc7564e1627671361b01b4869b491bnicholes && (len = find_content_length(neg, variant)) >= 0) {
141e1368614dc7564e1627671361b01b4869b491bnicholes *((const char **) apr_array_push(arr)) = " {length ";
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes *((const char **) apr_array_push(arr)) = ", "; /* trimmed below */
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes if (neg->is_transparent || vary_by_type || vary_by_language ||
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes vary_by_language || vary_by_charset || vary_by_encoding) {
1c9fe70e77b36d36ae34997fe25fe47beacf8709bnicholes apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
d0fb19f6ddefe0d1b3c94eee44776a848ab0690drpluem if (neg->is_transparent) { /* Create TCN response header */
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes/**********************************************************************
9ad7b260be233be7d7b5576979825cac72e15498rederpj * Return an HTML list of variants. This is output as part of the
9ad7b260be233be7d7b5576979825cac72e15498rederpj * choice response or 406 status body.
9ad7b260be233be7d7b5576979825cac72e15498rederpjstatic char *make_variant_list(request_rec *r, negotiation_state *neg)
128a5d93141a86e3afa151e921035a07297c9833rederpj int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
9ad7b260be233be7d7b5576979825cac72e15498rederpj /* In order to avoid O(n^2) memory copies in building the list,
9ad7b260be233be7d7b5576979825cac72e15498rederpj * we preallocate a apr_table_t with the maximum substrings possible,
9ad7b260be233be7d7b5576979825cac72e15498rederpj * fill it with the variant list, and then concatenate the entire array.
9ad7b260be233be7d7b5576979825cac72e15498rederpj arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
0f60998368b493f90120180a93fc2e1e74490872covener *((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n";
0f60998368b493f90120180a93fc2e1e74490872covener var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
0f60998368b493f90120180a93fc2e1e74490872covener const char *filename = variant->file_name ? variant->file_name : "";
0f60998368b493f90120180a93fc2e1e74490872covener apr_array_header_t *languages = variant->content_languages;
0f60998368b493f90120180a93fc2e1e74490872covener const char *description = variant->description ? variant->description : "";
0f60998368b493f90120180a93fc2e1e74490872covener /* The format isn't very neat, and it would be nice to make
0f60998368b493f90120180a93fc2e1e74490872covener * the tags human readable (eg replace 'language en' with 'English').
0f60998368b493f90120180a93fc2e1e74490872covener * Note that if you change the number of substrings pushed, you also
0f60998368b493f90120180a93fc2e1e74490872covener * need to change the calculation of max_vlist_array above.
0f60998368b493f90120180a93fc2e1e74490872covener *((const char **) apr_array_push(arr)) = "<li><a href=\"";
0f60998368b493f90120180a93fc2e1e74490872covener *((const char **) apr_array_push(arr)) = description;
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = variant->mime_type;
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = ", language ";
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool,
67c5e190a7b84528b8895d7ef7be81712d206805covener if (variant->content_charset && *variant->content_charset) {
67c5e190a7b84528b8895d7ef7be81712d206805covener *((const char **) apr_array_push(arr)) = ", charset ";
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = variant->content_charset;
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = ", encoding ";
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = variant->content_encoding;
128a5d93141a86e3afa151e921035a07297c9833rederpjstatic void store_variant_list(request_rec *r, negotiation_state *neg)
9ad7b260be233be7d7b5576979825cac72e15498rederpj apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf/* Called if we got a "Choice" response from the variant selection algorithm.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * It checks the result of the chosen variant to see if it
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * Otherwise, add the appropriate headers to the current response.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sfstatic int setup_choice_response(request_rec *r, negotiation_state *neg,
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf const char *sub_vary;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf sub_req = ap_sub_req_lookup_file(variant->file_name, r, NULL);
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf /* The variant selection algorithm told us to return a "Choice"
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * response. This is the normal variant response, with
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * some extra headers. First, ensure that the chosen
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * variant did or will not itself engage in transparent negotiation.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * If not, set the appropriate headers, and fall through to
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * the normal variant handling
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* This catches the error that a transparent type map selects a
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * transparent multiviews resource as the best variant.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * XXX: We do not signal an error if a transparent type map
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * selects a _non_transparent multiviews resource as the best
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * variant, because we can generate a legal negotiation response
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * in this case. In this case, the vlist_validator of the
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * nontransparent subrequest will be lost however. This could
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * lead to cases in which a change in the set of variants or the
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * negotiation algorithm of the nontransparent resource is never
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * propagated up to a HTTP/1.1 cache which interprets Vary. To be
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * for this type of recursive negotiation too.
0972df03482b520989d4adbf12626755aef24907covener /* This catches the error that a transparent type map recursively
0972df03482b520989d4adbf12626755aef24907covener * selects, as the best variant, another type map which itself
0972df03482b520989d4adbf12626755aef24907covener * causes transparent negotiation to be done.
7a55c294da84865fe13262ed66ffd0c5841a9da5covener * XXX: Actually, we catch this error by catching all cases of
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * type map recursion. There are some borderline recursive type
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * map arrangements which would not produce transparent
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * negotiation protocol errors or lack of cache propagation
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * problems, but such arrangements are very hard to detect at this
9ad7b260be233be7d7b5576979825cac72e15498rederpj * point in the control flow, so we do not bother to single them
e8f95a682820a599fe41b22977010636be5c2717jim * Recursive type maps imply a recursive arrangement of negotiated
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * resources which is visible to outside clients, and this is not
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * supported by the transparent negotiation caching protocols, so
a0b44ec81b32ffb946537d4e43e1c3a3f7594137gregames * if we are to have generic support for recursive type maps, we
1233ffe2092021833c2a642f1017d6c332075469gregames * have to create some configuration setting which makes all type
1233ffe2092021833c2a642f1017d6c332075469gregames * maps non-transparent when recursion is enabled. Also, if we
a0b44ec81b32ffb946537d4e43e1c3a3f7594137gregames * want recursive type map support which ensures propagation of
490046d2a164ad86cc63bd789f32eaed663d239abnicholes * type map changes into HTTP/1.1 caches that handle Vary, we
490046d2a164ad86cc63bd789f32eaed663d239abnicholes * would have to extend the current mechanism for generating
490046d2a164ad86cc63bd789f32eaed663d239abnicholes * variant list validators.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* This adds an appropriate Variant-Vary header if the subrequest
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * is a multiviews resource.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * XXX: TODO: Note that this does _not_ handle any Vary header
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * returned by a CGI if sub_req is a CGI script, because we don't
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * see that Vary header yet at this point in the control flow.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * This won't cause any cache consistency problems _unless_ the
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes * CGI script also returns a Cache-Control header marking the
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * response as cachable. This needs to be fixed, also there are
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * problems if a CGI returns an Etag header which also need to be
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Move the subreq Vary header into the main request to
f2386b627177c7a80d38fed6ec0aed3c086909c1covener * prevent having two Vary headers in the response, which
f2386b627177c7a80d38fed6ec0aed3c086909c1covener * would be legal but strange.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_table_setn(r->err_headers_out, "Content-Location",
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes /* Still to do by caller: add Expires */
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes/****************************************************************
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Executive...
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic int do_negotiation(request_rec *r, negotiation_state *neg,
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes int alg_result; /* result of variant selection algorithm */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Decide if resource is transparently negotiable */
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes /* GET or HEAD? (HEAD has same method number as GET) */
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes /* maybe this should be configurable, see also the comment
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * about recursive type maps in setup_choice_response()
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes /* We can't be transparent if we are a map file in the middle
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * of the request URI.
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener /* We can't be transparent, because of internal
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * assumptions in best_match(), if there is a
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * non-neighboring variant. We can have a non-neighboring
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variant when processing a type map.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else { /* configure negotiation on non-transparent resource */
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick /* alg_result is one of
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick * alg_choice: a best variant is chosen
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick * alg_list: no best variant is chosen
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick /* send a list response or HTTP_NOT_ACCEPTABLE error response */
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick neg->send_alternates = 1; /* always include Alternates header */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* XXX todo: expires? cachability? */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Some HTTP/1.0 clients are known to choke when they get
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * a 300 (multiple choices) response without a Location
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header. However the 300 code response we are are about
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * to generate will only reach 1.0 clients which support
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * transparent negotiation, and they should be OK. The
e8f95a682820a599fe41b22977010636be5c2717jim * response should never reach older 1.0 clients, even if
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * we have CacheNegotiatedDocs enabled, because no 1.0
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * proxy cache (we know of) will cache and return 300
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes * responses (they certainly won't if they conform to the
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes * HTTP/1.0 specification).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Variant selection chose a variant */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* XXX todo: merge the two cases in the if statement below */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Make sure caching works - Vary should handle HTTP/1.1, but for
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * HTTP/1.0, we can't allow caching at all.
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton /* XXX: Note that we only set r->no_cache to 1, which causes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Expires: <now> to be added, when responding to a HTTP/1.0
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * client. If we return the response to a 1.1 client, we do not
024e70e05386a6367eb45d0d1cc406764520ff4cwrowe * add Expires <now>, because doing so would degrade 1.1 cache
43997561b2302d13dee973998e77743a3ddd2374trawick * performance by preventing re-use of the response without prior
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * revalidation. On the other hand, if the 1.1 client is a proxy
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * which was itself contacted by a 1.0 client, or a proxy cache
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * which can be contacted later by 1.0 clients, then we currently
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * rely on this 1.1 proxy to add the Expires: <now> when it
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * forwards the response.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * XXX: TODO: Find out if the 1.1 spec requires proxies and
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * tunnels to add Expires: <now> when forwarding the response to
e8f95a682820a599fe41b22977010636be5c2717jim * 1.0 clients. I (kh) recall it is rather vague on this point.
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * Testing actual 1.1 proxy implementations would also be nice. If
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Expires: <now> is not added by proxies then we need to always
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * include Expires: <now> ourselves to ensure correct caching, but
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * this would degrade HTTP/1.1 cache efficiency unless we also add
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Cache-Control: max-age=N, which we currently don't.
b26e0fd306aeae8a51efc1a57d1e9f42dc37a3f1covener * Roy: No, we are not going to screw over HTTP future just to
e8f95a682820a599fe41b22977010636be5c2717jim * ensure that people who can't be bothered to upgrade their
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * clients will always receive perfect server-side negotiation.
e8f95a682820a599fe41b22977010636be5c2717jim * Hell, those clients are sending bogus accept headers anyway.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Manual setting of cache-control/expires always overrides this
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * automated kluge, on purpose.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if(strcmp(r->handler,MAP_FILE_MAGIC_TYPE) && strcmp(r->handler,"type-map"))
54d22ed1c429b903b029bbd62621f11a9e286137minfrin ap_allow_standard_methods(r, REPLACE_ALLOW, M_GET, M_OPTIONS, M_POST, -1);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /*if (r->method_number == M_OPTIONS) {
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton * return ap_send_http_options(r);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (r->method_number != M_GET && r->method_number != M_POST) {
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton /* ### These may be implemented by adding some 'extra' info
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton * of the file offset onto the etag
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * ap_update_mtime(r, r->finfo.mtime);
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes * ap_set_last_modified(r);
f7edd56768a46a01ae8b43c712c9eeef40e37f59bnicholes * ap_set_etag(r);
f7edd56768a46a01ae8b43c712c9eeef40e37f59bnicholes apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* remove any path_info from the end of the uri before trying
e8f95a682820a599fe41b22977010636be5c2717jim * to change the filename. r->path_info from the original
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * request is passed along on the redirect.
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name,
023ab66b169a239c82d6c36410ab43fe63561d11covener /* free all allocated memory from subrequests */
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj /* We got this out of a map file, so we don't actually have
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj * a sub_req structure yet. Get one now.
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj sub_req = ap_sub_req_lookup_file(best->file_name, r, NULL);
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj /* now do a "fast redirect" ... promotes the sub_req into the main req */
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj /* give no advise for time on this subrequest. Perhaps we
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj * should tally the last mtime amoung all variants, and date
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj * the most recent, but that could confuse the proxies.
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* clean up all but our favorite variant, since that sub_req
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * is now merged into the main request!
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe/**********************************************************************
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes * There is a problem with content-encoding, as some clients send and
9ad7b260be233be7d7b5576979825cac72e15498rederpj * expect an x- token (e.g. x-gzip) while others expect the plain token
9ad7b260be233be7d7b5576979825cac72e15498rederpj * (i.e. gzip). To try and deal with this as best as possible we do
9ad7b260be233be7d7b5576979825cac72e15498rederpj * the following: if the client sent an Accept-Encoding header and it
9ad7b260be233be7d7b5576979825cac72e15498rederpj * contains a plain token corresponding to the content encoding of the
9ad7b260be233be7d7b5576979825cac72e15498rederpj * response, then set content encoding using the plain token. Else if
9ad7b260be233be7d7b5576979825cac72e15498rederpj * the A-E header contains the x- token use the x- token in the C-E
9ad7b260be233be7d7b5576979825cac72e15498rederpj * header. Else don't do anything.
9ad7b260be233be7d7b5576979825cac72e15498rederpj * Note that if no A-E header was sent, or it does not contain a token
0f60998368b493f90120180a93fc2e1e74490872covener * compatible with the final content encoding, then the token in the
0f60998368b493f90120180a93fc2e1e74490872covener * C-E header will be whatever was specified in the AddEncoding
0f60998368b493f90120180a93fc2e1e74490872covener * directive.
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) {
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes accept_recs = (accept_rec *) accept_encodings->elts;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) {
return DECLINED;