📄 mod_negotiation.c
字号:
for (cp = desc; *cp; ++cp) { if (*cp=='\n') *cp=' '; } if (cp>desc) *(cp-1)=0; mime_info.description = desc; } } else { if (*mime_info.file_name && has_content) { void *new_var = ap_push_array(neg->avail_vars); memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); } clean_var_rec(&mime_info); has_content = 0; } } while (hstate != header_eof); ap_pfclose(neg->pool, map); set_vlist_validator(r, rr); return OK;}/* Sort function used by read_types_multi. */static int variantsortf(var_rec *a, var_rec *b) { /* First key is the source quality, sort in descending order. */ /* XXX: note that we currently implement no method of setting the * source quality for multiviews variants, so we are always comparing * 1.0 to 1.0 for now */ if (a->source_quality < b->source_quality) return 1; if (a->source_quality > b->source_quality) return -1; /* Second key is the variant name */ return strcmp(a->file_name, b->file_name);}/***************************************************************** * * Same as read_type_map, except we use a filtered directory listing * as the map... */static int read_types_multi(negotiation_state *neg){ request_rec *r = neg->r; char *filp; int prefix_len; DIR *dirp; struct DIR_TYPE *dir_entry; struct var_rec mime_info; struct accept_rec accept_info; void *new_var; struct { int any, all; } forbidden; clean_var_rec(&mime_info); if (!(filp = strrchr(r->filename, '/'))) { return DECLINED; /* Weird... */ } if (strncmp(r->filename, "proxy:", 6) == 0) { return DECLINED; } ++filp; prefix_len = strlen(filp); dirp = ap_popendir(neg->pool, neg->dir_name); if (dirp == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "cannot read directory for multi: %s", neg->dir_name); return HTTP_FORBIDDEN; } forbidden.any = 0; forbidden.all = 1; while ((dir_entry = readdir(dirp))) { array_header *exception_list; request_rec *sub_req; /* Do we have a match? */#ifdef CASE_BLIND_FILESYSTEM if (strncasecmp(dir_entry->d_name, filp, prefix_len)) {#else if (strncmp(dir_entry->d_name, filp, prefix_len)) {#endif continue; } if (dir_entry->d_name[prefix_len] != '.') { continue; } /* Yep. See if it's something which we have access to, and * which has a known type and encoding (as opposed to something * which we'll be slapping default_type on later). */ sub_req = ap_sub_req_lookup_file(dir_entry->d_name, r); /* If it has a handler, we'll pretend it's a CGI script, * since that's a good indication of the sort of thing it * might be doing. */ if (sub_req->handler && !sub_req->content_type) { sub_req->content_type = CGI_MAGIC_TYPE; } /* HTTP_FORBIDDEN is returned, e.g., if the path length limit was exceeded */ /* HTTP_OK does NOT necessarily mean that the file is really readable! */ if (sub_req->status == HTTP_OK) forbidden.all = 0; else if (sub_req->status == HTTP_FORBIDDEN) forbidden.any = 1; /* * mod_mime will _always_ provide us the base name in the * ap-mime-exception-list, if it processed anything. If * this list is empty, give up immediately, there was * nothing interesting. For example, looking at the files * readme.txt and readme.foo, we will throw away .foo if * it's an insignificant file (e.g. did not identify a * language, charset, encoding, content type or handler,) */ exception_list = (array_header *) ap_table_get(sub_req->notes, "ap-mime-exceptions-list"); if (!exception_list) { ap_destroy_sub_req(sub_req); continue; } /* Each unregonized bit better match our base name, in sequence. * A test of index.html.foo will match index.foo or index.html.foo, * but it will never transpose the segments and allow index.foo.html * because that would introduce too much CPU consumption. Better that * we don't attempt a many-to-many match here. */ { int nexcept = exception_list->nelts; char **cur_except = (char**)exception_list->elts; char *segstart = filp, *segend, saveend; while (*segstart && nexcept) { if (!(segend = strchr(segstart, '.'))) segend = strchr(segstart, '\0'); saveend = *segend; *segend = '\0';#ifdef CASE_BLIND_FILESYSTEM if (strcasecmp(segstart, *cur_except) == 0) {#else if (strcmp(segstart, *cur_except) == 0) {#endif --nexcept; ++cur_except; } if (!saveend) break; *segend = saveend; segstart = segend + 1; } if (nexcept) { /* Something you don't know is, something you don't know... */ ap_destroy_sub_req(sub_req); continue; } } /* * ###: be warned, the _default_ content type is already * picked up here! If we failed the subrequest, or don't * know what we are serving, then continue. */ if (sub_req->status != HTTP_OK || (!sub_req->content_type)) { ap_destroy_sub_req(sub_req); continue; } /* If it's a map file, we use that instead of the map * we're building... */ if (((sub_req->content_type) && !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) || ((sub_req->handler) && !strcmp(sub_req->handler, "type-map"))) { ap_pclosedir(neg->pool, dirp); neg->avail_vars->nelts = 0; if (sub_req->status != HTTP_OK) { return sub_req->status; } return read_type_map(neg, sub_req); } /* Have reasonable variant --- gather notes. */ mime_info.sub_req = sub_req; mime_info.file_name = ap_pstrdup(neg->pool, dir_entry->d_name); if (sub_req->content_encoding) { mime_info.content_encoding = sub_req->content_encoding; } if (sub_req->content_languages) { mime_info.content_languages = sub_req->content_languages; } get_entry(neg->pool, &accept_info, sub_req->content_type); set_mime_fields(&mime_info, &accept_info); new_var = ap_push_array(neg->avail_vars); memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); neg->count_multiviews_variants++; clean_var_rec(&mime_info); } ap_pclosedir(neg->pool, dirp); /* If all variants we considered turn out to be forbidden, then return FORBIDDEN */ if (forbidden.any && forbidden.all) return HTTP_FORBIDDEN; set_vlist_validator(r, r); /* Sort the variants into a canonical order. The negotiation * result sometimes depends on the order of the variants. By * sorting the variants into a canonical order, rather than using * the order in which readdir() happens to return them, we ensure * that the negotiation result will be consistent over filesystem * backup/restores and over all mirror sites. */ qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts, sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf); return OK;}/***************************************************************** * And now for the code you've been waiting for... actually * finding a match to the client's requirements. *//* Matching MIME types ... the star/star and foo/star commenting conventions * are implemented here. (You know what I mean by star/star, but just * try mentioning those three characters in a C comment). Using strcmp() * is legit, because everything has already been smashed to lowercase. * * Note also that if we get an exact match on the media type, we update * level_matched for use in level_cmp below... * * We also give a value for mime_stars, which is used later. It should * be 1 for star/star, 2 for type/star and 3 for type/subtype. */static int mime_match(accept_rec *accept_r, var_rec *avail){ char *accept_type = accept_r->name; char *avail_type = avail->mime_type; int len = strlen(accept_type); if (accept_type[0] == '*') { /* Anything matches star/star */ if (avail->mime_stars < 1) { avail->mime_stars = 1; } return 1; } else if ((accept_type[len - 1] == '*') && !strncmp(accept_type, avail_type, len - 2)) { if (avail->mime_stars < 2) { avail->mime_stars = 2; } return 1; } else if (!strcmp(accept_type, avail_type) || (!strcmp(accept_type, "text/html") && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE) || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) { if (accept_r->level >= avail->level) { avail->level_matched = avail->level; avail->mime_stars = 3; return 1; } } return OK;}/* This code implements a piece of the tie-breaking algorithm between * variants of equal quality. This piece is the treatment of variants * of the same base media type, but different levels. What we want to * return is the variant at the highest level that the client explicitly * claimed to accept. * * If all the variants available are at a higher level than that, or if * the client didn't say anything specific about this media type at all * and these variants just got in on a wildcard, we prefer the lowest * level, on grounds that that's the one that the client is least likely * to choke on. * * (This is all motivated by treatment of levels in HTML --- we only * want to give level 3 to browsers that explicitly ask for it; browsers * that don't, including HTTP/0.9 browsers that only get the implicit * "Accept: * / *" [space added to avoid confusing cpp --- no, that * syntax doesn't really work] should get HTML2 if available). * * (Note that this code only comes into play when we are choosing among * variants of equal quality, where the draft standard gives us a fair * bit of leeway about what to do. It ain't specified by the standard; * rather, it is a choice made by this server about what to do in cases * where the standard does not specify a unique course of action). */static int level_cmp(var_rec *var1, var_rec *var2){ /* Levels are only comparable between matching media types */ if (var1->is_pseudo_html && !var2->is_pseudo_html) { return 0; } if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) { return 0; } /* The result of the above if statements is that, if we get to * here, both variants have the same mime_type or both are * pseudo-html. */ /* Take highest level that matched, if either did match. */ if (var1->level_matched > var2->level_matched) { return 1; } if (var1->level_matched < var2->level_matched) { return -1; } /* Neither matched. Take lowest level, if there's a difference. */ if (var1->level < var2->level) { return 1; } if (var1->level > var2->level) { return -1; } /* Tied */ return 0;}/* Finding languages. The main entry point is set_language_quality() * which is called for each variant. It sets two elements in the * variant record: * language_quality - the 'q' value of the 'best' matching language * from Accept-Language: header (HTTP/1.1) * lang_index - Pre HTTP/1.1 language priority, using * position of language on the Accept-Language: * header, if present, else LanguagePriority * directive order. * * When we do the variant checking for best variant, we use language * quality first, and if a tie, language_index next (this only applies * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0 * algorithm, lang_index is never used. * * set_language_quality() calls find_lang_index() and find_default_index() * to set lang_index. */static int find_lang_index(array_header *accept_langs, char *lang){ accept_rec *accs; int i; if (!lang || !accept_langs) { return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -