📄 mod_negotiation.c
字号:
} } } return (float)i / 1000.0f; } return 0.0f;}/* * 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); /* Look for 'var = value' --- and make sure the var is in lcase. */ for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) { *cp = apr_tolower(*cp); } if (!*cp) { continue; /* No '='; just ignore it. */ } *cp++ = '\0'; /* Delimit var */ while (*cp && (apr_isspace(*cp) || *cp == '=')) { ++cp; } if (*cp == '"') { ++cp; for (end = cp; (*end && *end != '\n' && *end != '\r' && *end != '\"'); end++); } else { for (end = cp; (*end && !apr_isspace(*end)); end++); } if (*end) { *end = '\0'; /* strip ending quote or return */ } ap_str_tolower(cp); if (parm[0] == 'q' && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) { result->quality = atoq(cp); } else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) { result->level = (float)atoi(cp); } else if (!strcmp(parm, "charset")) { result->charset = cp; } } if (*accept_line == ',') { ++accept_line; } return accept_line;}/***************************************************************** * * Dealing with header lines ... * * Accept, Accept-Charset, Accept-Language and Accept-Encoding * are handled by do_header_line() - they all have the same * basic structure of a list of items of the format * name; q=N; charset=TEXT * * where charset is only valid in Accept. */static apr_array_header_t *do_header_line(apr_pool_t *p, const char *accept_line){ apr_array_header_t *accept_recs; if (!accept_line) { return NULL; } accept_recs = apr_array_make(p, 40, sizeof(accept_rec)); while (*accept_line) { accept_rec *new = (accept_rec *) apr_array_push(accept_recs); accept_line = get_entry(p, new, accept_line); } return accept_recs;}/* Given the text of the Content-Languages: line from the var map file, * return an array containing the languages of this variant */static apr_array_header_t *do_languages_line(apr_pool_t *p, const char **lang_line){ apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *)); if (!lang_line) { return lang_recs; } while (**lang_line) { char **new = (char **) apr_array_push(lang_recs); *new = ap_get_token(p, lang_line, 0); ap_str_tolower(*new); if (**lang_line == ',' || **lang_line == ';') { ++(*lang_line); } } return lang_recs;}/***************************************************************** * * Handling header lines from clients... */static negotiation_state *parse_accept_headers(request_rec *r){ negotiation_state *new = (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state)); accept_rec *elts; apr_table_t *hdrs = r->headers_in; int i; new->pool = r->pool; new->r = r; new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config, &negotiation_module); new->dir_name = ap_make_dirstr_parent(r->pool, r->filename); new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept")); /* calculate new->accept_q value */ if (new->accepts) { elts = (accept_rec *) new->accepts->elts; for (i = 0; i < new->accepts->nelts; ++i) { if (elts[i].quality < 1.0) { new->accept_q = 1; } } } new->accept_encodings = do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding")); new->accept_langs = do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language")); new->accept_charsets = do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset")); /* This is possibly overkill for some servers, heck, we have * only 33 index.html variants in docs/docroot (today). * Make this configurable? */ new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec)); return new;}static void parse_negotiate_header(request_rec *r, negotiation_state *neg){ const char *negotiate = apr_table_get(r->headers_in, "Negotiate"); char *tok; /* First, default to no TCN, no Alternates, and the original Apache * negotiation algorithm with fiddles for broken browser configs. * * To save network bandwidth, we do not configure to send an * Alternates header to the user agent by default. User * agents that want an Alternates header for agent-driven * negotiation will have to request it by sending an * appropriate Negotiate header. */ neg->ua_supports_trans = 0; neg->send_alternates = 0; neg->may_choose = 1; neg->use_rvsa = 0; neg->dont_fiddle_headers = 0; if (!negotiate) return; if (strcmp(negotiate, "trans") == 0) { /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they * do not support transparent content negotiation, so for Lynx we * ignore the negotiate header when its contents are exactly "trans". * If future versions of Lynx ever need to say 'negotiate: trans', * they can send the equivalent 'negotiate: trans, trans' instead * to avoid triggering the workaround below. */ const char *ua = apr_table_get(r->headers_in, "User-Agent"); if (ua && (strncmp(ua, "Lynx", 4) == 0)) return; } neg->may_choose = 0; /* An empty Negotiate would require 300 response */ while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) { if (strcmp(tok, "trans") == 0 || strcmp(tok, "vlist") == 0 || strcmp(tok, "guess-small") == 0 || apr_isdigit(tok[0]) || strcmp(tok, "*") == 0) { /* The user agent supports transparent negotiation */ neg->ua_supports_trans = 1; /* Send-alternates could be configurable, but note * that it must be 1 if we have 'vlist' in the * negotiate header. */ neg->send_alternates = 1; if (strcmp(tok, "1.0") == 0) { /* we may use the RVSA/1.0 algorithm, configure for it */ neg->may_choose = 1; neg->use_rvsa = 1; neg->dont_fiddle_headers = 1; } else if (tok[0] == '*') { /* we may use any variant selection algorithm, configure * to use the Apache algorithm */ neg->may_choose = 1; /* We disable header fiddles on the assumption that a * client sending Negotiate knows how to send correct * headers which don't need fiddling. */ neg->dont_fiddle_headers = 1; } } }#ifdef NEG_DEBUG ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d " "send_alternates=%d, may_choose=%d", neg->dont_fiddle_headers, neg->use_rvsa, neg->ua_supports_trans, neg->send_alternates, neg->may_choose);#endif}/* Sometimes clients will give us no Accept info at all; this routine sets * up the standard default for that case, and also arranges for us to be * willing to run a CGI script if we find one. (In fact, we set up to * dramatically prefer CGI scripts in cases where that's appropriate, * e.g., POST or when URI includes query args or extra path info). */static void maybe_add_default_accepts(negotiation_state *neg, int prefer_scripts){ accept_rec *new_accept; if (!neg->accepts) { neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec)); new_accept = (accept_rec *) apr_array_push(neg->accepts); new_accept->name = "*/*"; new_accept->quality = 1.0f; new_accept->level = 0.0f; } new_accept = (accept_rec *) apr_array_push(neg->accepts); new_accept->name = CGI_MAGIC_TYPE; if (neg->use_rvsa) { new_accept->quality = 0; } else { new_accept->quality = prefer_scripts ? 2.0f : 0.001f; } new_accept->level = 0.0f;}/***************************************************************** * * Parsing type-map files, in Roy's meta/http format augmented with * #-comments. *//* Reading RFC822-style header lines, ignoring #-comments and * handling continuations. */enum header_state { header_eof, header_seen, header_sep};static enum header_state get_header_line(char *buffer, int len, apr_file_t *map){ char *buf_end = buffer + len; char *cp; char c; /* Get a noncommented line */ do { if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) { return header_eof; } } while (buffer[0] == '#'); /* If blank, just return it --- this ends information on this variant */ for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) { continue; } if (*cp == '\0') { return header_sep; } /* If non-blank, go looking for header lines, but note that we still * have to treat comments specially... */ cp += strlen(cp); /* We need to shortcut the rest of this block following the Body: * tag - we will not look for continutation after this line. */ if (!strncasecmp(buffer, "Body:", 5)) return header_seen; while (apr_file_getc(&c, map) != APR_EOF) { if (c == '#') { /* Comment line */ while (apr_file_getc(&c, map) != APR_EOF && c != '\n') { continue; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -