1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * mod_negotiation.c: keeps track of MIME types the client is willing to
19 * accept, and contains code to handle type arbitration.
25 #include "apr_strings.h"
26 #include "apr_file_io.h"
29 #define APR_WANT_STRFUNC
32 #include "ap_config.h"
34 #include "http_config.h"
35 #include "http_request.h"
36 #include "http_protocol.h"
37 #include "http_core.h"
39 #include "util_script.h"
42 #define MAP_FILE_MAGIC_TYPE "application/x-type-map"
44 /* Commands --- configuring document caching on a per (virtual?)
49 int forcelangpriority
;
50 apr_array_header_t
*language_priority
;
53 /* forcelangpriority flags
55 #define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
56 #define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
57 #define FLP_PREFER 2 /* Use language_priority rather than MC */
58 #define FLP_FALLBACK 4 /* Use language_priority rather than NA */
60 #define FLP_DEFAULT FLP_PREFER
64 #define DISCARD_ALL_ENCODINGS 1 /* no-gzip */
65 #define DISCARD_ALL_BUT_HTML 2 /* gzip-only-text/html */
67 module AP_MODULE_DECLARE_DATA negotiation_module
;
69 static void *create_neg_dir_config(apr_pool_t
*p
, char *dummy
)
71 neg_dir_config
*new = (neg_dir_config
*) apr_palloc(p
,
72 sizeof(neg_dir_config
));
74 new->forcelangpriority
= FLP_UNDEF
;
75 new->language_priority
= NULL
;
79 static void *merge_neg_dir_configs(apr_pool_t
*p
, void *basev
, void *addv
)
81 neg_dir_config
*base
= (neg_dir_config
*) basev
;
82 neg_dir_config
*add
= (neg_dir_config
*) addv
;
83 neg_dir_config
*new = (neg_dir_config
*) apr_palloc(p
,
84 sizeof(neg_dir_config
));
86 /* give priority to the config in the subdirectory */
87 new->forcelangpriority
= (add
->forcelangpriority
!= FLP_UNDEF
)
88 ? add
->forcelangpriority
89 : base
->forcelangpriority
;
90 new->language_priority
= add
->language_priority
91 ? add
->language_priority
92 : base
->language_priority
;
96 static const char *set_language_priority(cmd_parms
*cmd
, void *n_
,
99 neg_dir_config
*n
= n_
;
102 if (!n
->language_priority
)
103 n
->language_priority
= apr_array_make(cmd
->pool
, 4, sizeof(char *));
105 langp
= (const char **) apr_array_push(n
->language_priority
);
110 static const char *set_force_priority(cmd_parms
*cmd
, void *n_
, const char *w
)
112 neg_dir_config
*n
= n_
;
114 if (!strcasecmp(w
, "None")) {
115 if (n
->forcelangpriority
& ~FLP_NONE
) {
116 return "Cannot combine ForceLanguagePriority options with None";
118 n
->forcelangpriority
= FLP_NONE
;
120 else if (!strcasecmp(w
, "Prefer")) {
121 if (n
->forcelangpriority
& FLP_NONE
) {
122 return "Cannot combine ForceLanguagePriority options None and "
125 n
->forcelangpriority
|= FLP_PREFER
;
127 else if (!strcasecmp(w
, "Fallback")) {
128 if (n
->forcelangpriority
& FLP_NONE
) {
129 return "Cannot combine ForceLanguagePriority options None and "
132 n
->forcelangpriority
|= FLP_FALLBACK
;
135 return apr_pstrcat(cmd
->pool
, "Invalid ForceLanguagePriority option ",
142 static const char *cache_negotiated_docs(cmd_parms
*cmd
, void *dummy
,
145 ap_set_module_config(cmd
->server
->module_config
, &negotiation_module
,
146 (arg
? "Cache" : NULL
));
150 static int do_cache_negotiated_docs(server_rec
*s
)
152 return (ap_get_module_config(s
->module_config
,
153 &negotiation_module
) != NULL
);
156 static const command_rec negotiation_cmds
[] =
158 AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs
, NULL
, RSRC_CONF
,
159 "Either 'on' or 'off' (default)"),
160 AP_INIT_ITERATE("LanguagePriority", set_language_priority
, NULL
,
162 "space-delimited list of MIME language abbreviations"),
163 AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority
, NULL
,
165 "Force LanguagePriority elections, either None, or "
166 "Fallback and/or Prefer"),
171 * Record of available info on a media type specified by the client
172 * (we also use 'em for encodings and languages)
175 typedef struct accept_rec
{
176 char *name
; /* MUST be lowercase */
179 char *charset
; /* for content-type only */
183 * Record of available info on a particular variant
185 * Note that a few of these fields are updated by the actual negotiation
188 * level_matched --- initialized to zero. Set to the value of level
189 * if the client actually accepts this media type at that
190 * level (and *not* if it got in on a wildcard). See level_cmp
192 * mime_stars -- initialized to zero. Set to the number of stars
193 * present in the best matching Accept header element.
194 * 1 for star/star, 2 for type/star and 3 for
197 * definite -- initialized to 1. Set to 0 if there is a match which
198 * makes the variant non-definite according to the rules
202 typedef struct var_rec
{
203 request_rec
*sub_req
; /* May be NULL (is, for map files) */
204 const char *mime_type
; /* MUST be lowercase */
205 const char *file_name
; /* Set to 'this' (for map file body content) */
206 apr_off_t body
; /* Only for map file body content */
207 const char *content_encoding
;
208 apr_array_header_t
*content_languages
; /* list of lang. for this variant */
209 const char *content_charset
;
210 const char *description
;
212 /* The next five items give the quality values for the dimensions
213 * of negotiation for this variant. They are obtained from the
214 * appropriate header lines, except for source_quality, which
215 * is obtained from the variant itself (the 'qs' parameter value
216 * from the variant's mime-type). Apart from source_quality,
217 * these values are set when we find the quality for each variant
218 * (see best_match()). source_quality is set from the 'qs' parameter
219 * of the variant description or mime type: see set_mime_fields().
221 float lang_quality
; /* quality of this variant's language */
222 float encoding_quality
; /* ditto encoding */
223 float charset_quality
; /* ditto charset */
224 float mime_type_quality
; /* ditto media type */
225 float source_quality
; /* source quality for this variant */
227 /* Now some special values */
228 float level
; /* Auxiliary to content-type... */
229 apr_off_t bytes
; /* content length, if known */
230 int lang_index
; /* Index into LanguagePriority list */
231 int is_pseudo_html
; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
233 /* Above are all written-once properties of the variant. The
234 * three fields below are changed during negotiation:
242 /* Something to carry around the state of negotiation (and to keep
243 * all of this thread-safe)...
249 neg_dir_config
*conf
;
251 int accept_q
; /* 1 if an Accept item has a q= param */
252 float default_lang_quality
; /* fiddle lang q for variants with no lang */
254 /* the array pointers below are NULL if the corresponding accept
255 * headers are not present
257 apr_array_header_t
*accepts
; /* accept_recs */
258 apr_array_header_t
*accept_encodings
; /* accept_recs */
259 apr_array_header_t
*accept_charsets
; /* accept_recs */
260 apr_array_header_t
*accept_langs
; /* accept_recs */
262 apr_array_header_t
*avail_vars
; /* available variants */
264 int count_multiviews_variants
; /* number of variants found on disk */
266 int is_transparent
; /* 1 if this resource is trans. negotiable */
268 int dont_fiddle_headers
; /* 1 if we may not fiddle with accept hdrs */
269 int ua_supports_trans
; /* 1 if ua supports trans negotiation */
270 int send_alternates
; /* 1 if we want to send an Alternates header */
271 int may_choose
; /* 1 if we may choose a variant for the client */
272 int use_rvsa
; /* 1 if we must use RVSA/1.0 negotiation algo */
275 /* A few functions to manipulate var_recs.
276 * Cleaning out the fields...
279 static void clean_var_rec(var_rec
*mime_info
)
281 mime_info
->sub_req
= NULL
;
282 mime_info
->mime_type
= "";
283 mime_info
->file_name
= "";
285 mime_info
->content_encoding
= NULL
;
286 mime_info
->content_languages
= NULL
;
287 mime_info
->content_charset
= "";
288 mime_info
->description
= "";
290 mime_info
->is_pseudo_html
= 0;
291 mime_info
->level
= 0.0f
;
292 mime_info
->level_matched
= 0.0f
;
293 mime_info
->bytes
= -1;
294 mime_info
->lang_index
= -1;
295 mime_info
->mime_stars
= 0;
296 mime_info
->definite
= 1;
298 mime_info
->charset_quality
= 1.0f
;
299 mime_info
->encoding_quality
= 1.0f
;
300 mime_info
->lang_quality
= 1.0f
;
301 mime_info
->mime_type_quality
= 1.0f
;
302 mime_info
->source_quality
= 0.0f
;
305 /* Initializing the relevant fields of a variant record from the
306 * accept_info read out of its content-type, one way or another.
309 static void set_mime_fields(var_rec
*var
, accept_rec
*mime_info
)
311 var
->mime_type
= mime_info
->name
;
312 var
->source_quality
= mime_info
->quality
;
313 var
->level
= mime_info
->level
;
314 var
->content_charset
= mime_info
->charset
;
316 var
->is_pseudo_html
= (!strcmp(var
->mime_type
, "text/html")
317 || !strcmp(var
->mime_type
, INCLUDES_MAGIC_TYPE
)
318 || !strcmp(var
->mime_type
, INCLUDES_MAGIC_TYPE3
));
321 /* Create a variant list validator in r using info from vlistr. */
323 static void set_vlist_validator(request_rec
*r
, request_rec
*vlistr
)
325 /* Calculating the variant list validator is similar to
326 * calculating an etag for the source of the variant list
327 * information, so we use ap_make_etag(). Note that this
328 * validator can be 'weak' in extreme case.
330 ap_update_mtime(vlistr
, vlistr
->finfo
.mtime
);
331 r
->vlist_validator
= ap_make_etag(vlistr
, 0);
333 /* ap_set_etag will later take r->vlist_validator into account
334 * when creating the etag header
339 /*****************************************************************
341 * Parsing (lists of) media types and their parameters, as seen in
342 * HTTPD header lines and elsewhere.
346 * parse quality value. atof(3) is not well-usable here, because it
347 * depends on the locale (argh).
349 * However, RFC 2616 states:
352 * [...] HTTP/1.1 applications MUST NOT generate more than three digits
353 * after the decimal point. User configuration of these values SHOULD also
354 * be limited in this fashion.
356 * qvalue = ( "0" [ "." 0*3DIGIT ] )
357 * | ( "1" [ "." 0*3("0") ] )
359 * This is quite easy. If the supplied string doesn't match the above
360 * definition (loosely), we simply return 1 (same as if there's no qvalue)
363 static float atoq(const char *string
)
365 if (!string
|| !*string
) {
369 while (*string
&& apr_isspace(*string
)) {
373 /* be tolerant and accept qvalues without leading zero
374 * (also for backwards compat, where atof() was in use)
376 if (*string
!= '.' && *string
++ != '0') {
380 if (*string
== '.') {
381 /* better only one division later, than dealing with fscking
382 * IEEE format 0.1 factors ...
386 if (*++string
>= '0' && *string
<= '9') {
387 i
+= (*string
- '0') * 100;
389 if (*++string
>= '0' && *string
<= '9') {
390 i
+= (*string
- '0') * 10;
392 if (*++string
> '0' && *string
<= '9') {
393 i
+= (*string
- '0');
398 return (float)i
/ 1000.0f
;
405 * Get a single mime type entry --- one media type and parameters;
406 * enter the values we recognize into the argument accept_rec
409 static const char *get_entry(apr_pool_t
*p
, accept_rec
*result
,
410 const char *accept_line
)
412 result
->quality
= 1.0f
;
413 result
->level
= 0.0f
;
414 result
->charset
= "";
417 * Note that this handles what I gather is the "old format",
419 * Accept: text/html text/plain moo/zot
421 * without any compatibility kludges --- if the token after the
422 * MIME type begins with a semicolon, we know we're looking at parms,
423 * otherwise, we know we aren't. (So why all the pissing and moaning
424 * in the CERN server code? I must be missing something).
427 result
->name
= ap_get_token(p
, &accept_line
, 0);
428 ap_str_tolower(result
->name
); /* You want case insensitive,
429 * you'll *get* case insensitive.
432 /* KLUDGE!!! Default HTML to level 2.0 unless the browser
433 * *explicitly* says something else.
436 if (!strcmp(result
->name
, "text/html") && (result
->level
== 0.0)) {
437 result
->level
= 2.0f
;
439 else if (!strcmp(result
->name
, INCLUDES_MAGIC_TYPE
)) {
440 result
->level
= 2.0f
;
442 else if (!strcmp(result
->name
, INCLUDES_MAGIC_TYPE3
)) {
443 result
->level
= 3.0f
;
446 while (*accept_line
== ';') {
454 parm
= ap_get_token(p
, &accept_line
, 1);
456 /* Look for 'var = value' --- and make sure the var is in lcase. */
458 for (cp
= parm
; (*cp
&& !apr_isspace(*cp
) && *cp
!= '='); ++cp
) {
459 *cp
= apr_tolower(*cp
);
463 continue; /* No '='; just ignore it. */
466 *cp
++ = '\0'; /* Delimit var */
467 while (*cp
&& (apr_isspace(*cp
) || *cp
== '=')) {
474 (*end
&& *end
!= '\n' && *end
!= '\r' && *end
!= '\"');
478 for (end
= cp
; (*end
&& !apr_isspace(*end
)); end
++);
481 *end
= '\0'; /* strip ending quote or return */
486 && (parm
[1] == '\0' || (parm
[1] == 's' && parm
[2] == '\0'))) {
487 result
->quality
= atoq(cp
);
489 else if (parm
[0] == 'l' && !strcmp(&parm
[1], "evel")) {
490 result
->level
= (float)atoi(cp
);
492 else if (!strcmp(parm
, "charset")) {
493 result
->charset
= cp
;
497 if (*accept_line
== ',') {
504 /*****************************************************************
506 * Dealing with header lines ...
508 * Accept, Accept-Charset, Accept-Language and Accept-Encoding
509 * are handled by do_header_line() - they all have the same
510 * basic structure of a list of items of the format
511 * name; q=N; charset=TEXT
513 * where charset is only valid in Accept.
516 static apr_array_header_t
*do_header_line(apr_pool_t
*p
,
517 const char *accept_line
)
519 apr_array_header_t
*accept_recs
;
525 accept_recs
= apr_array_make(p
, 40, sizeof(accept_rec
));
527 while (*accept_line
) {
528 accept_rec
*new = (accept_rec
*) apr_array_push(accept_recs
);
529 accept_line
= get_entry(p
, new, accept_line
);
535 /* Given the text of the Content-Languages: line from the var map file,
536 * return an array containing the languages of this variant
539 static apr_array_header_t
*do_languages_line(apr_pool_t
*p
,
540 const char **lang_line
)
542 apr_array_header_t
*lang_recs
= apr_array_make(p
, 2, sizeof(char *));
548 while (**lang_line
) {
549 char **new = (char **) apr_array_push(lang_recs
);
550 *new = ap_get_token(p
, lang_line
, 0);
551 ap_str_tolower(*new);
552 if (**lang_line
== ',' || **lang_line
== ';') {
560 /*****************************************************************
562 * Handling header lines from clients...
565 static negotiation_state
*parse_accept_headers(request_rec
*r
)
567 negotiation_state
*new =
568 (negotiation_state
*) apr_pcalloc(r
->pool
, sizeof(negotiation_state
));
570 apr_table_t
*hdrs
= r
->headers_in
;
575 new->conf
= (neg_dir_config
*)ap_get_module_config(r
->per_dir_config
,
576 &negotiation_module
);
578 new->dir_name
= ap_make_dirstr_parent(r
->pool
, r
->filename
);
580 new->accepts
= do_header_line(r
->pool
, apr_table_get(hdrs
, "Accept"));
582 /* calculate new->accept_q value */
584 elts
= (accept_rec
*) new->accepts
->elts
;
586 for (i
= 0; i
< new->accepts
->nelts
; ++i
) {
587 if (elts
[i
].quality
< 1.0) {
593 new->accept_encodings
=
594 do_header_line(r
->pool
, apr_table_get(hdrs
, "Accept-Encoding"));
596 do_header_line(r
->pool
, apr_table_get(hdrs
, "Accept-Language"));
597 new->accept_charsets
=
598 do_header_line(r
->pool
, apr_table_get(hdrs
, "Accept-Charset"));
600 /* This is possibly overkill for some servers, heck, we have
601 * only 33 index.html variants in docs/docroot (today).
602 * Make this configurable?
604 new->avail_vars
= apr_array_make(r
->pool
, 40, sizeof(var_rec
));
610 static void parse_negotiate_header(request_rec
*r
, negotiation_state
*neg
)
612 const char *negotiate
= apr_table_get(r
->headers_in
, "Negotiate");
615 /* First, default to no TCN, no Alternates, and the original Apache
616 * negotiation algorithm with fiddles for broken browser configs.
618 * To save network bandwidth, we do not configure to send an
619 * Alternates header to the user agent by default. User
620 * agents that want an Alternates header for agent-driven
621 * negotiation will have to request it by sending an
622 * appropriate Negotiate header.
624 neg
->ua_supports_trans
= 0;
625 neg
->send_alternates
= 0;
628 neg
->dont_fiddle_headers
= 0;
633 if (strcmp(negotiate
, "trans") == 0) {
634 /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
635 * do not support transparent content negotiation, so for Lynx we
636 * ignore the negotiate header when its contents are exactly "trans".
637 * If future versions of Lynx ever need to say 'negotiate: trans',
638 * they can send the equivalent 'negotiate: trans, trans' instead
639 * to avoid triggering the workaround below.
641 const char *ua
= apr_table_get(r
->headers_in
, "User-Agent");
643 if (ua
&& (strncmp(ua
, "Lynx", 4) == 0))
647 neg
->may_choose
= 0; /* An empty Negotiate would require 300 response */
649 while ((tok
= ap_get_list_item(neg
->pool
, &negotiate
)) != NULL
) {
651 if (strcmp(tok
, "trans") == 0 ||
652 strcmp(tok
, "vlist") == 0 ||
653 strcmp(tok
, "guess-small") == 0 ||
654 apr_isdigit(tok
[0]) ||
655 strcmp(tok
, "*") == 0) {
657 /* The user agent supports transparent negotiation */
658 neg
->ua_supports_trans
= 1;
660 /* Send-alternates could be configurable, but note
661 * that it must be 1 if we have 'vlist' in the
664 neg
->send_alternates
= 1;
666 if (strcmp(tok
, "1.0") == 0) {
667 /* we may use the RVSA/1.0 algorithm, configure for it */
670 neg
->dont_fiddle_headers
= 1;
672 else if (tok
[0] == '*') {
673 /* we may use any variant selection algorithm, configure
674 * to use the Apache algorithm
678 /* We disable header fiddles on the assumption that a
679 * client sending Negotiate knows how to send correct
680 * headers which don't need fiddling.
682 neg
->dont_fiddle_headers
= 1;
688 ap_log_error(APLOG_MARK
, APLOG_STARTUP
, 0, NULL
,
689 "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
690 "send_alternates=%d, may_choose=%d",
691 neg
->dont_fiddle_headers
, neg
->use_rvsa
,
692 neg
->ua_supports_trans
, neg
->send_alternates
, neg
->may_choose
);
697 /* Sometimes clients will give us no Accept info at all; this routine sets
698 * up the standard default for that case, and also arranges for us to be
699 * willing to run a CGI script if we find one. (In fact, we set up to
700 * dramatically prefer CGI scripts in cases where that's appropriate,
701 * e.g., POST or when URI includes query args or extra path info).
703 static void maybe_add_default_accepts(negotiation_state
*neg
,
706 accept_rec
*new_accept
;
709 neg
->accepts
= apr_array_make(neg
->pool
, 4, sizeof(accept_rec
));
711 new_accept
= (accept_rec
*) apr_array_push(neg
->accepts
);
713 new_accept
->name
= "*/*";
714 new_accept
->quality
= 1.0f
;
715 new_accept
->level
= 0.0f
;
718 new_accept
= (accept_rec
*) apr_array_push(neg
->accepts
);
720 new_accept
->name
= CGI_MAGIC_TYPE
;
722 new_accept
->quality
= 0;
725 new_accept
->quality
= prefer_scripts
? 2.0f
: 0.001f
;
727 new_accept
->level
= 0.0f
;
730 /*****************************************************************
732 * Parsing type-map files, in Roy's meta/http format augmented with
736 /* Reading RFC822-style header lines, ignoring #-comments and
737 * handling continuations.
741 header_eof
, header_seen
, header_sep
744 static enum header_state
get_header_line(char *buffer
, int len
, apr_file_t
*map
)
746 char *buf_end
= buffer
+ len
;
750 /* Get a noncommented line */
753 if (apr_file_gets(buffer
, MAX_STRING_LEN
, map
) != APR_SUCCESS
) {
756 } while (buffer
[0] == '#');
758 /* If blank, just return it --- this ends information on this variant */
760 for (cp
= buffer
; (*cp
&& apr_isspace(*cp
)); ++cp
) {
768 /* If non-blank, go looking for header lines, but note that we still
769 * have to treat comments specially...
774 /* We need to shortcut the rest of this block following the Body:
775 * tag - we will not look for continutation after this line.
777 if (!strncasecmp(buffer
, "Body:", 5))
780 while (apr_file_getc(&c
, map
) != APR_EOF
) {
783 while (apr_file_getc(&c
, map
) != APR_EOF
&& c
!= '\n') {
787 else if (apr_isspace(c
)) {
788 /* Leading whitespace. POSSIBLE continuation line
789 * Also, possibly blank --- if so, we ungetc() the final newline
790 * so that we will pick up the blank line the next time 'round.
793 while (c
!= '\n' && apr_isspace(c
)) {
794 if(apr_file_getc(&c
, map
) != APR_SUCCESS
)
798 apr_file_ungetc(c
, map
);
801 return header_seen
; /* Blank line */
806 while ( cp
< buf_end
- 2
807 && (apr_file_getc(&c
, map
)) != APR_EOF
817 /* Line beginning with something other than whitespace */
819 apr_file_ungetc(c
, map
);
827 static apr_off_t
get_body(char *buffer
, apr_size_t
*len
, const char *tag
,
835 taglen
= strlen(tag
);
838 /* We are at the first character following a body:tag\n entry
839 * Suck in the body, then backspace to the first char after the
840 * closing tag entry. If we fail to read, find the tag or back
841 * up then we have a hosed file, so give up already
843 if (apr_file_read(map
, buffer
, len
) != APR_SUCCESS
) {
847 /* put a copy of the tag *after* the data read from the file
848 * so that strstr() will find something with no reliance on
851 memcpy(buffer
+ *len
, tag
, taglen
);
852 endbody
= strstr(buffer
, tag
);
853 if (endbody
== buffer
+ *len
) {
856 bodylen
= endbody
- buffer
;
858 /* Skip all the trailing cruft after the end tag to the next line */
860 if (*endbody
== '\n') {
867 pos
= -(apr_off_t
)(*len
- (endbody
- buffer
));
868 if (apr_file_seek(map
, APR_CUR
, &pos
) != APR_SUCCESS
) {
872 /* Give the caller back the actual body's file offset and length */
874 return pos
- (endbody
- buffer
);
878 /* Stripping out RFC822 comments */
880 static void strip_paren_comments(char *hdr
)
882 /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
883 /* Nope, it isn't correct. Fails to handle backslash escape as well. */
887 hdr
= strchr(hdr
, '"');
893 else if (*hdr
== '(') {
894 while (*hdr
&& *hdr
!= ')') {
908 /* Getting to a header body from the header */
910 static char *lcase_header_name_return_body(char *header
, request_rec
*r
)
914 for ( ; *cp
&& *cp
!= ':' ; ++cp
) {
915 *cp
= apr_tolower(*cp
);
919 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
920 "Syntax error in type map, no ':' in %s for header %s",
921 r
->filename
, header
);
927 } while (*cp
&& apr_isspace(*cp
));
930 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
931 "Syntax error in type map --- no header body: %s for %s",
932 r
->filename
, header
);
939 static int read_type_map(apr_file_t
**map
, negotiation_state
*neg
,
942 request_rec
*r
= neg
->r
;
943 apr_file_t
*map_
= NULL
;
945 char buffer
[MAX_STRING_LEN
];
946 enum header_state hstate
;
947 struct var_rec mime_info
;
953 /* We are not using multiviews */
954 neg
->count_multiviews_variants
= 0;
956 if ((status
= apr_file_open(map
, rr
->filename
, APR_READ
| APR_BUFFERED
,
957 APR_OS_DEFAULT
, neg
->pool
)) != APR_SUCCESS
) {
958 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, status
, r
,
959 "cannot access type map file: %s", rr
->filename
);
960 if (APR_STATUS_IS_ENOTDIR(status
) || APR_STATUS_IS_ENOENT(status
)) {
961 return HTTP_NOT_FOUND
;
964 return HTTP_FORBIDDEN
;
968 clean_var_rec(&mime_info
);
972 hstate
= get_header_line(buffer
, MAX_STRING_LEN
, *map
);
974 if (hstate
== header_seen
) {
975 char *body1
= lcase_header_name_return_body(buffer
, neg
->r
);
979 return HTTP_INTERNAL_SERVER_ERROR
;
982 strip_paren_comments(body1
);
985 if (!strncmp(buffer
, "uri:", 4)) {
986 mime_info
.file_name
= ap_get_token(neg
->pool
, &body
, 0);
988 else if (!strncmp(buffer
, "content-type:", 13)) {
989 struct accept_rec accept_info
;
991 get_entry(neg
->pool
, &accept_info
, body
);
992 set_mime_fields(&mime_info
, &accept_info
);
995 else if (!strncmp(buffer
, "content-length:", 15)) {
999 if (apr_strtoff(&number
, body
, &errp
, 10)
1000 || *errp
|| number
< 0) {
1001 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
1002 "Parse error in type map, Content-Length: "
1003 "'%s' in %s is invalid.",
1007 mime_info
.bytes
= number
;
1010 else if (!strncmp(buffer
, "content-language:", 17)) {
1011 mime_info
.content_languages
= do_languages_line(neg
->pool
,
1015 else if (!strncmp(buffer
, "content-encoding:", 17)) {
1016 mime_info
.content_encoding
= ap_get_token(neg
->pool
, &body
, 0);
1019 else if (!strncmp(buffer
, "description:", 12)) {
1020 char *desc
= apr_pstrdup(neg
->pool
, body
);
1023 for (cp
= desc
; *cp
; ++cp
) {
1024 if (*cp
=='\n') *cp
=' ';
1026 if (cp
>desc
) *(cp
-1)=0;
1027 mime_info
.description
= desc
;
1029 else if (!strncmp(buffer
, "body:", 5)) {
1030 char *tag
= apr_pstrdup(neg
->pool
, body
);
1031 char *eol
= strchr(tag
, '\0');
1032 apr_size_t len
= MAX_STRING_LEN
;
1033 while (--eol
>= tag
&& apr_isspace(*eol
))
1035 if ((mime_info
.body
= get_body(buffer
, &len
, tag
, *map
)) < 0) {
1036 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
1037 "Syntax error in type map, no end tag '%s'"
1038 "found in %s for Body: content.",
1042 mime_info
.bytes
= len
;
1043 mime_info
.file_name
= apr_filepath_name_get(rr
->filename
);
1047 if (*mime_info
.file_name
&& has_content
) {
1048 void *new_var
= apr_array_push(neg
->avail_vars
);
1050 memcpy(new_var
, (void *) &mime_info
, sizeof(var_rec
));
1053 clean_var_rec(&mime_info
);
1056 } while (hstate
!= header_eof
);
1059 apr_file_close(map_
);
1061 set_vlist_validator(r
, rr
);
1067 /* Sort function used by read_types_multi. */
1068 static int variantsortf(var_rec
*a
, var_rec
*b
) {
1070 /* First key is the source quality, sort in descending order. */
1072 /* XXX: note that we currently implement no method of setting the
1073 * source quality for multiviews variants, so we are always comparing
1074 * 1.0 to 1.0 for now
1076 if (a
->source_quality
< b
->source_quality
)
1078 if (a
->source_quality
> b
->source_quality
)
1081 /* Second key is the variant name */
1082 return strcmp(a
->file_name
, b
->file_name
);
1085 /*****************************************************************
1087 * Same as read_type_map, except we use a filtered directory listing
1091 static int read_types_multi(negotiation_state
*neg
)
1093 request_rec
*r
= neg
->r
;
1099 apr_status_t status
;
1100 struct var_rec mime_info
;
1101 struct accept_rec accept_info
;
1105 clean_var_rec(&mime_info
);
1107 if (r
->proxyreq
|| !r
->filename
1108 || !ap_os_is_path_absolute(neg
->pool
, r
->filename
)) {
1112 /* Only absolute paths here */
1113 if (!(filp
= strrchr(r
->filename
, '/'))) {
1117 prefix_len
= strlen(filp
);
1119 if ((status
= apr_dir_open(&dirp
, neg
->dir_name
,
1120 neg
->pool
)) != APR_SUCCESS
) {
1121 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, status
, r
,
1122 "cannot read directory for multi: %s", neg
->dir_name
);
1123 return HTTP_FORBIDDEN
;
1126 while (apr_dir_read(&dirent
, APR_FINFO_DIRENT
, dirp
) == APR_SUCCESS
) {
1127 apr_array_header_t
*exception_list
;
1128 request_rec
*sub_req
;
1130 /* Do we have a match? */
1131 #ifdef CASE_BLIND_FILESYSTEM
1132 if (strncasecmp(dirent
.name
, filp
, prefix_len
)) {
1134 if (strncmp(dirent
.name
, filp
, prefix_len
)) {
1138 if (dirent
.name
[prefix_len
] != '.') {
1142 /* Don't negotiate directories and other unusual files
1143 * Really shouldn't see anything but DIR/LNK/REG here,
1144 * and we aught to discover if the LNK was interesting.
1146 * Of course, this only helps platforms that capture the
1147 * the filetype in apr_dir_read(), which most can once
1148 * they are optimized with some magic [it's known to the
1149 * dirent, not associated to the inode, on most FS's.]
1151 if ((dirent
.valid
& APR_FINFO_TYPE
) && (dirent
.filetype
== APR_DIR
))
1154 /* Ok, something's here. Maybe nothing useful. Remember that
1155 * we tried, if we completely fail, so we can reject the request!
1159 /* See if it's something which we have access to, and which
1160 * has a known type and encoding.
1162 sub_req
= ap_sub_req_lookup_dirent(&dirent
, r
, AP_SUBREQ_MERGE_ARGS
,
1165 /* Double check, we still don't multi-resolve non-ordinary files
1167 if (sub_req
->finfo
.filetype
!= APR_REG
) {
1168 /* XXX sub req not destroyed -- may be a bug/unintentional ? */
1172 /* If it has a handler, we'll pretend it's a CGI script,
1173 * since that's a good indication of the sort of thing it
1176 if (sub_req
->handler
&& !sub_req
->content_type
) {
1177 ap_set_content_type(sub_req
, CGI_MAGIC_TYPE
);
1181 * mod_mime will _always_ provide us the base name in the
1182 * ap-mime-exception-list, if it processed anything. If
1183 * this list is empty, give up immediately, there was
1184 * nothing interesting. For example, looking at the files
1185 * readme.txt and readme.foo, we will throw away .foo if
1186 * it's an insignificant file (e.g. did not identify a
1187 * language, charset, encoding, content type or handler,)
1190 (apr_array_header_t
*)apr_table_get(sub_req
->notes
,
1191 "ap-mime-exceptions-list");
1193 if (!exception_list
) {
1194 ap_destroy_sub_req(sub_req
);
1198 /* Each unregonized bit better match our base name, in sequence.
1199 * A test of index.html.foo will match index.foo or index.html.foo,
1200 * but it will never transpose the segments and allow index.foo.html
1201 * because that would introduce too much CPU consumption. Better that
1202 * we don't attempt a many-to-many match here.
1205 int nexcept
= exception_list
->nelts
;
1206 char **cur_except
= (char**)exception_list
->elts
;
1207 char *segstart
= filp
, *segend
, saveend
;
1209 while (*segstart
&& nexcept
) {
1210 if (!(segend
= strchr(segstart
, '.')))
1211 segend
= strchr(segstart
, '\0');
1215 #ifdef CASE_BLIND_FILESYSTEM
1216 if (strcasecmp(segstart
, *cur_except
) == 0) {
1218 if (strcmp(segstart
, *cur_except
) == 0) {
1228 segstart
= segend
+ 1;
1232 /* Something you don't know is, something you don't know...
1234 ap_destroy_sub_req(sub_req
);
1240 * If we failed the subrequest, or don't
1241 * know what we are serving, then continue.
1243 if (sub_req
->status
!= HTTP_OK
|| (!sub_req
->content_type
)) {
1244 ap_destroy_sub_req(sub_req
);
1248 /* If it's a map file, we use that instead of the map
1251 if (((sub_req
->content_type
) &&
1252 !strcmp(sub_req
->content_type
, MAP_FILE_MAGIC_TYPE
)) ||
1253 ((sub_req
->handler
) &&
1254 !strcmp(sub_req
->handler
, "type-map"))) {
1256 apr_dir_close(dirp
);
1257 neg
->avail_vars
->nelts
= 0;
1258 if (sub_req
->status
!= HTTP_OK
) {
1259 return sub_req
->status
;
1261 return read_type_map(NULL
, neg
, sub_req
);
1264 /* Have reasonable variant --- gather notes. */
1266 mime_info
.sub_req
= sub_req
;
1267 mime_info
.file_name
= apr_pstrdup(neg
->pool
, dirent
.name
);
1268 if (sub_req
->content_encoding
) {
1269 mime_info
.content_encoding
= sub_req
->content_encoding
;
1271 if (sub_req
->content_languages
) {
1272 mime_info
.content_languages
= sub_req
->content_languages
;
1275 get_entry(neg
->pool
, &accept_info
, sub_req
->content_type
);
1276 set_mime_fields(&mime_info
, &accept_info
);
1278 new_var
= apr_array_push(neg
->avail_vars
);
1279 memcpy(new_var
, (void *) &mime_info
, sizeof(var_rec
));
1281 neg
->count_multiviews_variants
++;
1283 clean_var_rec(&mime_info
);
1286 apr_dir_close(dirp
);
1288 /* We found some file names that matched. None could be served.
1289 * Rather than fall out to autoindex or some other mapper, this
1292 if (anymatch
&& !neg
->avail_vars
->nelts
) {
1293 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
1294 "Negotiation: discovered file(s) matching request: %s"
1295 " (None could be negotiated).",
1297 return HTTP_NOT_FOUND
;
1300 set_vlist_validator(r
, r
);
1302 /* Sort the variants into a canonical order. The negotiation
1303 * result sometimes depends on the order of the variants. By
1304 * sorting the variants into a canonical order, rather than using
1305 * the order in which readdir() happens to return them, we ensure
1306 * that the negotiation result will be consistent over filesystem
1307 * backup/restores and over all mirror sites.
1310 qsort((void *) neg
->avail_vars
->elts
, neg
->avail_vars
->nelts
,
1311 sizeof(var_rec
), (int (*)(const void *, const void *)) variantsortf
);
1317 /*****************************************************************
1318 * And now for the code you've been waiting for... actually
1319 * finding a match to the client's requirements.
1322 /* Matching MIME types ... the star/star and foo/star commenting conventions
1323 * are implemented here. (You know what I mean by star/star, but just
1324 * try mentioning those three characters in a C comment). Using strcmp()
1325 * is legit, because everything has already been smashed to lowercase.
1327 * Note also that if we get an exact match on the media type, we update
1328 * level_matched for use in level_cmp below...
1330 * We also give a value for mime_stars, which is used later. It should
1331 * be 1 for star/star, 2 for type/star and 3 for type/subtype.
1334 static int mime_match(accept_rec
*accept_r
, var_rec
*avail
)
1336 const char *accept_type
= accept_r
->name
;
1337 const char *avail_type
= avail
->mime_type
;
1338 int len
= strlen(accept_type
);
1340 if (accept_type
[0] == '*') { /* Anything matches star/star */
1341 if (avail
->mime_stars
< 1) {
1342 avail
->mime_stars
= 1;
1346 else if ((accept_type
[len
- 1] == '*') &&
1347 !strncmp(accept_type
, avail_type
, len
- 2)) {
1348 if (avail
->mime_stars
< 2) {
1349 avail
->mime_stars
= 2;
1353 else if (!strcmp(accept_type
, avail_type
)
1354 || (!strcmp(accept_type
, "text/html")
1355 && (!strcmp(avail_type
, INCLUDES_MAGIC_TYPE
)
1356 || !strcmp(avail_type
, INCLUDES_MAGIC_TYPE3
)))) {
1357 if (accept_r
->level
>= avail
->level
) {
1358 avail
->level_matched
= avail
->level
;
1359 avail
->mime_stars
= 3;
1367 /* This code implements a piece of the tie-breaking algorithm between
1368 * variants of equal quality. This piece is the treatment of variants
1369 * of the same base media type, but different levels. What we want to
1370 * return is the variant at the highest level that the client explicitly
1371 * claimed to accept.
1373 * If all the variants available are at a higher level than that, or if
1374 * the client didn't say anything specific about this media type at all
1375 * and these variants just got in on a wildcard, we prefer the lowest
1376 * level, on grounds that that's the one that the client is least likely
1379 * (This is all motivated by treatment of levels in HTML --- we only
1380 * want to give level 3 to browsers that explicitly ask for it; browsers
1381 * that don't, including HTTP/0.9 browsers that only get the implicit
1382 * "Accept: * / *" [space added to avoid confusing cpp --- no, that
1383 * syntax doesn't really work] should get HTML2 if available).
1385 * (Note that this code only comes into play when we are choosing among
1386 * variants of equal quality, where the draft standard gives us a fair
1387 * bit of leeway about what to do. It ain't specified by the standard;
1388 * rather, it is a choice made by this server about what to do in cases
1389 * where the standard does not specify a unique course of action).
1392 static int level_cmp(var_rec
*var1
, var_rec
*var2
)
1394 /* Levels are only comparable between matching media types */
1396 if (var1
->is_pseudo_html
&& !var2
->is_pseudo_html
) {
1400 if (!var1
->is_pseudo_html
&& strcmp(var1
->mime_type
, var2
->mime_type
)) {
1403 /* The result of the above if statements is that, if we get to
1404 * here, both variants have the same mime_type or both are
1408 /* Take highest level that matched, if either did match. */
1410 if (var1
->level_matched
> var2
->level_matched
) {
1413 if (var1
->level_matched
< var2
->level_matched
) {
1417 /* Neither matched. Take lowest level, if there's a difference. */
1419 if (var1
->level
< var2
->level
) {
1422 if (var1
->level
> var2
->level
) {
1431 /* Finding languages. The main entry point is set_language_quality()
1432 * which is called for each variant. It sets two elements in the
1434 * language_quality - the 'q' value of the 'best' matching language
1435 * from Accept-Language: header (HTTP/1.1)
1436 * lang_index - Non-negotiated language priority, using
1437 * position of language on the Accept-Language:
1438 * header, if present, else LanguagePriority
1441 * When we do the variant checking for best variant, we use language
1442 * quality first, and if a tie, language_index next (this only applies
1443 * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
1444 * algorithm, lang_index is never used.
1446 * set_language_quality() calls find_lang_index() and find_default_index()
1447 * to set lang_index.
1450 static int find_lang_index(apr_array_header_t
*accept_langs
, char *lang
)
1455 if (!lang
|| !accept_langs
) {
1459 alang
= (const char **) accept_langs
->elts
;
1461 for (i
= 0; i
< accept_langs
->nelts
; ++i
) {
1462 if (!strncmp(lang
, *alang
, strlen(*alang
))) {
1465 alang
+= (accept_langs
->elt_size
/ sizeof(char*));
1471 /* set_default_lang_quality() sets the quality we apply to variants
1472 * which have no language assigned to them. If none of the variants
1473 * have a language, we are not negotiating on language, so all are
1474 * acceptable, and we set the default q value to 1.0. However if
1475 * some of the variants have languages, we set this default to 0.0001.
1476 * The value of this default will be applied to all variants with
1477 * no explicit language -- which will have the effect of making them
1478 * acceptable, but only if no variants with an explicit language
1479 * are acceptable. The default q value set here is assigned to variants
1480 * with no language type in set_language_quality().
1482 * Note that if using the RVSA/1.0 algorithm, we don't use this
1486 static void set_default_lang_quality(negotiation_state
*neg
)
1488 var_rec
*avail_recs
= (var_rec
*) neg
->avail_vars
->elts
;
1491 if (!neg
->dont_fiddle_headers
) {
1492 for (j
= 0; j
< neg
->avail_vars
->nelts
; ++j
) {
1493 var_rec
*variant
= &avail_recs
[j
];
1494 if (variant
->content_languages
&&
1495 variant
->content_languages
->nelts
) {
1496 neg
->default_lang_quality
= 0.0001f
;
1502 neg
->default_lang_quality
= 1.0f
;
1505 /* Set the language_quality value in the variant record. Also
1506 * assigns lang_index for ForceLanguagePriority.
1508 * To find the language_quality value, we look for the 'q' value
1509 * of the 'best' matching language on the Accept-Language
1510 * header. The 'best' match is the language on Accept-Language
1511 * header which matches the language of this variant either fully,
1512 * or as far as the prefix marker (-). If two or more languages
1513 * match, use the longest string from the Accept-Language header
1514 * (see HTTP/1.1 [14.4])
1516 * When a variant has multiple languages, we find the 'best'
1517 * match for each variant language tag as above, then select the
1518 * one with the highest q value. Because both the accept-header
1519 * and variant can have multiple languages, we now have a hairy
1520 * loop-within-a-loop here.
1522 * If the variant has no language and we have no Accept-Language
1523 * items, leave the quality at 1.0 and return.
1525 * If the variant has no language, we use the default as set by
1526 * set_default_lang_quality() (1.0 if we are not negotiating on
1527 * language, 0.001 if we are).
1529 * Following the setting of the language quality, we drop through to
1530 * set the old 'lang_index'. This is set based on either the order
1531 * of the languages on the Accept-Language header, or the
1532 * order on the LanguagePriority directive. This is only used
1533 * in the negotiation if the language qualities tie.
1536 static void set_language_quality(negotiation_state
*neg
, var_rec
*variant
)
1538 int forcepriority
= neg
->conf
->forcelangpriority
;
1539 if (forcepriority
== FLP_UNDEF
) {
1540 forcepriority
= FLP_DEFAULT
;
1543 if (!variant
->content_languages
|| !variant
->content_languages
->nelts
) {
1544 /* This variant has no content-language, so use the default
1545 * quality factor for variants with no content-language
1546 * (previously set by set_default_lang_quality()).
1547 * Leave the factor alone (it remains at 1.0) when we may not fiddle
1550 if (!neg
->dont_fiddle_headers
) {
1551 variant
->lang_quality
= neg
->default_lang_quality
;
1553 if (!neg
->accept_langs
) {
1554 return; /* no accept-language header */
1559 /* Variant has one (or more) languages. Look for the best
1560 * match. We do this by going through each language on the
1561 * variant description looking for a match on the
1562 * Accept-Language header. The best match is the longest
1563 * matching language on the header. The final result is the
1564 * best q value from all the languages on the variant
1568 if (!neg
->accept_langs
) {
1569 /* no accept-language header makes the variant indefinite */
1570 variant
->definite
= 0;
1572 else { /* There is an accept-language with 0 or more items */
1573 accept_rec
*accs
= (accept_rec
*) neg
->accept_langs
->elts
;
1574 accept_rec
*best
= NULL
, *star
= NULL
;
1575 accept_rec
*bestthistag
;
1577 float fiddle_q
= 0.0f
;
1578 int any_match_on_star
= 0;
1580 apr_size_t alen
, longest_lang_range_len
;
1582 for (j
= 0; j
< variant
->content_languages
->nelts
; ++j
) {
1585 longest_lang_range_len
= 0;
1588 /* lang is the variant's language-tag, which is the one
1589 * we are allowed to use the prefix of in HTTP/1.1
1591 lang
= ((char **) (variant
->content_languages
->elts
))[j
];
1593 /* now find the best (i.e. longest) matching
1594 * Accept-Language header language. We put the best match
1595 * for this tag in bestthistag. We cannot update the
1596 * overall best (based on q value) because the best match
1597 * for this tag is the longest language item on the accept
1598 * header, not necessarily the highest q.
1600 for (i
= 0; i
< neg
->accept_langs
->nelts
; ++i
) {
1601 if (!strcmp(accs
[i
].name
, "*")) {
1607 /* Find language. We match if either the variant
1608 * language tag exactly matches the language range
1609 * from the accept header, or a prefix of the variant
1610 * language tag up to a '-' character matches the
1611 * whole of the language range in the Accept-Language
1612 * header. Note that HTTP/1.x allows any number of
1613 * '-' characters in a tag or range, currently only
1614 * tags with zero or one '-' characters are defined
1615 * for general use (see rfc1766).
1617 * We only use language range in the Accept-Language
1618 * header the best match for the variant language tag
1619 * if it is longer than the previous best match.
1622 alen
= strlen(accs
[i
].name
);
1624 if ((strlen(lang
) >= alen
) &&
1625 !strncmp(lang
, accs
[i
].name
, alen
) &&
1626 ((lang
[alen
] == 0) || (lang
[alen
] == '-')) ) {
1628 if (alen
> longest_lang_range_len
) {
1629 longest_lang_range_len
= alen
;
1630 bestthistag
= &accs
[i
];
1634 if (!bestthistag
&& !neg
->dont_fiddle_headers
) {
1635 /* The next bit is a fiddle. Some browsers might
1636 * be configured to send more specific language
1637 * ranges than desirable. For example, an
1638 * Accept-Language of en-US should never match
1639 * variants with languages en or en-GB. But US
1640 * English speakers might pick en-US as their
1641 * language choice. So this fiddle checks if the
1642 * language range has a prefix, and if so, it
1643 * matches variants which match that prefix with a
1644 * priority of 0.001. So a request for en-US would
1645 * match variants of types en and en-GB, but at
1646 * much lower priority than matches of en-US
1647 * directly, or of any other language listed on
1648 * the Accept-Language header. Note that this
1649 * fiddle does not handle multi-level prefixes.
1651 if ((p
= strchr(accs
[i
].name
, '-'))) {
1652 int plen
= p
- accs
[i
].name
;
1654 if (!strncmp(lang
, accs
[i
].name
, plen
)) {
1660 /* Finished looking at Accept-Language headers, the best
1661 * (longest) match is in bestthistag, or NULL if no match
1664 (bestthistag
&& bestthistag
->quality
> best
->quality
)) {
1668 /* See if the tag matches on a * in the Accept-Language
1669 * header. If so, record this fact for later use
1671 if (!bestthistag
&& star
) {
1672 any_match_on_star
= 1;
1676 /* If one of the language tags of the variant matched on *, we
1677 * need to see if its q is better than that of any non-* match
1678 * on any other tag of the variant. If so the * match takes
1679 * precedence and the overall match is not definite.
1681 if ( any_match_on_star
&&
1682 ((best
&& star
->quality
> best
->quality
) ||
1685 variant
->definite
= 0;
1688 variant
->lang_quality
= best
? best
->quality
: fiddle_q
;
1692 /* Handle the ForceDefaultLanguage overrides, based on the best match
1693 * to LanguagePriority order. The best match is the lowest index of
1694 * any LanguagePriority match.
1696 if (((forcepriority
& FLP_PREFER
)
1697 && (variant
->lang_index
< 0))
1698 || ((forcepriority
& FLP_FALLBACK
)
1699 && !variant
->lang_quality
))
1704 for (j
= 0; j
< variant
->content_languages
->nelts
; ++j
)
1706 /* lang is the variant's language-tag, which is the one
1707 * we are allowed to use the prefix of in HTTP/1.1
1709 char *lang
= ((char **) (variant
->content_languages
->elts
))[j
];
1712 /* If we wish to fallback or
1713 * we use our own LanguagePriority index.
1715 idx
= find_lang_index(neg
->conf
->language_priority
, lang
);
1716 if ((idx
>= 0) && ((bestidx
== -1) || (idx
< bestidx
))) {
1722 if (variant
->lang_quality
) {
1723 if (forcepriority
& FLP_PREFER
) {
1724 variant
->lang_index
= bestidx
;
1728 if (forcepriority
& FLP_FALLBACK
) {
1729 variant
->lang_index
= bestidx
;
1730 variant
->lang_quality
= .0001f
;
1731 variant
->definite
= 0;
1739 /* Determining the content length --- if the map didn't tell us,
1740 * we have to do a stat() and remember for next time.
1743 static apr_off_t
find_content_length(negotiation_state
*neg
, var_rec
*variant
)
1747 if (variant
->bytes
< 0) {
1748 if ( variant
->sub_req
1749 && (variant
->sub_req
->finfo
.valid
& APR_FINFO_SIZE
)) {
1750 variant
->bytes
= variant
->sub_req
->finfo
.size
;
1753 char *fullname
= ap_make_full_path(neg
->pool
, neg
->dir_name
,
1754 variant
->file_name
);
1756 if (apr_stat(&statb
, fullname
,
1757 APR_FINFO_SIZE
, neg
->pool
) == APR_SUCCESS
) {
1758 variant
->bytes
= statb
.size
;
1763 return variant
->bytes
;
1766 /* For a given variant, find the best matching Accept: header
1767 * and assign the Accept: header's quality value to the
1768 * mime_type_quality field of the variant, for later use in
1769 * determining the best matching variant.
1772 static void set_accept_quality(negotiation_state
*neg
, var_rec
*variant
)
1775 accept_rec
*accept_recs
;
1779 /* if no Accept: header, leave quality alone (will
1780 * remain at the default value of 1)
1782 * XXX: This if is currently never true because of the effect of
1783 * maybe_add_default_accepts().
1785 if (!neg
->accepts
) {
1786 if (variant
->mime_type
&& *variant
->mime_type
)
1787 variant
->definite
= 0;
1791 accept_recs
= (accept_rec
*) neg
->accepts
->elts
;
1794 * Go through each of the ranges on the Accept: header,
1795 * looking for the 'best' match with this variant's
1796 * content-type. We use the best match's quality
1797 * value (from the Accept: header) for this variant's
1798 * mime_type_quality field.
1800 * The best match is determined like this:
1801 * type/type is better than type/ * is better than * / *
1802 * if match is type/type, use the level mime param if available
1804 for (i
= 0; i
< neg
->accepts
->nelts
; ++i
) {
1806 accept_rec
*type
= &accept_recs
[i
];
1807 int prev_mime_stars
;
1809 prev_mime_stars
= variant
->mime_stars
;
1811 if (!mime_match(type
, variant
)) {
1812 continue; /* didn't match the content type at all */
1815 /* did match - see if there were less or more stars than
1818 if (prev_mime_stars
== variant
->mime_stars
) {
1819 continue; /* more stars => not as good a match */
1823 /* If we are allowed to mess with the q-values
1824 * and have no explicit q= parameters in the accept header,
1825 * make wildcards very low, so we have a low chance
1826 * of ending up with them if there's something better.
1829 if (!neg
->dont_fiddle_headers
&& !neg
->accept_q
&&
1830 variant
->mime_stars
== 1) {
1833 else if (!neg
->dont_fiddle_headers
&& !neg
->accept_q
&&
1834 variant
->mime_stars
== 2) {
1841 q_definite
= (variant
->mime_stars
== 3);
1843 variant
->mime_type_quality
= q
;
1844 variant
->definite
= variant
->definite
&& q_definite
;
1848 /* For a given variant, find the 'q' value of the charset given
1849 * on the Accept-Charset line. If no charsets are listed,
1850 * assume value of '1'.
1852 static void set_charset_quality(negotiation_state
*neg
, var_rec
*variant
)
1855 accept_rec
*accept_recs
;
1856 const char *charset
= variant
->content_charset
;
1857 accept_rec
*star
= NULL
;
1859 /* if no Accept-Charset: header, leave quality alone (will
1860 * remain at the default value of 1)
1862 if (!neg
->accept_charsets
) {
1863 if (charset
&& *charset
)
1864 variant
->definite
= 0;
1868 accept_recs
= (accept_rec
*) neg
->accept_charsets
->elts
;
1870 if (charset
== NULL
|| !*charset
) {
1871 /* Charset of variant not known */
1873 /* if not a text / * type, leave quality alone */
1874 if (!(!strncmp(variant
->mime_type
, "text/", 5)
1875 || !strcmp(variant
->mime_type
, INCLUDES_MAGIC_TYPE
)
1876 || !strcmp(variant
->mime_type
, INCLUDES_MAGIC_TYPE3
)
1880 /* Don't go guessing if we are in strict header mode,
1881 * e.g. when running the rvsa, as any guess won't be reflected
1882 * in the variant list or content-location headers.
1884 if (neg
->dont_fiddle_headers
)
1887 charset
= "iso-8859-1"; /* The default charset for HTTP text types */
1891 * Go through each of the items on the Accept-Charset header,
1892 * looking for a match with this variant's charset. If none
1893 * match, charset is unacceptable, so set quality to 0.
1895 for (i
= 0; i
< neg
->accept_charsets
->nelts
; ++i
) {
1897 accept_rec
*type
= &accept_recs
[i
];
1899 if (!strcmp(type
->name
, charset
)) {
1900 variant
->charset_quality
= type
->quality
;
1903 else if (strcmp(type
->name
, "*") == 0) {
1907 /* No explicit match */
1909 variant
->charset_quality
= star
->quality
;
1910 variant
->definite
= 0;
1913 /* If this variant is in charset iso-8859-1, the default is 1.0 */
1914 if (strcmp(charset
, "iso-8859-1") == 0) {
1915 variant
->charset_quality
= 1.0f
;
1918 variant
->charset_quality
= 0.0f
;
1923 /* is_identity_encoding is included for back-compat, but does anyone
1924 * use 7bit, 8bin or binary in their var files??
1927 static int is_identity_encoding(const char *enc
)
1929 return (!enc
|| !enc
[0] || !strcmp(enc
, "7bit") || !strcmp(enc
, "8bit")
1930 || !strcmp(enc
, "binary"));
1934 * set_encoding_quality determines whether the encoding for a particular
1935 * variant is acceptable for the user-agent.
1937 * The rules for encoding are that if the user-agent does not supply
1938 * any Accept-Encoding header, then all encodings are allowed but a
1939 * variant with no encoding should be preferred.
1940 * If there is an empty Accept-Encoding header, then no encodings are
1941 * acceptable. If there is a non-empty Accept-Encoding header, then
1942 * any of the listed encodings are acceptable, as well as no encoding
1943 * unless the "identity" encoding is specifically excluded.
1945 static void set_encoding_quality(negotiation_state
*neg
, var_rec
*variant
)
1947 accept_rec
*accept_recs
;
1948 const char *enc
= variant
->content_encoding
;
1949 accept_rec
*star
= NULL
;
1950 float value_if_not_found
= 0.0f
;
1953 if (!neg
->accept_encodings
) {
1954 /* We had no Accept-Encoding header, assume that all
1955 * encodings are acceptable with a low quality,
1956 * but we prefer no encoding if available.
1958 if (!enc
|| is_identity_encoding(enc
))
1959 variant
->encoding_quality
= 1.0f
;
1961 variant
->encoding_quality
= 0.5f
;
1966 if (!enc
|| is_identity_encoding(enc
)) {
1968 value_if_not_found
= 0.0001f
;
1971 accept_recs
= (accept_rec
*) neg
->accept_encodings
->elts
;
1973 /* Go through each of the encodings on the Accept-Encoding: header,
1974 * looking for a match with our encoding. x- prefixes are ignored.
1976 if (enc
[0] == 'x' && enc
[1] == '-') {
1979 for (i
= 0; i
< neg
->accept_encodings
->nelts
; ++i
) {
1981 char *name
= accept_recs
[i
].name
;
1983 if (name
[0] == 'x' && name
[1] == '-') {
1987 if (!strcmp(name
, enc
)) {
1988 variant
->encoding_quality
= accept_recs
[i
].quality
;
1992 if (strcmp(name
, "*") == 0) {
1993 star
= &accept_recs
[i
];
1997 /* No explicit match */
1999 variant
->encoding_quality
= star
->quality
;
2003 /* Encoding not found on Accept-Encoding: header, so it is
2004 * _not_ acceptable unless it is the identity (no encoding)
2006 variant
->encoding_quality
= value_if_not_found
;
2009 /*************************************************************
2010 * Possible results of the variant selection algorithm
2012 enum algorithm_results
{
2013 alg_choice
= 1, /* choose variant */
2014 alg_list
/* list variants */
2017 /* Below is the 'best_match' function. It returns an int, which has
2018 * one of the two values alg_choice or alg_list, which give the result
2019 * of the variant selection algorithm. alg_list means that no best
2020 * variant was found by the algorithm, alg_choice means that a best
2021 * variant was found and should be returned. The list/choice
2022 * terminology comes from TCN (rfc2295), but is used in a more generic
2023 * way here. The best variant is returned in *pbest. best_match has
2024 * two possible algorithms for determining the best variant: the
2025 * RVSA/1.0 algorithm (from RFC2296), and the standard Apache
2026 * algorithm. These are split out into separate functions
2027 * (is_variant_better_rvsa() and is_variant_better()). Selection of
2028 * one is through the neg->use_rvsa flag.
2030 * The call to best_match also creates full information, including
2031 * language, charset, etc quality for _every_ variant. This is needed
2032 * for generating a correct Vary header, and can be used for the
2033 * Alternates header, the human-readable list responses and 406 errors.
2036 /* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
2037 * v1.0) from rfc2296. This is the algorithm that goes together with
2038 * transparent content negotiation (TCN).
2040 static int is_variant_better_rvsa(negotiation_state
*neg
, var_rec
*variant
,
2041 var_rec
*best
, float *p_bestq
)
2043 float bestq
= *p_bestq
, q
;
2045 /* TCN does not cover negotiation on content-encoding. For now,
2046 * we ignore the encoding unless it was explicitly excluded.
2048 if (variant
->encoding_quality
== 0.0f
)
2051 q
= variant
->mime_type_quality
*
2052 variant
->source_quality
*
2053 variant
->charset_quality
*
2054 variant
->lang_quality
;
2056 /* RFC 2296 calls for the result to be rounded to 5 decimal places,
2057 * but we don't do that because it serves no useful purpose other
2058 * than to ensure that a remote algorithm operates on the same
2059 * precision as ours. That is silly, since what we obviously want
2060 * is for the algorithm to operate on the best available precision
2061 * regardless of who runs it. Since the above calculation may
2062 * result in significant variance at 1e-12, rounding would be bogus.
2066 ap_log_error(APLOG_MARK
, APLOG_STARTUP
, 0, NULL
,
2067 "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2068 "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
2069 "q=%1.5f definite=%d",
2070 (variant
->file_name
? variant
->file_name
: ""),
2071 (variant
->mime_type
? variant
->mime_type
: ""),
2072 (variant
->content_languages
2073 ? apr_array_pstrcat(neg
->pool
, variant
->content_languages
, ',')
2075 variant
->source_quality
,
2076 variant
->mime_type_quality
,
2077 variant
->lang_quality
,
2078 variant
->charset_quality
,
2079 variant
->encoding_quality
,
2092 /* If the best variant's encoding is of lesser quality than
2093 * this variant, then we prefer this variant
2095 if (variant
->encoding_quality
> best
->encoding_quality
) {
2103 /* Negotiation algorithm as used by previous versions of Apache
2107 static int is_variant_better(negotiation_state
*neg
, var_rec
*variant
,
2108 var_rec
*best
, float *p_bestq
)
2110 float bestq
= *p_bestq
, q
;
2113 /* For non-transparent negotiation, server can choose how
2114 * to handle the negotiation. We'll use the following in
2115 * order: content-type, language, content-type level, charset,
2116 * content encoding, content length.
2118 * For each check, we have three possible outcomes:
2119 * This variant is worse than current best: return 0
2120 * This variant is better than the current best:
2121 * assign this variant's q to *p_bestq, and return 1
2122 * This variant is just as desirable as the current best:
2123 * drop through to the next test.
2125 * This code is written in this long-winded way to allow future
2126 * customisation, either by the addition of additional
2127 * checks, or to allow the order of the checks to be determined
2128 * by configuration options (e.g. we might prefer to check
2129 * language quality _before_ content type).
2132 /* First though, eliminate this variant if it is not
2133 * acceptable by type, charset, encoding or language.
2137 ap_log_error(APLOG_MARK
, APLOG_STARTUP
, 0, NULL
,
2138 "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2139 "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
2140 (variant
->file_name
? variant
->file_name
: ""),
2141 (variant
->mime_type
? variant
->mime_type
: ""),
2142 (variant
->content_languages
2143 ? apr_array_pstrcat(neg
->pool
, variant
->content_languages
, ',')
2145 variant
->source_quality
,
2146 variant
->mime_type_quality
,
2147 variant
->lang_quality
,
2148 variant
->lang_index
,
2149 variant
->charset_quality
,
2150 variant
->encoding_quality
);
2153 if (variant
->encoding_quality
== 0.0f
||
2154 variant
->lang_quality
== 0.0f
||
2155 variant
->source_quality
== 0.0f
||
2156 variant
->charset_quality
== 0.0f
||
2157 variant
->mime_type_quality
== 0.0f
) {
2158 return 0; /* don't consider unacceptables */
2161 q
= variant
->mime_type_quality
* variant
->source_quality
;
2162 if (q
== 0.0 || q
< bestq
) {
2165 if (q
> bestq
|| !best
) {
2171 if (variant
->lang_quality
< best
->lang_quality
) {
2174 if (variant
->lang_quality
> best
->lang_quality
) {
2179 /* if language qualities were equal, try the LanguagePriority stuff */
2180 if (best
->lang_index
!= -1 &&
2181 (variant
->lang_index
== -1 || variant
->lang_index
> best
->lang_index
)) {
2184 if (variant
->lang_index
!= -1 &&
2185 (best
->lang_index
== -1 || variant
->lang_index
< best
->lang_index
)) {
2190 /* content-type level (sometimes used with text/html, though we
2191 * support it on other types too)
2193 levcmp
= level_cmp(variant
, best
);
2203 if (variant
->charset_quality
< best
->charset_quality
) {
2206 /* If the best variant's charset is ISO-8859-1 and this variant has
2207 * the same charset quality, then we prefer this variant
2210 if (variant
->charset_quality
> best
->charset_quality
||
2211 ((variant
->content_charset
!= NULL
&&
2212 *variant
->content_charset
!= '\0' &&
2213 strcmp(variant
->content_charset
, "iso-8859-1") != 0) &&
2214 (best
->content_charset
== NULL
||
2215 *best
->content_charset
== '\0' ||
2216 strcmp(best
->content_charset
, "iso-8859-1") == 0))) {
2221 /* Prefer the highest value for encoding_quality.
2223 if (variant
->encoding_quality
< best
->encoding_quality
) {
2226 if (variant
->encoding_quality
> best
->encoding_quality
) {
2231 /* content length if all else equal */
2232 if (find_content_length(neg
, variant
) >= find_content_length(neg
, best
)) {
2236 /* ok, to get here means every thing turned out equal, except
2237 * we have a shorter content length, so use this variant
2243 /* figure out, whether a variant is in a specific language
2244 * it returns also false, if the variant has no language.
2246 static int variant_has_language(var_rec
*variant
, const char *lang
)
2252 || !variant
->content_languages
2253 || !(max
= variant
->content_languages
->nelts
)) {
2257 for (j
= 0; j
< max
; ++j
) {
2259 ((char **) (variant
->content_languages
->elts
))[j
])) {
2267 /* check for environment variables 'no-gzip' and
2268 * 'gzip-only-text/html' to get a behaviour similiar
2271 static int discard_variant_by_env(var_rec
*variant
, int discard
)
2273 if ( is_identity_encoding(variant
->content_encoding
)
2274 || !strcmp(variant
->content_encoding
, "identity")) {
2278 return ( (discard
== DISCARD_ALL_ENCODINGS
)
2279 || (discard
== DISCARD_ALL_BUT_HTML
2280 && (!variant
->mime_type
2281 || strncmp(variant
->mime_type
, "text/html", 9))));
2284 static int best_match(negotiation_state
*neg
, var_rec
**pbest
)
2289 enum algorithm_results algorithm_result
;
2290 int may_discard
= 0;
2292 var_rec
*avail_recs
= (var_rec
*) neg
->avail_vars
->elts
;
2294 /* fetch request dependent variables
2295 * prefer-language: prefer a certain language.
2297 const char *preferred_language
= apr_table_get(neg
->r
->subprocess_env
,
2300 /* no-gzip: do not send encoded documents */
2301 if (apr_table_get(neg
->r
->subprocess_env
, "no-gzip")) {
2302 may_discard
= DISCARD_ALL_ENCODINGS
;
2305 /* gzip-only-text/html: send encoded documents only
2306 * if they are text/html. (no-gzip has a higher priority).
2309 const char *env_value
= apr_table_get(neg
->r
->subprocess_env
,
2310 "gzip-only-text/html");
2312 if (env_value
&& !strcmp(env_value
, "1")) {
2313 may_discard
= DISCARD_ALL_BUT_HTML
;
2317 set_default_lang_quality(neg
);
2320 * Find the 'best' variant
2321 * We run the loop possibly twice: if "prefer-language"
2322 * environment variable is set but we did not find an appropriate
2323 * best variant. In that case forget the preferred language and
2324 * negotiate over all variants.
2330 for (j
= 0; j
< neg
->avail_vars
->nelts
; ++j
) {
2331 var_rec
*variant
= &avail_recs
[j
];
2333 /* if this variant is encoded somehow and there are special
2334 * variables set, we do not negotiate it. see above.
2337 && discard_variant_by_env(variant
, may_discard
)) {
2341 /* if a language is preferred, but the current variant
2342 * is not in that language, then drop it for now
2344 if ( preferred_language
2345 && !variant_has_language(variant
, preferred_language
)) {
2349 /* Find all the relevant 'quality' values from the
2350 * Accept... headers, and store in the variant. This also
2351 * prepares for sending an Alternates header etc so we need to
2352 * do it even if we do not actually plan to find a best
2355 set_accept_quality(neg
, variant
);
2356 /* accept the preferred language, even when it's not listed within
2357 * the Accept-Language header
2359 if (preferred_language
) {
2360 variant
->lang_quality
= 1.0f
;
2361 variant
->definite
= 1;
2364 set_language_quality(neg
, variant
);
2366 set_encoding_quality(neg
, variant
);
2367 set_charset_quality(neg
, variant
);
2369 /* Only do variant selection if we may actually choose a
2370 * variant for the client
2372 if (neg
->may_choose
) {
2374 /* Now find out if this variant is better than the current
2375 * best, either using the RVSA/1.0 algorithm, or Apache's
2376 * internal server-driven algorithm. Presumably other
2377 * server-driven algorithms are possible, and could be
2381 if (neg
->use_rvsa
) {
2382 if (is_variant_better_rvsa(neg
, variant
, best
, &bestq
)) {
2387 if (is_variant_better(neg
, variant
, best
, &bestq
)) {
2394 /* We now either have a best variant, or no best variant */
2396 if (neg
->use_rvsa
) {
2397 /* calculate result for RVSA/1.0 algorithm:
2398 * only a choice response if the best variant has q>0
2401 algorithm_result
= (best
&& best
->definite
) && (bestq
> 0) ?
2402 alg_choice
: alg_list
;
2405 /* calculate result for Apache negotiation algorithm */
2406 algorithm_result
= bestq
> 0 ? alg_choice
: alg_list
;
2409 /* run the loop again, if the "prefer-language" got no clear result */
2410 if (preferred_language
&& (!best
|| algorithm_result
!= alg_choice
)) {
2411 preferred_language
= NULL
;
2418 /* Returning a choice response with a non-neighboring variant is a
2419 * protocol security error in TCN (see rfc2295). We do *not*
2420 * verify here that the variant and URI are neighbors, even though
2421 * we may return alg_choice. We depend on the environment (the
2422 * caller) to only declare the resource transparently negotiable if
2423 * all variants are neighbors.
2426 return algorithm_result
;
2429 /* Sets response headers for a negotiated response.
2430 * neg->is_transparent determines whether a transparently negotiated
2431 * response or a plain `server driven negotiation' response is
2432 * created. Applicable headers are Alternates, Vary, and TCN.
2434 * The Vary header we create is sometimes longer than is required for
2435 * the correct caching of negotiated results by HTTP/1.1 caches. For
2436 * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
2437 * the Accept: header assigns a 0 quality to .ps, then the results of
2438 * the two server-side negotiation algorithms we currently implement
2439 * will never depend on Accept-Language so we could return `Vary:
2440 * negotiate, accept' instead of the longer 'Vary: negotiate, accept,
2441 * accept-language' which the code below will return. A routine for
2442 * computing the exact minimal Vary header would be a huge pain to code
2443 * and maintain though, especially because we need to take all possible
2444 * twiddles in the server-side negotiation algorithms into account.
2446 static void set_neg_headers(request_rec
*r
, negotiation_state
*neg
,
2450 var_rec
*avail_recs
= (var_rec
*) neg
->avail_vars
->elts
;
2451 const char *sample_type
= NULL
;
2452 const char *sample_language
= NULL
;
2453 const char *sample_encoding
= NULL
;
2454 const char *sample_charset
= NULL
;
2458 apr_array_header_t
*arr
;
2459 int max_vlist_array
= (neg
->avail_vars
->nelts
* 21);
2460 int first_variant
= 1;
2461 int vary_by_type
= 0;
2462 int vary_by_language
= 0;
2463 int vary_by_charset
= 0;
2464 int vary_by_encoding
= 0;
2467 /* In order to avoid O(n^2) memory copies in building Alternates,
2468 * we preallocate a apr_table_t with the maximum substrings possible,
2469 * fill it with the variant list, and then concatenate the entire array.
2470 * Note that if you change the number of substrings pushed, you also
2471 * need to change the calculation of max_vlist_array above.
2473 if (neg
->send_alternates
&& neg
->avail_vars
->nelts
)
2474 arr
= apr_array_make(r
->pool
, max_vlist_array
, sizeof(char *));
2478 /* Put headers into err_headers_out, since send_http_header()
2479 * outputs both headers_out and err_headers_out.
2481 hdrs
= r
->err_headers_out
;
2483 for (j
= 0; j
< neg
->avail_vars
->nelts
; ++j
) {
2484 var_rec
*variant
= &avail_recs
[j
];
2486 if (variant
->content_languages
&& variant
->content_languages
->nelts
) {
2487 lang
= apr_array_pstrcat(r
->pool
, variant
->content_languages
, ',');
2493 /* Calculate Vary by looking for any difference between variants */
2495 if (first_variant
) {
2496 sample_type
= variant
->mime_type
;
2497 sample_charset
= variant
->content_charset
;
2498 sample_language
= lang
;
2499 sample_encoding
= variant
->content_encoding
;
2502 if (!vary_by_type
&&
2503 strcmp(sample_type
? sample_type
: "",
2504 variant
->mime_type
? variant
->mime_type
: "")) {
2507 if (!vary_by_charset
&&
2508 strcmp(sample_charset
? sample_charset
: "",
2509 variant
->content_charset
?
2510 variant
->content_charset
: "")) {
2511 vary_by_charset
= 1;
2513 if (!vary_by_language
&&
2514 strcmp(sample_language
? sample_language
: "",
2515 lang
? lang
: "")) {
2516 vary_by_language
= 1;
2518 if (!vary_by_encoding
&&
2519 strcmp(sample_encoding
? sample_encoding
: "",
2520 variant
->content_encoding
?
2521 variant
->content_encoding
: "")) {
2522 vary_by_encoding
= 1;
2527 if (!neg
->send_alternates
)
2530 /* Generate the string components for this Alternates entry */
2532 *((const char **) apr_array_push(arr
)) = "{\"";
2533 *((const char **) apr_array_push(arr
)) = variant
->file_name
;
2534 *((const char **) apr_array_push(arr
)) = "\" ";
2536 qstr
= (char *) apr_palloc(r
->pool
, 6);
2537 apr_snprintf(qstr
, 6, "%1.3f", variant
->source_quality
);
2539 /* Strip trailing zeros (saves those valuable network bytes) */
2540 if (qstr
[4] == '0') {
2542 if (qstr
[3] == '0') {
2544 if (qstr
[2] == '0') {
2549 *((const char **) apr_array_push(arr
)) = qstr
;
2551 if (variant
->mime_type
&& *variant
->mime_type
) {
2552 *((const char **) apr_array_push(arr
)) = " {type ";
2553 *((const char **) apr_array_push(arr
)) = variant
->mime_type
;
2554 *((const char **) apr_array_push(arr
)) = "}";
2556 if (variant
->content_charset
&& *variant
->content_charset
) {
2557 *((const char **) apr_array_push(arr
)) = " {charset ";
2558 *((const char **) apr_array_push(arr
)) = variant
->content_charset
;
2559 *((const char **) apr_array_push(arr
)) = "}";
2562 *((const char **) apr_array_push(arr
)) = " {language ";
2563 *((const char **) apr_array_push(arr
)) = lang
;
2564 *((const char **) apr_array_push(arr
)) = "}";
2566 if (variant
->content_encoding
&& *variant
->content_encoding
) {
2567 /* Strictly speaking, this is non-standard, but so is TCN */
2569 *((const char **) apr_array_push(arr
)) = " {encoding ";
2570 *((const char **) apr_array_push(arr
)) = variant
->content_encoding
;
2571 *((const char **) apr_array_push(arr
)) = "}";
2574 /* Note that the Alternates specification (in rfc2295) does
2575 * not require that we include {length x}, so we could omit it
2576 * if determining the length is too expensive. We currently
2577 * always include it though.
2579 * If the variant is a CGI script, find_content_length would
2580 * return the length of the script, not the output it
2581 * produces, so we check for the presence of a handler and if
2582 * there is one we don't add a length.
2584 * XXX: TODO: This check does not detect a CGI script if we
2585 * get the variant from a type map. This needs to be fixed
2586 * (without breaking things if the type map specifies a
2587 * content-length, which currently leads to the correct result).
2589 if (!(variant
->sub_req
&& variant
->sub_req
->handler
)
2590 && (len
= find_content_length(neg
, variant
)) >= 0) {
2592 *((const char **) apr_array_push(arr
)) = " {length ";
2593 *((const char **) apr_array_push(arr
)) = apr_off_t_toa(r
->pool
,
2595 *((const char **) apr_array_push(arr
)) = "}";
2598 *((const char **) apr_array_push(arr
)) = "}";
2599 *((const char **) apr_array_push(arr
)) = ", "; /* trimmed below */
2602 if (neg
->send_alternates
&& neg
->avail_vars
->nelts
) {
2603 arr
->nelts
--; /* remove last comma */
2604 apr_table_mergen(hdrs
, "Alternates",
2605 apr_array_pstrcat(r
->pool
, arr
, '\0'));
2608 if (neg
->is_transparent
|| vary_by_type
|| vary_by_language
||
2609 vary_by_language
|| vary_by_charset
|| vary_by_encoding
) {
2611 apr_table_mergen(hdrs
, "Vary", 2 + apr_pstrcat(r
->pool
,
2612 neg
->is_transparent
? ", negotiate" : "",
2613 vary_by_type
? ", accept" : "",
2614 vary_by_language
? ", accept-language" : "",
2615 vary_by_charset
? ", accept-charset" : "",
2616 vary_by_encoding
? ", accept-encoding" : "", NULL
));
2619 if (neg
->is_transparent
) { /* Create TCN response header */
2620 apr_table_setn(hdrs
, "TCN",
2621 alg_result
== alg_list
? "list" : "choice");
2625 /**********************************************************************
2627 * Return an HTML list of variants. This is output as part of the
2628 * choice response or 406 status body.
2631 static char *make_variant_list(request_rec
*r
, negotiation_state
*neg
)
2633 apr_array_header_t
*arr
;
2635 int max_vlist_array
= (neg
->avail_vars
->nelts
* 15) + 2;
2637 /* In order to avoid O(n^2) memory copies in building the list,
2638 * we preallocate a apr_table_t with the maximum substrings possible,
2639 * fill it with the variant list, and then concatenate the entire array.
2641 arr
= apr_array_make(r
->pool
, max_vlist_array
, sizeof(char *));
2643 *((const char **) apr_array_push(arr
)) = "Available variants:\n<ul>\n";
2645 for (i
= 0; i
< neg
->avail_vars
->nelts
; ++i
) {
2646 var_rec
*variant
= &((var_rec
*) neg
->avail_vars
->elts
)[i
];
2647 const char *filename
= variant
->file_name
? variant
->file_name
: "";
2648 apr_array_header_t
*languages
= variant
->content_languages
;
2649 const char *description
= variant
->description
2650 ? variant
->description
2653 /* The format isn't very neat, and it would be nice to make
2654 * the tags human readable (eg replace 'language en' with 'English').
2655 * Note that if you change the number of substrings pushed, you also
2656 * need to change the calculation of max_vlist_array above.
2658 *((const char **) apr_array_push(arr
)) = "<li><a href=\"";
2659 *((const char **) apr_array_push(arr
)) = filename
;
2660 *((const char **) apr_array_push(arr
)) = "\">";
2661 *((const char **) apr_array_push(arr
)) = filename
;
2662 *((const char **) apr_array_push(arr
)) = "</a> ";
2663 *((const char **) apr_array_push(arr
)) = description
;
2665 if (variant
->mime_type
&& *variant
->mime_type
) {
2666 *((const char **) apr_array_push(arr
)) = ", type ";
2667 *((const char **) apr_array_push(arr
)) = variant
->mime_type
;
2669 if (languages
&& languages
->nelts
) {
2670 *((const char **) apr_array_push(arr
)) = ", language ";
2671 *((const char **) apr_array_push(arr
)) = apr_array_pstrcat(r
->pool
,
2674 if (variant
->content_charset
&& *variant
->content_charset
) {
2675 *((const char **) apr_array_push(arr
)) = ", charset ";
2676 *((const char **) apr_array_push(arr
)) = variant
->content_charset
;
2678 if (variant
->content_encoding
) {
2679 *((const char **) apr_array_push(arr
)) = ", encoding ";
2680 *((const char **) apr_array_push(arr
)) = variant
->content_encoding
;
2682 *((const char **) apr_array_push(arr
)) = "</li>\n";
2684 *((const char **) apr_array_push(arr
)) = "</ul>\n";
2686 return apr_array_pstrcat(r
->pool
, arr
, '\0');
2689 static void store_variant_list(request_rec
*r
, negotiation_state
*neg
)
2691 if (r
->main
== NULL
) {
2692 apr_table_setn(r
->notes
, "variant-list", make_variant_list(r
, neg
));
2695 apr_table_setn(r
->main
->notes
, "variant-list",
2696 make_variant_list(r
->main
, neg
));
2700 /* Called if we got a "Choice" response from the variant selection algorithm.
2701 * It checks the result of the chosen variant to see if it
2702 * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
2703 * Otherwise, add the appropriate headers to the current response.
2706 static int setup_choice_response(request_rec
*r
, negotiation_state
*neg
,
2709 request_rec
*sub_req
;
2710 const char *sub_vary
;
2712 if (!variant
->sub_req
) {
2715 sub_req
= ap_sub_req_lookup_file(variant
->file_name
, r
, r
->output_filters
);
2716 status
= sub_req
->status
;
2718 if (status
!= HTTP_OK
&&
2719 !apr_table_get(sub_req
->err_headers_out
, "TCN")) {
2720 ap_destroy_sub_req(sub_req
);
2723 variant
->sub_req
= sub_req
;
2726 sub_req
= variant
->sub_req
;
2729 /* The variant selection algorithm told us to return a "Choice"
2730 * response. This is the normal variant response, with
2731 * some extra headers. First, ensure that the chosen
2732 * variant did or will not itself engage in transparent negotiation.
2733 * If not, set the appropriate headers, and fall through to
2734 * the normal variant handling
2737 /* This catches the error that a transparent type map selects a
2738 * transparent multiviews resource as the best variant.
2740 * XXX: We do not signal an error if a transparent type map
2741 * selects a _non_transparent multiviews resource as the best
2742 * variant, because we can generate a legal negotiation response
2743 * in this case. In this case, the vlist_validator of the
2744 * nontransparent subrequest will be lost however. This could
2745 * lead to cases in which a change in the set of variants or the
2746 * negotiation algorithm of the nontransparent resource is never
2747 * propagated up to a HTTP/1.1 cache which interprets Vary. To be
2748 * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
2749 * for this type of recursive negotiation too.
2751 if (neg
->is_transparent
&&
2752 apr_table_get(sub_req
->err_headers_out
, "TCN")) {
2753 return HTTP_VARIANT_ALSO_VARIES
;
2756 /* This catches the error that a transparent type map recursively
2757 * selects, as the best variant, another type map which itself
2758 * causes transparent negotiation to be done.
2760 * XXX: Actually, we catch this error by catching all cases of
2761 * type map recursion. There are some borderline recursive type
2762 * map arrangements which would not produce transparent
2763 * negotiation protocol errors or lack of cache propagation
2764 * problems, but such arrangements are very hard to detect at this
2765 * point in the control flow, so we do not bother to single them
2768 * Recursive type maps imply a recursive arrangement of negotiated
2769 * resources which is visible to outside clients, and this is not
2770 * supported by the transparent negotiation caching protocols, so
2771 * if we are to have generic support for recursive type maps, we
2772 * have to create some configuration setting which makes all type
2773 * maps non-transparent when recursion is enabled. Also, if we
2774 * want recursive type map support which ensures propagation of
2775 * type map changes into HTTP/1.1 caches that handle Vary, we
2776 * would have to extend the current mechanism for generating
2777 * variant list validators.
2779 if (sub_req
->handler
&& strcmp(sub_req
->handler
, "type-map") == 0) {
2780 return HTTP_VARIANT_ALSO_VARIES
;
2783 /* This adds an appropriate Variant-Vary header if the subrequest
2784 * is a multiviews resource.
2786 * XXX: TODO: Note that this does _not_ handle any Vary header
2787 * returned by a CGI if sub_req is a CGI script, because we don't
2788 * see that Vary header yet at this point in the control flow.
2789 * This won't cause any cache consistency problems _unless_ the
2790 * CGI script also returns a Cache-Control header marking the
2791 * response as cachable. This needs to be fixed, also there are
2792 * problems if a CGI returns an Etag header which also need to be
2795 if ((sub_vary
= apr_table_get(sub_req
->err_headers_out
, "Vary")) != NULL
) {
2796 apr_table_setn(r
->err_headers_out
, "Variant-Vary", sub_vary
);
2798 /* Move the subreq Vary header into the main request to
2799 * prevent having two Vary headers in the response, which
2800 * would be legal but strange.
2802 apr_table_setn(r
->err_headers_out
, "Vary", sub_vary
);
2803 apr_table_unset(sub_req
->err_headers_out
, "Vary");
2806 apr_table_setn(r
->err_headers_out
, "Content-Location",
2807 apr_pstrdup(r
->pool
, variant
->file_name
));
2809 set_neg_headers(r
, neg
, alg_choice
); /* add Alternates and Vary */
2811 /* Still to do by caller: add Expires */
2816 /****************************************************************
2821 static int do_negotiation(request_rec
*r
, negotiation_state
*neg
,
2822 var_rec
**bestp
, int prefer_scripts
)
2824 var_rec
*avail_recs
= (var_rec
*) neg
->avail_vars
->elts
;
2825 int alg_result
; /* result of variant selection algorithm */
2829 /* Decide if resource is transparently negotiable */
2831 /* GET or HEAD? (HEAD has same method number as GET) */
2832 if (r
->method_number
== M_GET
) {
2834 /* maybe this should be configurable, see also the comment
2835 * about recursive type maps in setup_choice_response()
2837 neg
->is_transparent
= 1;
2839 /* We can't be transparent if we are a map file in the middle
2840 * of the request URI.
2842 if (r
->path_info
&& *r
->path_info
)
2843 neg
->is_transparent
= 0;
2845 for (j
= 0; j
< neg
->avail_vars
->nelts
; ++j
) {
2846 var_rec
*variant
= &avail_recs
[j
];
2848 /* We can't be transparent, because of internal
2849 * assumptions in best_match(), if there is a
2850 * non-neighboring variant. We can have a non-neighboring
2851 * variant when processing a type map.
2853 if (ap_strchr_c(variant
->file_name
, '/'))
2854 neg
->is_transparent
= 0;
2856 /* We can't be transparent, because of the behavior
2857 * of variant typemap bodies.
2859 if (variant
->body
) {
2860 neg
->is_transparent
= 0;
2865 if (neg
->is_transparent
) {
2866 parse_negotiate_header(r
, neg
);
2868 else { /* configure negotiation on non-transparent resource */
2869 neg
->may_choose
= 1;
2872 maybe_add_default_accepts(neg
, prefer_scripts
);
2874 alg_result
= best_match(neg
, bestp
);
2876 /* alg_result is one of
2877 * alg_choice: a best variant is chosen
2878 * alg_list: no best variant is chosen
2881 if (alg_result
== alg_list
) {
2882 /* send a list response or HTTP_NOT_ACCEPTABLE error response */
2884 neg
->send_alternates
= 1; /* always include Alternates header */
2885 set_neg_headers(r
, neg
, alg_result
);
2886 store_variant_list(r
, neg
);
2888 if (neg
->is_transparent
&& neg
->ua_supports_trans
) {
2889 /* XXX todo: expires? cachability? */
2891 /* Some HTTP/1.0 clients are known to choke when they get
2892 * a 300 (multiple choices) response without a Location
2893 * header. However the 300 code response we are are about
2894 * to generate will only reach 1.0 clients which support
2895 * transparent negotiation, and they should be OK. The
2896 * response should never reach older 1.0 clients, even if
2897 * we have CacheNegotiatedDocs enabled, because no 1.0
2898 * proxy cache (we know of) will cache and return 300
2899 * responses (they certainly won't if they conform to the
2900 * HTTP/1.0 specification).
2902 return HTTP_MULTIPLE_CHOICES
;
2906 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
2907 "no acceptable variant: %s", r
->filename
);
2908 return HTTP_NOT_ACCEPTABLE
;
2912 /* Variant selection chose a variant */
2914 /* XXX todo: merge the two cases in the if statement below */
2915 if (neg
->is_transparent
) {
2917 if ((res
= setup_choice_response(r
, neg
, *bestp
)) != 0) {
2918 return res
; /* return if error */
2922 set_neg_headers(r
, neg
, alg_result
);
2925 /* Make sure caching works - Vary should handle HTTP/1.1, but for
2926 * HTTP/1.0, we can't allow caching at all.
2929 /* XXX: Note that we only set r->no_cache to 1, which causes
2930 * Expires: <now> to be added, when responding to a HTTP/1.0
2931 * client. If we return the response to a 1.1 client, we do not
2932 * add Expires <now>, because doing so would degrade 1.1 cache
2933 * performance by preventing re-use of the response without prior
2934 * revalidation. On the other hand, if the 1.1 client is a proxy
2935 * which was itself contacted by a 1.0 client, or a proxy cache
2936 * which can be contacted later by 1.0 clients, then we currently
2937 * rely on this 1.1 proxy to add the Expires: <now> when it
2938 * forwards the response.
2940 * XXX: TODO: Find out if the 1.1 spec requires proxies and
2941 * tunnels to add Expires: <now> when forwarding the response to
2942 * 1.0 clients. I (kh) recall it is rather vague on this point.
2943 * Testing actual 1.1 proxy implementations would also be nice. If
2944 * Expires: <now> is not added by proxies then we need to always
2945 * include Expires: <now> ourselves to ensure correct caching, but
2946 * this would degrade HTTP/1.1 cache efficiency unless we also add
2947 * Cache-Control: max-age=N, which we currently don't.
2949 * Roy: No, we are not going to screw over HTTP future just to
2950 * ensure that people who can't be bothered to upgrade their
2951 * clients will always receive perfect server-side negotiation.
2952 * Hell, those clients are sending bogus accept headers anyway.
2954 * Manual setting of cache-control/expires always overrides this
2955 * automated kluge, on purpose.
2958 if ((!do_cache_negotiated_docs(r
->server
)
2959 && (r
->proto_num
< HTTP_VERSION(1,1)))
2960 && neg
->count_multiviews_variants
!= 1) {
2967 static int handle_map_file(request_rec
*r
)
2969 negotiation_state
*neg
;
2974 const char *new_req
;
2976 if(strcmp(r
->handler
,MAP_FILE_MAGIC_TYPE
) && strcmp(r
->handler
,"type-map"))
2979 neg
= parse_accept_headers(r
);
2980 if ((res
= read_type_map(&map
, neg
, r
))) {
2984 res
= do_negotiation(r
, neg
, &best
, 0);
2985 if (res
!= 0) return res
;
2989 conn_rec
*c
= r
->connection
;
2990 apr_bucket_brigade
*bb
;
2993 ap_allow_standard_methods(r
, REPLACE_ALLOW
, M_GET
, M_OPTIONS
,
2996 * if (r->method_number == M_OPTIONS) {
2997 * return ap_send_http_options(r);
3000 if (r
->method_number
!= M_GET
&& r
->method_number
!= M_POST
) {
3001 return HTTP_METHOD_NOT_ALLOWED
;
3004 /* ### These may be implemented by adding some 'extra' info
3005 * of the file offset onto the etag
3006 * ap_update_mtime(r, r->finfo.mtime);
3007 * ap_set_last_modified(r);
3010 apr_table_setn(r
->headers_out
, "Accept-Ranges", "bytes");
3011 ap_set_content_length(r
, best
->bytes
);
3013 /* set MIME type and charset as negotiated */
3014 if (best
->mime_type
&& *best
->mime_type
) {
3015 if (best
->content_charset
&& *best
->content_charset
) {
3016 ap_set_content_type(r
, apr_pstrcat(r
->pool
,
3019 best
->content_charset
,
3023 ap_set_content_type(r
, apr_pstrdup(r
->pool
, best
->mime_type
));
3027 /* set Content-language(s) as negotiated */
3028 if (best
->content_languages
&& best
->content_languages
->nelts
) {
3029 r
->content_languages
= apr_array_copy(r
->pool
,
3030 best
->content_languages
);
3033 /* set Content-Encoding as negotiated */
3034 if (best
->content_encoding
&& *best
->content_encoding
) {
3035 r
->content_encoding
= apr_pstrdup(r
->pool
,
3036 best
->content_encoding
);
3039 if ((res
= ap_meets_conditions(r
)) != OK
) {
3043 if ((res
= ap_discard_request_body(r
)) != OK
) {
3046 bb
= apr_brigade_create(r
->pool
, c
->bucket_alloc
);
3048 apr_brigade_insert_file(bb
, map
, best
->body
, best
->bytes
, r
->pool
);
3050 e
= apr_bucket_eos_create(c
->bucket_alloc
);
3051 APR_BRIGADE_INSERT_TAIL(bb
, e
);
3053 return ap_pass_brigade(r
->output_filters
, bb
);
3056 if (r
->path_info
&& *r
->path_info
) {
3057 /* remove any path_info from the end of the uri before trying
3058 * to change the filename. r->path_info from the original
3059 * request is passed along on the redirect.
3061 r
->uri
[ap_find_path_info(r
->uri
, r
->path_info
)] = '\0';
3063 udir
= ap_make_dirstr_parent(r
->pool
, r
->uri
);
3064 udir
= ap_escape_uri(r
->pool
, udir
);
3067 new_req
= apr_pstrcat(r
->pool
, udir
, best
->file_name
,
3068 r
->path_info
, "?", r
->args
, NULL
);
3071 new_req
= apr_pstrcat(r
->pool
, udir
, best
->file_name
,
3072 "?", r
->args
, NULL
);
3076 new_req
= apr_pstrcat(r
->pool
, udir
, best
->file_name
,
3077 r
->path_info
, NULL
);
3079 ap_internal_redirect(new_req
, r
);
3083 static int handle_multi(request_rec
*r
)
3085 negotiation_state
*neg
;
3086 var_rec
*best
, *avail_recs
;
3087 request_rec
*sub_req
;
3091 if (r
->finfo
.filetype
!= APR_NOFILE
3092 || !(ap_allow_options(r
) & OPT_MULTI
)) {
3096 neg
= parse_accept_headers(r
);
3098 if ((res
= read_types_multi(neg
))) {
3100 /* free all allocated memory from subrequests */
3101 avail_recs
= (var_rec
*) neg
->avail_vars
->elts
;
3102 for (j
= 0; j
< neg
->avail_vars
->nelts
; ++j
) {
3103 var_rec
*variant
= &avail_recs
[j
];
3104 if (variant
->sub_req
) {
3105 ap_destroy_sub_req(variant
->sub_req
);
3110 if (neg
->avail_vars
->nelts
== 0) {
3114 res
= do_negotiation(r
, neg
, &best
,
3115 (r
->method_number
!= M_GET
) || r
->args
||
3116 (r
->path_info
&& *r
->path_info
));
3118 goto return_from_multi
;
3120 if (!(sub_req
= best
->sub_req
)) {
3121 /* We got this out of a map file, so we don't actually have
3122 * a sub_req structure yet. Get one now.
3125 sub_req
= ap_sub_req_lookup_file(best
->file_name
, r
, r
->output_filters
);
3126 if (sub_req
->status
!= HTTP_OK
) {
3127 res
= sub_req
->status
;
3128 ap_destroy_sub_req(sub_req
);
3129 goto return_from_multi
;
3133 /* now do a "fast redirect" ... promotes the sub_req into the main req */
3134 ap_internal_fast_redirect(sub_req
, r
);
3136 /* give no advise for time on this subrequest. Perhaps we
3137 * should tally the last mtime amoung all variants, and date
3138 * the most recent, but that could confuse the proxies.
3142 /* clean up all but our favorite variant, since that sub_req
3143 * is now merged into the main request!
3145 avail_recs
= (var_rec
*) neg
->avail_vars
->elts
;
3146 for (j
= 0; j
< neg
->avail_vars
->nelts
; ++j
) {
3147 var_rec
*variant
= &avail_recs
[j
];
3148 if (variant
!= best
&& variant
->sub_req
) {
3149 ap_destroy_sub_req(variant
->sub_req
);
3155 /**********************************************************************
3156 * There is a problem with content-encoding, as some clients send and
3157 * expect an x- token (e.g. x-gzip) while others expect the plain token
3158 * (i.e. gzip). To try and deal with this as best as possible we do
3159 * the following: if the client sent an Accept-Encoding header and it
3160 * contains a plain token corresponding to the content encoding of the
3161 * response, then set content encoding using the plain token. Else if
3162 * the A-E header contains the x- token use the x- token in the C-E
3163 * header. Else don't do anything.
3165 * Note that if no A-E header was sent, or it does not contain a token
3166 * compatible with the final content encoding, then the token in the
3167 * C-E header will be whatever was specified in the AddEncoding
3170 static int fix_encoding(request_rec
*r
)
3172 const char *enc
= r
->content_encoding
;
3174 apr_array_header_t
*accept_encodings
;
3175 accept_rec
*accept_recs
;
3178 if (!enc
|| !*enc
) {
3182 if (enc
[0] == 'x' && enc
[1] == '-') {
3186 if ((accept_encodings
= do_header_line(r
->pool
,
3187 apr_table_get(r
->headers_in
, "Accept-Encoding"))) == NULL
) {
3191 accept_recs
= (accept_rec
*) accept_encodings
->elts
;
3193 for (i
= 0; i
< accept_encodings
->nelts
; ++i
) {
3194 char *name
= accept_recs
[i
].name
;
3196 if (!strcmp(name
, enc
)) {
3197 r
->content_encoding
= name
;
3201 if (name
[0] == 'x' && name
[1] == '-' && !strcmp(name
+2, enc
)) {
3207 r
->content_encoding
= x_enc
;
3214 static void register_hooks(apr_pool_t
*p
)
3216 ap_hook_fixups(fix_encoding
,NULL
,NULL
,APR_HOOK_MIDDLE
);
3217 ap_hook_type_checker(handle_multi
,NULL
,NULL
,APR_HOOK_FIRST
);
3218 ap_hook_handler(handle_map_file
,NULL
,NULL
,APR_HOOK_MIDDLE
);
3221 module AP_MODULE_DECLARE_DATA negotiation_module
=
3223 STANDARD20_MODULE_STUFF
,
3224 create_neg_dir_config
, /* dir config creator */
3225 merge_neg_dir_configs
, /* dir merger --- default is to override */
3226 NULL
, /* server config */
3227 NULL
, /* merge server config */
3228 negotiation_cmds
, /* command apr_table_t */
3229 register_hooks
/* register hooks */