mod_xhtml_neg.c File Reference

The main source file for the mod_xhtml_neg module. More...

#include "apr.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include "apr_want.h"
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_log.h"
#include "lookupa.h"

Data Structures

struct  xhtml_neg_state
 A per-server configuration state for this module. More...
struct  xhtml_dir_config
 A per-directory configuration state for this module. More...
struct  accept_rec
 Record of available info on a media type specified by the client. More...
struct  extension_rec
 Record the maps a file extension to zero or more content-types. More...

Defines

#define APR_WANT_STRFUNC
#define CORE_PRIVATE
#define DEFAULT_CHARSET_NAME   "iso-8859-1"
 The default HTTP character set for most content types.
#define DEFAULT_TEXT_XML_CHARSET   "us-ascii"
 The default HTTP character set for text/xml and text/xml-external-entity content types.
#define mod_xhtml_strempty(str)   (((str) == NULL) || (*(str) == '\0'))
 Test whether a string is empty without having to do a full scan of the string just to return a length, as per strlen.

Enumerations

enum  xhtml_neg_active { active_on = 1, active_off = 0, active_dontcare = 2 }
 Enumeration for determining whether processing should be enabled for this module. More...
enum  http_caching { caching_on = 1, caching_off = 0, caching_dontcare = 2 }
 Enumeration for determining whether HTTP 1.0 caching should be allowed for negotiated documents. More...

Functions

static int mod_xhtml_strlen (const char *str)
 Calculate the length of the given string.
static int mod_xhtml_strcmp (const char *str1, const char *str2)
 Compare one string against another in a case-sensitive manner.
static int mod_xhtml_stricmp (const char *str1, const char *str2)
 Compare one string against another in a case-insensitive manner.
static int mod_xhtml_strncmp (const char *str1, const char *str2, const int len)
 Compare strings up to a specified limit in a case-sensitive manner.
static int mod_xhtml_strnicmp (const char *str1, const char *str2, const int len)
 Compare strings up to a specified limit in a case-insensitive manner.
static int mod_xhtml_strendswith (const char *reference, const char *suffix, const int casecompare)
 Determines if the given string end with the given suffix.
static float mod_xhtml_atoq (const char *string)
 Parse quality value.
static const char * get_default_charset (request_rec *r)
 This naughty function goes digging into the Apache http_core module to find the default character set.
static extension_recfind_extension (apr_array_header_t *extensions, const char *ext)
 Given an array of extension_rec records, find one with the given file extension.
static int count_stars (const char *content_type)
 Count the number of stars in an Accept content token.
static char * reconstruct_content_type (apr_pool_t *p, accept_rec *content_rec)
 Reconstruct the original content-type, if required due to special "charset" values.
static void make_etag_hashcode (apr_pool_t *p, accept_rec *rec)
 Make a simple hash code for the given accept_rec structure.
static accept_recmake_default_accept (apr_pool_t *p, accept_rec *rec, const char *default_charset)
 Make an accept_rec with an explicit charset.
static char * merge_validators (apr_pool_t *p, char *old_variant, char *new_variant)
 Merge vlist_validators from different modules.
static extension_recget_extension_for_filename (apr_array_header_t *extensions, const char *filename)
 For a given filename, scan through an array of extension_rec records until we find a match.
static const char * get_entry (apr_pool_t *p, accept_rec *result, const char *accept_line)
 Get a single mime type entry --- one media type and parameters; enter the values we recognize into the argument accept_rec.
static
apr_array_header_t * 
do_accept_line (apr_pool_t *p, const char *accept_line)
 Parse and tokenise Accept header lines.
static
apr_array_header_t * 
do_accept_charset_line (apr_pool_t *p, const char *accept_charset_line)
 Parse and tokenise Accept-Charset headers.
static float charsets_match (apr_array_header_t *accept_charsets, const char *content_charset)
 Look for a matching charset within the given array of accept_recs.
static float types_match (accept_rec *content_type, accept_rec *content_accept, apr_array_header_t *accept_charsets, const char *default_charset)
 Determine if a given content_type record matches the criteria of the given content_accept record.
static accept_recbest_match (apr_array_header_t *accept_type, apr_array_header_t *content_type, apr_array_header_t *accept_charset, const char *default_charset, int stars_to_match, xhtml_neg_state *xns, apr_pool_t *p)
 Implements the best match algorithm for a given Accept header, possible content-type headers and an optional default content-type in the event of a star-slash-star match.
static void set_vary_header (request_rec *r)
 For HTTP 1.1 responses, where the content has been modified based on HTTP request headers, need to set the Vary header to name the headers that the variation was based on.
int xhtml_negotiate (request_rec *r)
 The main routine for the content-negotiation phase of this module goes here.
static const char * set_xhtml_active (cmd_parms *cmd, void *in_dir_config, int active)
 Is this module active for the given directory.
static const char * set_xhtml_cache_negotiated (cmd_parms *cmd, void *dummy, int cache)
 Set whether HTTP 1.0 caching should be allowed.
static const char * add_xhtml_type (cmd_parms *cmd, void *in_dir_config, const char *ext, const char *type)
 Add content types for the given file extension.
static const char * add_xhtml_ignore (cmd_parms *cmd, void *in_dir_config, const char *stars)
 Set the minimun number of stars in an Accept token that should be ignored when performing negotiation.
static const char * add_xhtml_log (cmd_parms *cmd, void *dummy, const char *logfile)
 Set the log file name for this module.
static void * make_xhtml_neg_state (apr_pool_t *p, server_rec *s)
 Create a default XHTML negotiation module configuration record.
static void * create_xhtml_dir_config (apr_pool_t *p, char *dummy)
 Create a default directory configuration module.
static void * merge_xhtml_dir_configs (apr_pool_t *p, void *basev, void *addv)
 Merge configuration info from different directories.
static int init_xhtml_log (apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
 Initialize the log file, if one is specified.
static void xhtml_register_hooks (apr_pool_t *p)
 Register specific hooks during request processing.

Variables

static int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE)
 Read/write flags used when opening the log file.
static apr_fileperms_t xfer_perms = APR_OS_DEFAULT
 Permission flags used when opening the log file.
module
AP_MODULE_DECLARE_DATA 
xhtml_neg_module
command_rec config_xhtml_cmds []
 Record for registering commands with the Apache 2.0.x server.
module
AP_MODULE_DECLARE_DATA 
xhtml_neg_module
 Module declaration for registering hooks into the Apache 2.0.x server.


Detailed Description

The main source file for the mod_xhtml_neg module.

This file implements the module for Apache 2.x servers.


Define Documentation

#define DEFAULT_CHARSET_NAME   "iso-8859-1"

The default HTTP character set for most content types.

#define DEFAULT_TEXT_XML_CHARSET   "us-ascii"

The default HTTP character set for text/xml and text/xml-external-entity content types.

#define mod_xhtml_strempty ( str   )     (((str) == NULL) || (*(str) == '\0'))

Test whether a string is empty without having to do a full scan of the string just to return a length, as per strlen.

Parameters:
str a string pointer, possibly NULL, to be tested
Returns:
a true value if the pointer is NULL, or points to a zero-length string, otherwise a false value


Enumeration Type Documentation

enum xhtml_neg_active

Enumeration for determining whether processing should be enabled for this module.

By default, we set the directory config state to active_dontcare, meaning the value hasn't been explicitly set. When it is set by XhtmlNegActive, the directory config will be set to either active_on or active_off.

Enumerator:
active_on  processing should be active
active_off  processing should not be active
active_dontcare  default processing state (not active)

enum http_caching

Enumeration for determining whether HTTP 1.0 caching should be allowed for negotiated documents.

By default, we set the server config state to caching_dontcare, meaning the value hasn't been explicitly set. When it set by the XhtmlNegCache parameter, the server config will be set to either caching_on or caching_off.

Enumerator:
caching_on  HTTP 1.0 caching should be active.
caching_off  HTTP 1.0 caching should not be active.
caching_dontcare  default processing state (not active)


Function Documentation

static int mod_xhtml_strlen ( const char *  str  )  [static]

Calculate the length of the given string.

The pointer may be NULL.

Parameters:
str a string pointer, possibly NULL, to be tested
Returns:
the length of the string if the pointer is non-NULL, otherwise zero

static int mod_xhtml_strcmp ( const char *  str1,
const char *  str2 
) [static]

Compare one string against another in a case-sensitive manner.

Will correctly deal with either pointer being NULL.

Parameters:
str1 the first string pointer to be compared
str2 the second string pointer to be compared
Returns:
zero if the strings are identical, less than zero if the first string is less than the second, or greater than zero if the first string is greater than the second

static int mod_xhtml_stricmp ( const char *  str1,
const char *  str2 
) [static]

Compare one string against another in a case-insensitive manner.

Will correctly deal with either pointer being NULL.

Parameters:
str1 the first string pointer to be compared
str2 the second string pointer to be compared
Returns:
zero if the strings are identical, less than zero if the first string is less than the second, or greater than zero if the first string is greater than the second

static int mod_xhtml_strncmp ( const char *  str1,
const char *  str2,
const int  len 
) [static]

Compare strings up to a specified limit in a case-sensitive manner.

If a NULL byte is encountered, finish comparing at that character. Will correctly deal with either pointer being NULL.

Parameters:
str1 the first string pointer to be compared
str2 the second string pointer to be compared
len the maximum number of characters to be compared
Returns:
zero if the substrings are identical, less than zero if the first substring is less than the second, or greater than zero if the first substring is greater than the second

static int mod_xhtml_strnicmp ( const char *  str1,
const char *  str2,
const int  len 
) [static]

Compare strings up to a specified limit in a case-insensitive manner.

If a NULL byte is encountered, finish comparing at that character. Will correctly deal with either pointer being NULL.

Parameters:
str1 the first string pointer to be compared
str2 the second string pointer to be compared
len the maximum number of characters to be compared
Returns:
zero if the substrings are identical, less than zero if the first substring is less than the second, or greater than zero if the first substring is greater than the second

static int mod_xhtml_strendswith ( const char *  reference,
const char *  suffix,
const int  casecompare 
) [static]

Determines if the given string end with the given suffix.

Case matching can be enabled or disabled.

Parameters:
reference the reference string to be tested, possibly NULL
suffix the suffix to test against, possibly NULL
casecompare non-zero if a case sensitive compare is wanted, otherwise zero for a case insensitive compare
Returns:
non-zero if the suffix matches, otherwise zero

Is the reference string at least as long as the suffix?

static float mod_xhtml_atoq ( const char *  string  )  [static]

Parse quality value.

atof(3) is not globally usable here, because it depends on the locale (argh).

However, RFC 2616 states:
3.9 Quality Values

[...] HTTP/1.1 applications MUST NOT generate more than three digits after the decimal point. User configuration of these values SHOULD also be limited in this fashion.

qvalue = ( "0" [ "." 0*3DIGIT ] ) | ( "1" [ "." 0*3("0") ] )

This is quite easy. If the supplied string doesn't match the above definition (loosely), we simply return 1 (same as if there's no q-value)

Parameters:
string a string containing a q-value to be parsed
Returns:
the parsed q-value, truncated to three decimal places if necessary

static const char* get_default_charset ( request_rec *  r  )  [static]

This naughty function goes digging into the Apache http_core module to find the default character set.

Parameters:
r the current HTTP request from which we can determine the configuration of the Apache core
Returns:
the default character set as configured in the Apache core, or NULL if no value is configured
Todo:
Is there a cleaner way to find the default charset information?

static extension_rec* find_extension ( apr_array_header_t *  extensions,
const char *  ext 
) [static]

Given an array of extension_rec records, find one with the given file extension.

If a match is found, return the extension_rec, otherwise return NULL.

Parameters:
extensions an array of extension_rec elements to be scanned
ext the file extension to match
Returns:
the matching extension_rec item if one was found, otherwise NULL

static int count_stars ( const char *  content_type  )  [static]

Count the number of stars in an Accept content token.

This helps determine priority in case of a tie in q-value priorities.

Parameters:
content_type the content token that we're interested in, possibly NULL
Returns:
the number of '*' characters in the string, or 0 if the string is NULL

static char* reconstruct_content_type ( apr_pool_t *  p,
accept_rec content_rec 
) [static]

Reconstruct the original content-type, if required due to special "charset" values.

Normally the content-type will be a plain string without charset encoding. In these cases, just return the content-type without modification. In the case where we also have a charset entry, have to create the full content-type string.

Parameters:
p a memory pool from which we can allocate temporary memory for this request
content_rec the accept_rec that matched the content negotiation
Returns:
a string that contains a correctly formatted content-type value, or NULL if there is no valid record
Todo:
should we send the "profile" parameter as well?

static void make_etag_hashcode ( apr_pool_t *  p,
accept_rec rec 
) [static]

Make a simple hash code for the given accept_rec structure.

This allows us to generate unique Etags for accepted content-types. The Etag consists of a hash of each character. This is then represented as a zero padded hexadecimal number.

The data that we use to generate the hash is the content-type and charset, concatenated as one long string.

The hash function is a hash in the public domain by By Bob Jenkins. See lookupa.c or http://burtleburtle.net/bob/hash/doobs.html for details.

Note:
Since we're only using this algorithm for Etag uniqueness rather than for a hashtable lookup, DoS attacks such as those described in http://www.cs.rice.edu/~scrosby/hash/ are not an issue here, especially since our hash keys come from data in either httpd.conf or .htaccess files.
Parameters:
p a memory pool from which we can allocate temporary memory for this request
rec the accept_rec that matched the content negotiation; the hash value will be added to this structure
Todo:
If the content-type returned ever includes the "profile" parameter, include it as part of the hash code.

static accept_rec* make_default_accept ( apr_pool_t *  p,
accept_rec rec,
const char *  default_charset 
) [static]

Make an accept_rec with an explicit charset.

If the record already contains a charset parameter, it is returned as-is. Otherwise, the parameter default_charset is used.

Note:
default_charset can be NULL, in which case charset will be set to the empty string.
Parameters:
p a memory pool from which we can allocate temporary memory for this request
rec the accept_rec that matched the content negotiation
default_charset the default character set as configured in the Apache core
Returns:
the original accept_rec if it already contains a charset parameter, otherwise a new accept_rec containing the same information and the default charset parameter

static char* merge_validators ( apr_pool_t *  p,
char *  old_variant,
char *  new_variant 
) [static]

Merge vlist_validators from different modules.

This code is adapted from code in http_protocol.c in the Apache 1.3 core.

Parameters:
p a memory pool from which we can allocate temporary memory for this request
old_variant an Etag variant taken from the vlist_validator parameter of the current request
new_variant an Etag variant as supplied by make_etag_hashcode
Returns:
a correctly merged Etag variant, taking into account whether either variant has a weak validator attached, or whether either variant is NULL

static extension_rec* get_extension_for_filename ( apr_array_header_t *  extensions,
const char *  filename 
) [static]

For a given filename, scan through an array of extension_rec records until we find a match.

The first match wins. If no match is found, NULL is returned.

Parameters:
extensions an array of extension_rec elements containing filename suffixes to be matched
filename the filename we want to match
Returns:
the matching extension_rec item if one exists, otherwise NULL

static const char* get_entry ( apr_pool_t *  p,
accept_rec result,
const char *  accept_line 
) [static]

Get a single mime type entry --- one media type and parameters; enter the values we recognize into the argument accept_rec.

Parameters:
p a memory pool from which we can allocate temporary memory for this request
result the accept_rec to be populated by parsing the accept_line
accept_line a string containing the accept token to be parsed
Returns:
any remaining accept_line string left to be parsed

static apr_array_header_t* do_accept_line ( apr_pool_t *  p,
const char *  accept_line 
) [static]

Parse and tokenise Accept header lines.

The Accept request header is handled by do_accept_line() - it has the basic structure of a list of items of the format:

name; q=N; charset=TEXT; profile="URI"

Since this header is very similar in structure to the Accept-Charset request header, we can reuse the parsing in get_entry.

The profile parameter was added to handle section 8 of RFC 3236. These probably won't appear at an origin server, but we handle them explicitly if they do.

Parameters:
p a memory pool from which we can allocate temporary memory for this request
accept_line the HTTP Accept request value to be parsed
Returns:
an array of accept_rec items containing the parsed Accept tokens

static apr_array_header_t* do_accept_charset_line ( apr_pool_t *  p,
const char *  accept_charset_line 
) [static]

Parse and tokenise Accept-Charset headers.

There is an extra record inserted when neither "*" nor "ISO-8859-1" are mentioned.

Parameters:
p a memory pool from which we can allocate temporary memory for this request
accept_charset_line the HTTP Accept-Charset request value to be parsed
Returns:
an array of accept_rec items containing the parsed Accept-Charset tokens

Merge new file extensions into new array

static float charsets_match ( apr_array_header_t *  accept_charsets,
const char *  content_charset 
) [static]

Look for a matching charset within the given array of accept_recs.

If we find a match, return the corresponding q value, otherwise return 0.0.

Parameters:
accept_charsets an array of accept_rec items containing Accept-Charset tokens to be matched
content_charset the target charset parameter to match
Returns:
the q value of the best match, or 0.0f if no match was found

static float types_match ( accept_rec content_type,
accept_rec content_accept,
apr_array_header_t *  accept_charsets,
const char *  default_charset 
) [static]

Determine if a given content_type record matches the criteria of the given content_accept record.

Returns a quality value for the given variables.

First, check whether names match, depending on whether any stars are present in the content_accept record. If the first character is a star, then we assume its a total wildcard match. Otherwise, look for the major content-type only.

Once we match based on names, check whether a charset is present in the content_accept record. If so, need to match charsets exactly. Otherwise, no further check is needed, and we can return true immediately.

The quality value returned is a straight multiplication of all the q-values the went into determining the result: the q value from the config file (if any was specified), the q value of the Accept content-type header, and the q value of the Accept-Charset header. Where no q-value is specified, the value "1.0" is used.

Parameters:
content_type the content-type token to match
content_accept the accept token to be matched
accept_charsets an array of accept_rec items containing Accept-Charset tokens to be matched
default_charset the default charset value as configured in the Apache core
Returns:
the q value of the best matching content-type and charset combination, or 0.0f if no match was found

static accept_rec* best_match ( apr_array_header_t *  accept_type,
apr_array_header_t *  content_type,
apr_array_header_t *  accept_charset,
const char *  default_charset,
int  stars_to_match,
xhtml_neg_state xns,
apr_pool_t *  p 
) [static]

Implements the best match algorithm for a given Accept header, possible content-type headers and an optional default content-type in the event of a star-slash-star match.

Parameters:
accept_type an array of accept_rec items containing Accept tokens
content_type an array of accept_rec items containing possible content-type tokens to be matched
accept_charset an array of accept_rec items containing Accept-Charset tokens
default_charset the default character set as configured in the Apache core
stars_to_match the number of '*' tokens at which we start ignoring the Accept token
xns the configuration state of the module in case we need to write to the log file
p a memory pool from which we can allocate temporary memory for this request
Returns:
an accept_rec structure containing the best match we could find for the current request, or NULL if we couldn't find a match

static void set_vary_header ( request_rec *  r  )  [static]

For HTTP 1.1 responses, where the content has been modified based on HTTP request headers, need to set the Vary header to name the headers that the variation was based on.

For some requests, this cannot be determined by headers alone, and a "*" needs to be sent instead.

Here we first check any existing Vary header for the presence of a "*". If "Vary: *" exists, return it unaltered. Otherwise, we need to merge two new tokens: "Accept" and "Accept-Charset".

Parameters:
r the current HTTP request to which we merge Vary tokens

int xhtml_negotiate ( request_rec *  r  ) 

The main routine for the content-negotiation phase of this module goes here.

This is mainly setup, control flow and logging going on here.

Note:
Due to the way we hook into the Apache handler system means we always return DECLINED, even though handling was a success. We do this so that the default handler will always run, and so that ETag is handled correctly for both 200 OK and 304 Not Modified cases.
Parameters:
r the current HTTP request
Returns:
DECLINED, to indicate the request should be processed by the next handler in the chain

static const char* set_xhtml_active ( cmd_parms *  cmd,
void *  in_dir_config,
int  active 
) [static]

Is this module active for the given directory.

Parameters:
in_dir_config the configuration information for this directory
active a yes/no flag indicating whether the module should be active
Returns:
NULL to indicate that processing was successful

static const char* set_xhtml_cache_negotiated ( cmd_parms *  cmd,
void *  dummy,
int  cache 
) [static]

Set whether HTTP 1.0 caching should be allowed.

Default to "no" unless specifically overridden.

Parameters:
cmd the configuration information for this module on a per-server basis
cache a flag indicating whether HTTP 1.0 caching should be enabled
Returns:
NULL to indicate that processing was successful

static const char* add_xhtml_type ( cmd_parms *  cmd,
void *  in_dir_config,
const char *  ext,
const char *  type 
) [static]

Add content types for the given file extension.

Parameters:
in_dir_config the configuration information for this directory
ext the file extension we're configuring
type the content type to be added for this file extension
Returns:
NULL to indicate that processing was successful

static const char* add_xhtml_ignore ( cmd_parms *  cmd,
void *  in_dir_config,
const char *  stars 
) [static]

Set the minimun number of stars in an Accept token that should be ignored when performing negotiation.

Parameters:
in_dir_config the configuration information for this directory
stars the number of '*' tokens at which we start ignoring any Accept tokens
Returns:
NULL to indicate that processing was successful

static const char* add_xhtml_log ( cmd_parms *  cmd,
void *  dummy,
const char *  logfile 
) [static]

Set the log file name for this module.

Parameters:
cmd the configuration information for this module on a per-server basis
logfile the name of the log file to which the module should write
Returns:
NULL to indicate that processing was successful

static void* make_xhtml_neg_state ( apr_pool_t *  p,
server_rec *  s 
) [static]

Create a default XHTML negotiation module configuration record.

Parameters:
p a memory pool from which we can allocate memory that can later be recycled
Returns:
a new per-server configuration structure for this module

static void* create_xhtml_dir_config ( apr_pool_t *  p,
char *  dummy 
) [static]

Create a default directory configuration module.

Parameters:
p a memory pool from which we can allocate memory that can later be recycled
Returns:
a new per-directory configuration structure for this module

static void* merge_xhtml_dir_configs ( apr_pool_t *  p,
void *  basev,
void *  addv 
) [static]

Merge configuration info from different directories.

Parameters:
p a memory pool from which we can allocate memory that can later be recycled
basev the base directory configuration to be merged
addv the additional directory configuration to be merged
Returns:
a merged per-directory configuration structure for this module

Merge new file extensions into new array

static int init_xhtml_log ( apr_pool_t *  pconf,
apr_pool_t *  plog,
apr_pool_t *  ptemp,
server_rec *  s 
) [static]

Initialize the log file, if one is specified.

Parameters:
s the server configuration from which we derive the state of this module
p a memory pool from which we can allocate memory that can later be recycled

static void xhtml_register_hooks ( apr_pool_t *  p  )  [static]

Register specific hooks during request processing.


Variable Documentation

int xfer_flags = (APR_WRITE | APR_APPEND | APR_CREATE) [static]

Read/write flags used when opening the log file.

apr_fileperms_t xfer_perms = APR_OS_DEFAULT [static]

Permission flags used when opening the log file.

command_rec config_xhtml_cmds[]

Initial value:

 {
    AP_INIT_FLAG("XhtmlNegActive", set_xhtml_active, NULL, OR_INDEXES,
                 "Is this module enabled? Defaults to no"),
    AP_INIT_TAKE1("XhtmlNegLog", add_xhtml_log, NULL, RSRC_CONF,
                  "A file name for an output log file"),
    AP_INIT_ITERATE2("XhtmlNegTypes", add_xhtml_type, NULL, OR_INDEXES,
                  "A file extension followed by one or more matching "
                  "content-type strings"),
    AP_INIT_TAKE1("XhtmlNegStarsIgnore", add_xhtml_ignore, NULL, OR_INDEXES,
                  "The number of stars in an Accept header which should be "
                  "ignored if we match them"),
    AP_INIT_FLAG("XhtmlNegCache", set_xhtml_cache_negotiated, NULL, RSRC_CONF,
                 "Should negotiated HTTP 1.0 requests be cacheable? "
                 "Defaults to no"),
    { NULL }
}
Record for registering commands with the Apache 2.0.x server.

module AP_MODULE_DECLARE_DATA xhtml_neg_module

Initial value:

Module declaration for registering hooks into the Apache 2.0.x server.


Generated on Wed Jan 2 22:37:53 2008 for XHTML Negotiation Module by  doxygen 1.5.3