I'm planning to apply some request dependent extensions to mod_negotiation. 
The first is ready-to-commit :) but I'd like to hear some comments about 
them in general.
The main goal is to make some configuration cases more simple.

The attached patch (mod_negotiation.c.patch) introduces a new special 
variable "prefer-language" which influences mod_negotiation in that way, 
that it first tries to select only variants that have the specified 
language tag. If no success, it tries negotiation again with all variants.
A more readable patch (without the addional indentation) is also attached.

This allows, however, cool configurations like the following:

AliasMatch ^/manual(?:/(?:de|en|ja|pt-br|ru))?(/.*)? \
            /path/to/manual$1

<Directory "/path/to/manual">
  [...normal stuff...]

# would be nice, but isn't supported yet:
#    SetEnvIf Request_URI ^/manual/(de|en|ja|pt-br|ru)/ \
#               prefer-language=$1

    SetEnvIf Request_URI ^/manual/de/    prefer-language=de
    SetEnvIf Request_URI ^/manual/en/    prefer-language=en
    SetEnvIf Request_URI ^/manual/ja/    prefer-language=ja
    SetEnvIf Request_URI ^/manual/pt-br/ prefer-language=pt-br
    SetEnvIf Request_URI ^/manual/ru/    prefer-language=ru
</Directory>

As you may guess, it would be very useful for the distributed documentation 
(there are/were already some discussions about the problem). So I'd also 
propose a backport to 2.0.

The next step would be to make mod_negotiation recognize "no-gzip" and 
"gzip-only-text/html", too. In fact, I had an early patch for that (which 
dropped encoded variants entirely from the list), but I think, if it's 
implemented similar to the "prefer-language" code, it's much better.

Any comments, suggestions or objections?
If not, I'm going to commit the stuff :)

nd
-- 
Flhacs wird im Usenet grunds�tzlich alsfhc geschrieben. Schreibt man
lafhsc nicht slfach, so ist das schlichtweg hclafs. Hingegen darf man
rihctig ruhig rhitcgi schreiben, weil eine shcalfe Schreibweise bei
irhictg nicht als shflac angesehen wird.       -- Hajo Pfl�ger in dnq
Index: modules/mappers/mod_negotiation.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/mappers/mod_negotiation.c,v
retrieving revision 1.109
diff -u -r1.109 mod_negotiation.c
--- modules/mappers/mod_negotiation.c   25 Nov 2002 19:02:44 -0000      1.109
+++ modules/mappers/mod_negotiation.c   22 Jan 2003 00:29:55 -0000
@@ -2185,74 +2185,125 @@
     return 1;
 }
 
+/* figure out, whether a variant is in a specific language
+ * it returns also false, if the variant has no language.
+ */
+static int variant_has_language(var_rec *variant, const char *lang)
+{
+    int j, max;
+
+    /* fast exit */
+    if (   !lang
+        || !variant->content_languages
+        || !(max = variant->content_languages->nelts)) {
+        return 0;
+    }
+
+    for (j = 0; j < max; ++j) {
+        if (!strcmp(lang,
+                    ((char **) (variant->content_languages->elts))[j])) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 static int best_match(negotiation_state *neg, var_rec **pbest)
 {
     int j;
-    var_rec *best = NULL;
+    var_rec *best;
     float bestq = 0.0f;
     enum algorithm_results algorithm_result;
 
     var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
 
+    const char *preferred_language = apr_table_get(neg->r->subprocess_env,
+                                                   "prefer-language");
+
     set_default_lang_quality(neg);
 
     /*
      * Find the 'best' variant 
+     * We run the loop possibly twice: if "prefer-language"
+     * environment variable is set but we did not find an appropriate
+     * best variant. In that case forget the preferred language and
+     * negotiate over all variants.
      */
+    
+    do {
+        best = NULL;
+
+        for (j = 0; j < neg->avail_vars->nelts; ++j) {
+            var_rec *variant = &avail_recs[j];
 
-    for (j = 0; j < neg->avail_vars->nelts; ++j) {
-        var_rec *variant = &avail_recs[j];
+            /* if a language is preferred, but the current variant
+             * is not in that language, then drop it for now
+             */
+            if (   preferred_language
+                && !variant_has_language(variant, preferred_language)) {
+                continue;
+            }
 
-        /* Find all the relevant 'quality' values from the
-         * Accept... headers, and store in the variant.  This also
-         * prepares for sending an Alternates header etc so we need to
-         * do it even if we do not actually plan to find a best
-         * variant.  
-         */
-        set_accept_quality(neg, variant);
-        set_language_quality(neg, variant);
-        set_encoding_quality(neg, variant);
-        set_charset_quality(neg, variant);
-
-        /* Only do variant selection if we may actually choose a
-         * variant for the client 
-         */
-        if (neg->may_choose) {
-
-            /* Now find out if this variant is better than the current
-             * best, either using the RVSA/1.0 algorithm, or Apache's
-             * internal server-driven algorithm. Presumably other
-             * server-driven algorithms are possible, and could be
-             * implemented here.
+            /* Find all the relevant 'quality' values from the
+             * Accept... headers, and store in the variant.  This also
+             * prepares for sending an Alternates header etc so we need to
+             * do it even if we do not actually plan to find a best
+             * variant.  
              */
+            set_accept_quality(neg, variant);
+            set_language_quality(neg, variant);
+            set_encoding_quality(neg, variant);
+            set_charset_quality(neg, variant);
+
+            /* Only do variant selection if we may actually choose a
+             * variant for the client 
+             */
+            if (neg->may_choose) {
+
+                /* Now find out if this variant is better than the current
+                 * best, either using the RVSA/1.0 algorithm, or Apache's
+                 * internal server-driven algorithm. Presumably other
+                 * server-driven algorithms are possible, and could be
+                 * implemented here.
+                 */
      
-            if (neg->use_rvsa) {
-                if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
-                    best = variant;
+                if (neg->use_rvsa) {
+                    if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
+                        best = variant;
+                    }
                 }
-            }
-            else {
-                if (is_variant_better(neg, variant, best, &bestq)) {
-                    best = variant;
+                else {
+                    if (is_variant_better(neg, variant, best, &bestq)) {
+                        best = variant;
+                    }
                 }
             }
         }
-    }
 
-    /* We now either have a best variant, or no best variant */
+        /* We now either have a best variant, or no best variant */
 
-    if (neg->use_rvsa)    {
-        /* calculate result for RVSA/1.0 algorithm:
-         * only a choice response if the best variant has q>0
-         * and is definite
-         */
-        algorithm_result = (best && best->definite) && (bestq > 0) ?
-                           alg_choice : alg_list;
-    }
-    else {
-        /* calculate result for Apache negotiation algorithm */
-        algorithm_result = bestq > 0 ? alg_choice : alg_list;        
-    }
+        if (neg->use_rvsa)    {
+            /* calculate result for RVSA/1.0 algorithm:
+             * only a choice response if the best variant has q>0
+             * and is definite
+             */
+            algorithm_result = (best && best->definite) && (bestq > 0) ?
+                                alg_choice : alg_list;
+        }
+        else {
+            /* calculate result for Apache negotiation algorithm */
+            algorithm_result = bestq > 0 ? alg_choice : alg_list;        
+        }
+
+        /* run the loop again, if the "prefer-language" got no clear result */
+        if (preferred_language && (!best || algorithm_result != alg_choice)) {
+            preferred_language = NULL;
+            continue;
+        }
+
+        break;
+    } while (1);
 
     /* Returning a choice response with a non-neighboring variant is a
      * protocol security error in TCN (see rfc2295).  We do *not*
Index: mod_negotiation.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/mappers/mod_negotiation.c,v
retrieving revision 1.109
diff -u -r1.109 mod_negotiation.c
--- mod_negotiation.c   25 Nov 2002 19:02:44 -0000      1.109
+++ mod_negotiation.c   22 Jan 2003 00:48:21 -0000
@@ -2185,24 +2185,66 @@
     return 1;
 }
 
+/* figure out, whether a variant is in a specific language
+ * it returns also false, if the variant has no language.
+ */
+static int variant_has_language(var_rec *variant, const char *lang)
+{
+    int j, max;
+
+    /* fast exit */
+    if (   !lang
+        || !variant->content_languages
+        || !(max = variant->content_languages->nelts)) {
+        return 0;
+    }
+
+    for (j = 0; j < max; ++j) {
+        if (!strcmp(lang,
+                    ((char **) (variant->content_languages->elts))[j])) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 static int best_match(negotiation_state *neg, var_rec **pbest)
 {
     int j;
-    var_rec *best = NULL;
+    var_rec *best;
     float bestq = 0.0f;
     enum algorithm_results algorithm_result;
 
     var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
 
+    const char *preferred_language = apr_table_get(neg->r->subprocess_env,
+                                                   "prefer-language");
+
     set_default_lang_quality(neg);
 
     /*
      * Find the 'best' variant 
+     * We run the loop possibly twice: if "prefer-language"
+     * environment variable is set but we did not find an appropriate
+     * best variant. In that case forget the preferred language and
+     * negotiate over all variants.
      */
+    
+    do {
+        best = NULL;
 
     for (j = 0; j < neg->avail_vars->nelts; ++j) {
         var_rec *variant = &avail_recs[j];
 
+        /* if a language is preferred, but the current variant
+         * is not in that language, then drop it for now
+         */
+        if (   preferred_language
+            && !variant_has_language(variant, preferred_language)) {
+            continue;
+        }
+
         /* Find all the relevant 'quality' values from the
          * Accept... headers, and store in the variant.  This also
          * prepares for sending an Alternates header etc so we need to
@@ -2253,6 +2295,15 @@
         /* calculate result for Apache negotiation algorithm */
         algorithm_result = bestq > 0 ? alg_choice : alg_list;        
     }
+
+    /* run the loop again, if the "prefer-language" got no clear result */
+    if (preferred_language && (!best || algorithm_result != alg_choice)) {
+        preferred_language = NULL;
+        continue;
+    }
+
+    break;
+    } while (1);
 
     /* Returning a choice response with a non-neighboring variant is a
      * protocol security error in TCN (see rfc2295).  We do *not*

Reply via email to