📄 mod_negotiation.c
字号:
/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * mod_negotiation.c: keeps track of MIME types the client is willing to * accept, and contains code to handle type arbitration. * * rst */#include "apr.h"#include "apr_strings.h"#include "apr_file_io.h"#include "apr_lib.h"#define APR_WANT_STRFUNC#include "apr_want.h"#include "ap_config.h"#include "httpd.h"#include "http_config.h"#include "http_request.h"#include "http_protocol.h"#include "http_core.h"#include "http_log.h"#include "util_script.h"#define MAP_FILE_MAGIC_TYPE "application/x-type-map"/* Commands --- configuring document caching on a per (virtual?) * server basis... */typedef struct { int forcelangpriority; apr_array_header_t *language_priority;} neg_dir_config;/* forcelangpriority flags */#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */#define FLP_PREFER 2 /* Use language_priority rather than MC */#define FLP_FALLBACK 4 /* Use language_priority rather than NA */#define FLP_DEFAULT FLP_PREFERmodule AP_MODULE_DECLARE_DATA negotiation_module;static void *create_neg_dir_config(apr_pool_t *p, char *dummy){ neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config)); new->forcelangpriority = FLP_UNDEF; new->language_priority = NULL; return new;}static void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv){ neg_dir_config *base = (neg_dir_config *) basev; neg_dir_config *add = (neg_dir_config *) addv; neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config)); /* give priority to the config in the subdirectory */ new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF) ? add->forcelangpriority : base->forcelangpriority; new->language_priority = add->language_priority ? add->language_priority : base->language_priority; return new;}static const char *set_language_priority(cmd_parms *cmd, void *n_, const char *lang){ neg_dir_config *n = n_; const char **langp; if (!n->language_priority) n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *)); langp = (const char **) apr_array_push(n->language_priority); *langp = lang; return NULL;}static const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w){ neg_dir_config *n = n_; if (!strcasecmp(w, "None")) { if (n->forcelangpriority & ~FLP_NONE) { return "Cannot combine ForceLanguagePriority options with None"; } n->forcelangpriority = FLP_NONE; } else if (!strcasecmp(w, "Prefer")) { if (n->forcelangpriority & FLP_NONE) { return "Cannot combine ForceLanguagePriority options None and " "Prefer"; } n->forcelangpriority |= FLP_PREFER; } else if (!strcasecmp(w, "Fallback")) { if (n->forcelangpriority & FLP_NONE) { return "Cannot combine ForceLanguagePriority options None and " "Fallback"; } n->forcelangpriority |= FLP_FALLBACK; } else { return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ", w, NULL); } return NULL;}static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy, int arg){ ap_set_module_config(cmd->server->module_config, &negotiation_module, (arg ? "Cache" : NULL)); return NULL;}static int do_cache_negotiated_docs(server_rec *s){ return (ap_get_module_config(s->module_config, &negotiation_module) != NULL);}static const command_rec negotiation_cmds[] ={ AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, "Either 'on' or 'off' (default)"), AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL, OR_FILEINFO, "space-delimited list of MIME language abbreviations"), AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL, OR_FILEINFO, "Force LanguagePriority elections, either None, or " "Fallback and/or Prefer"), {NULL}};/* * Record of available info on a media type specified by the client * (we also use 'em for encodings and languages) */typedef struct accept_rec { char *name; /* MUST be lowercase */ float quality; float level; char *charset; /* for content-type only */} accept_rec;/* * Record of available info on a particular variant * * Note that a few of these fields are updated by the actual negotiation * code. These are: * * level_matched --- initialized to zero. Set to the value of level * if the client actually accepts this media type at that * level (and *not* if it got in on a wildcard). See level_cmp * below. * mime_stars -- initialized to zero. Set to the number of stars * present in the best matching Accept header element. * 1 for star/star, 2 for type/star and 3 for * type/subtype. * * definite -- initialized to 1. Set to 0 if there is a match which * makes the variant non-definite according to the rules * in rfc2296. */typedef struct var_rec { request_rec *sub_req; /* May be NULL (is, for map files) */ const char *mime_type; /* MUST be lowercase */ const char *file_name; /* Set to 'this' (for map file body content) */ apr_off_t body; /* Only for map file body content */ const char *content_encoding; apr_array_header_t *content_languages; /* list of lang. for this variant */ const char *content_charset; const char *description; /* The next five items give the quality values for the dimensions * of negotiation for this variant. They are obtained from the * appropriate header lines, except for source_quality, which * is obtained from the variant itself (the 'qs' parameter value * from the variant's mime-type). Apart from source_quality, * these values are set when we find the quality for each variant * (see best_match()). source_quality is set from the 'qs' parameter * of the variant description or mime type: see set_mime_fields(). */ float lang_quality; /* quality of this variant's language */ float encoding_quality; /* ditto encoding */ float charset_quality; /* ditto charset */ float mime_type_quality; /* ditto media type */ float source_quality; /* source quality for this variant */ /* Now some special values */ float level; /* Auxiliary to content-type... */ apr_off_t bytes; /* content length, if known */ int lang_index; /* Index into LanguagePriority list */ int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */ /* Above are all written-once properties of the variant. The * three fields below are changed during negotiation: */ float level_matched; int mime_stars; int definite;} var_rec;/* Something to carry around the state of negotiation (and to keep * all of this thread-safe)... */typedef struct { apr_pool_t *pool; request_rec *r; neg_dir_config *conf; char *dir_name; int accept_q; /* 1 if an Accept item has a q= param */ float default_lang_quality; /* fiddle lang q for variants with no lang */ /* the array pointers below are NULL if the corresponding accept * headers are not present */ apr_array_header_t *accepts; /* accept_recs */ apr_array_header_t *accept_encodings; /* accept_recs */ apr_array_header_t *accept_charsets; /* accept_recs */ apr_array_header_t *accept_langs; /* accept_recs */ apr_array_header_t *avail_vars; /* available variants */ int count_multiviews_variants; /* number of variants found on disk */ int is_transparent; /* 1 if this resource is trans. negotiable */ int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */ int ua_supports_trans; /* 1 if ua supports trans negotiation */ int send_alternates; /* 1 if we want to send an Alternates header */ int may_choose; /* 1 if we may choose a variant for the client */ int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */} negotiation_state;/* A few functions to manipulate var_recs. * Cleaning out the fields... */static void clean_var_rec(var_rec *mime_info){ mime_info->sub_req = NULL; mime_info->mime_type = ""; mime_info->file_name = ""; mime_info->body = 0; mime_info->content_encoding = NULL; mime_info->content_languages = NULL; mime_info->content_charset = ""; mime_info->description = ""; mime_info->is_pseudo_html = 0; mime_info->level = 0.0f; mime_info->level_matched = 0.0f; mime_info->bytes = -1; mime_info->lang_index = -1; mime_info->mime_stars = 0; mime_info->definite = 1; mime_info->charset_quality = 1.0f; mime_info->encoding_quality = 1.0f; mime_info->lang_quality = 1.0f; mime_info->mime_type_quality = 1.0f; mime_info->source_quality = 0.0f;}/* Initializing the relevant fields of a variant record from the * accept_info read out of its content-type, one way or another. */static void set_mime_fields(var_rec *var, accept_rec *mime_info){ var->mime_type = mime_info->name; var->source_quality = mime_info->quality; var->level = mime_info->level; var->content_charset = mime_info->charset; var->is_pseudo_html = (!strcmp(var->mime_type, "text/html") || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE) || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));}/* Create a variant list validator in r using info from vlistr. */static void set_vlist_validator(request_rec *r, request_rec *vlistr){ /* Calculating the variant list validator is similar to * calculating an etag for the source of the variant list * information, so we use ap_make_etag(). Note that this * validator can be 'weak' in extreme case. */ ap_update_mtime(vlistr, vlistr->finfo.mtime); r->vlist_validator = ap_make_etag(vlistr, 0); /* ap_set_etag will later take r->vlist_validator into account * when creating the etag header */}/***************************************************************** * * Parsing (lists of) media types and their parameters, as seen in * HTTPD header lines and elsewhere. *//* * Get a single mime type entry --- one media type and parameters; * enter the values we recognize into the argument accept_rec */static const char *get_entry(apr_pool_t *p, accept_rec *result, const char *accept_line){ result->quality = 1.0f; result->level = 0.0f; result->charset = ""; /* * Note that this handles what I gather is the "old format", * * Accept: text/html text/plain moo/zot * * without any compatibility kludges --- if the token after the * MIME type begins with a semicolon, we know we're looking at parms, * otherwise, we know we aren't. (So why all the pissing and moaning * in the CERN server code? I must be missing something). */ result->name = ap_get_token(p, &accept_line, 0); ap_str_tolower(result->name); /* You want case insensitive, * you'll *get* case insensitive. */ /* KLUDGE!!! Default HTML to level 2.0 unless the browser * *explicitly* says something else. */ if (!strcmp(result->name, "text/html") && (result->level == 0.0)) { result->level = 2.0f; } else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) { result->level = 2.0f; } else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) { result->level = 3.0f; } while (*accept_line == ';') { /* Parameters ... */ char *parm; char *cp; char *end; ++accept_line; parm = ap_get_token(p, &accept_line, 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -