<-
Apache HTTP 服务器 2.4 > Apache API注释

Apache 1.3 API注释

警告

尚未更新本文档,以考虑到Apache HTTP Server 2.0版中所做的更改。一些信息可能仍然有用,但请谨慎使用。

这些是有关Apache API和您必须处理的数据结构的一些说明它们尚未接近完成,但是希望它们能帮助您理解。请记住,随着我们积累经验,API仍可能会发生变化。(有关可能发生的情况,请参见TODO文件)。但是,很容易使模块适应所做的任何更改。(我们比您有更多的模块可以适应)。

这里有一些关于一般教学风格的注意事项。为了简洁起见,此处的所有结构声明都不完整-实际的声明还有更多我没有告诉您的位置。在大多数情况下,这些保留给服务器核心的一个组件或另一个组件,应谨慎使用模块进行更改。但是,在某些情况下,它们确实是我尚未了解的事情。欢迎来到前沿。

最后,这是一个概述,以使您对即将发生的事情和顺序有一些粗略的了解:

支持Apache!

也可以看看

最佳

基本概念

我们首先概述API背后的基本概念,以及它们如何在代码中体现。

处理程序,模块和请求

Apache将请求处理分为一系列步骤,大致与Netscape服务器API的方式相同(尽管此API比NetSite的步骤多一些,因为我认为将来可能有用的东西的钩子)。这些是:

通过查看一系列模块中的每个 模块,查看每个模块是否具有该阶段的处理程序,然后尝试调用它们,来处理这些阶段。处理程序通常可以执行以下三种操作之一:

大多数阶段都由处理它们的第一个模块终止。但是,对于日志记录,“修复程序”和非访问身份验证检查,所有处理程序始终运行(除非出现错误)。同样,响应阶段是唯一的,因为模块可以通过键入被请求对象的MIME类型的分派表为其声明多个处理程序。模块可以通过给它一个密钥(通配符MIME类型规范)来声明一个可以处理任何请求的响应阶段处理程序。但是,仅当服务器已尝试并且未能为请求的对象的MIME类型找到更具体的响应处理程序(不存在或全部拒绝)时,才调用通配符处理程序。*/*

处理程序本身是一个自变量(request_rec结构,见下文)的函数,该自变量 返回整数,如上所述。

模块简介

在这一点上,我们需要解释一个模块的结构。我们的候选人将是CGI模块之一,这将是更混乱的应用程序之一-它既处理CGI脚本又处理ScriptAliasconfig file命令。实际上,它比大多数模块都要复杂得多,但是如果我们只想举一个例子,那么它可能就是一个举手之劳的例子。

让我们从处理程序开始。为了处理CGI脚本,模块为它们声明了一个响应处理程序。由于ScriptAlias,它还具有名称翻译阶段(识别ScriptAliased URI),类型检查阶段(任何 ScriptAliased请求都键入为CGI脚本)的处理程序。

该模块需要维护一些(虚拟)服务器信息,即有效的ScriptAliases。因此,模块结构包含指向构建这些结构的功能的指针,以及包含将它们两者结合的另一个功能的指针(如果主服务器和虚拟服务器都ScriptAlias声明了es)。

最后,该模块包含用于处理ScriptAlias命令本身的代码。这个特定的模块仅声明一个命令,但可能会声明更多命令,因此模块具有命令表,这些 命令表声明了它们的命令,并描述了允许它们的位置以及如何调用它们。

关于这些命令中某些参数的声明类型的最后说明:a pool是指向资源池 结构的指针;服务器使用它们来跟踪已分配的内存,打开的文件,以服务特定请求或处理配置自身的过程。这样一来,当请求结束(或者,对于配置池,当服务器重新启动),内存可以被释放,并且文件关闭, 集体,而无需编写明确的代码,任何人都可以跟踪所有这些下来,处理它们。也cmd_parms 结构包含有关正在读取的配置文件的各种信息,以及其他状态信息,这些信息有时会用于处理config-file命令的功能(例如ScriptAlias)。事不宜迟,该模块本身:

/* Declarations of handlers. */

int translate_scriptalias (request_rec *);
int type_scriptalias (request_rec *);
int cgi_handler (request_rec *);

/* Subsidiary dispatch table for response-phase
 * handlers, by MIME type */

handler_rec cgi_handlers[] = {
{ "application/x-httpd-cgi", cgi_handler },
{ NULL }
};

/* Declarations of routines to manipulate the
 * module's configuration info. Note that these are
 * returned, and passed in, as void *'s; the server
 * core keeps track of them, but it doesn't, and can't,
 * know their internal structure.
 */

void *make_cgi_server_config (pool *);
void *merge_cgi_server_config (pool *, void *, void *);

/* Declarations of routines to handle config-file commands */

extern char *script_alias(cmd_parms *, void *per_dir_config, char *fake, char *real);

command_rec cgi_cmds[] = {
{ "ScriptAlias", script_alias, NULL, RSRC_CONF, TAKE2,
"a fakename and a realname"},
{ NULL }
};

module cgi_module = {

  STANDARD_MODULE_STUFF,
  NULL,                     /* initializer */
  NULL,                     /* dir config creator */
  NULL,                     /* dir merger */
  make_cgi_server_config,   /* server config */
  merge_cgi_server_config,  /* merge server config */
  cgi_cmds,                 /* command table */
  cgi_handlers,             /* handlers */
  translate_scriptalias,    /* filename translation */
  NULL,                     /* check_user_id */
  NULL,                     /* check auth */
  NULL,                     /* check access */
  type_scriptalias,         /* type_checker */
  NULL,                     /* fixups */
  NULL,                     /* logger */
  NULL                      /* header parser */
};
最佳

处理程序的工作方式

处理程序的唯一参数是request_rec结构。该结构描述了代表客户对服务器的特定请求。在大多数情况下,与客户端的每个连接仅生成一个request_rec结构。

request_rec的简要介绍

request_rec包含指向资源池的指针,该指针将在服务器完成对请求的处理后清除;到包含每个服务器和每个连接信息,最重要的是有关请求本身的信息的结构。

最重要的此类信息是一小串字符串,描述了所请求对象的属性,包括其URI,文件名,内容类型和内容编码(这些内容由处理请求的翻译和类型检查处理程序填写) , 分别)。

其他常用的数据项包括在客户端原始请求上提供MIME标头的表,将与响应一起发送回的MIME标头(模块可以随意添加)以及在运行过程中产生的任何子流程的环境变量。服务请求。这些表使用ap_table_getap_table_set例程进行操作。

请注意, 模块内容处理程序无法使用 例程设置Content-type标头值。而是通过 将结构中的字段指向适当的字符串来设置的。例如ap_table_*()content_typerequest_rec

r->content_type = "text/html";

最后,有两个数据结构的指针,它们依次指向每个模块的配置结构。具体来说,这些保留指向模块所构建的数据结构的指针,以描述其在服务于请求过程中所构建的私有数据(通过.htaccess文件或<Directory>节)来配置其在给定目录中的运行方式(通过 文件或节)。一个阶段的模块处理程序可以将“注释”传递给其他阶段的处理程序。所server_rec指向的数据结构中还有另一个这样的配置向量request_rec,其中包含每个(虚拟)服务器配置数据。

这是一个简短的声明,提供最常用的字段:

struct request_rec {

pool *pool;
conn_rec *connection;
server_rec *server;

/* What object is being requested */

char *uri;
char *filename;
char *path_info;

char *args;           /* QUERY_ARGS, if any */
struct stat finfo;    /* Set by server core;
                       * st_mode set to zero if no such file */

char *content_type;
char *content_encoding;

/* MIME header environments, in and out. Also,
 * an array containing environment variables to
 * be passed to subprocesses, so people can write
 * modules to add to that environment.
 *
 * The difference between headers_out and
 * err_headers_out is that the latter are printed
 * even on error, and persist across internal
 * redirects (so the headers printed for
 * ErrorDocument handlers will have them).
 */

table *headers_in;
table *headers_out;
table *err_headers_out;
table *subprocess_env;

/* Info about the request itself... */

int header_only;     /* HEAD request, as opposed to GET */
char *protocol;      /* Protocol, as given to us, or HTTP/0.9 */
char *method;        /* GET, HEAD, POST, etc. */
int method_number;   /* M_GET, M_POST, etc. */

/* Info for logging */

char *the_request;
int bytes_sent;

/* A flag which modules can set, to indicate that
 * the data being returned is volatile, and clients
 * should be told not to cache it.
 */

int no_cache;

/* Various other config info which may change
 * with .htaccess files
 * These are config vectors, with one void*
 * pointer for each module (the thing pointed
 * to being the module's business).
 */

void *per_dir_config;   /* Options set in config files, etc. */
void *request_config;   /* Notes on *this* request */

};

request_rec结构来自哪里

request_rec通过从客户端读取HTTP请求并填写字段来构建大多数结构。但是,有一些例外情况:

处理请求,拒绝并返回错误代码

如上所述,每个处理程序在被调用以处理特定的时 request_rec,必须返回int来指示发生了什么。那可以是

请注意,如果返回的错误代码为REDIRECT,则模块应Location在请求的位置 放入headers_out,以指示客户端应重定向到的位置

响应处理程序的特殊注意事项

大多数阶段的处理程序通过简单地在request_rec结构中设置几个字段来完成其工作(或者,对于访问检查器,只需返回正确的错误代码即可)。但是,响应处理程序必须实际将请求发送回客户端。

他们应该首先使用函数发送HTTP响应标头ap_send_http_header。(您不必做任何特殊的事情就可以跳过发送HTTP / 0.9请求的标头;该函数自行指出它不应该做任何事情)。如果请求被标记header_only,那就是他们应该做的;他们应该在此之后返回,而不尝试任何进一步的输出。

否则,他们应产生一个请求主体,以适当地响应客户。用于此的原语是ap_rputcap_rprintf,用于内部生成的输出,以及 ap_send_fd,用于将某些内容FILE * 直接复制到客户端。

在这一点上,您应该或多或少地理解以下代码,这是处理GET没有更多特定处理程序的请求的处理程序。它还显示了条件 GETs的处理方式(如果需要在特定的响应处理程序中进行处理)- ap_set_last_modified检查If-modified-since客户端提供的值(如果有),并返回适当的代码(如果非零,则为USE_LOCAL_COPY) 。没有类似的考虑适用于 ap_set_content_length,但是它返回对称错误代码。

int default_handler (request_rec *r)
{
int errstatus;
FILE *f;

if (r->method_number != M_GET) return DECLINED;
if (r->finfo.st_mode == 0) return NOT_FOUND;

if ((errstatus = ap_set_content_length (r, r->finfo.st_size))
    || (errstatus = ap_set_last_modified (r, r->finfo.st_mtime)))
return errstatus;

f = fopen (r->filename, "r");

if (f == NULL) {
log_reason("file permissions deny server access", r->filename, r);
return FORBIDDEN;
}

register_timeout ("send", r);
ap_send_http_header (r);

if (!r->header_only) send_fd (f, r);
ap_pfclose (r->pool, f);
return OK;
}

最后,如果所有这些挑战太大了,那么有几种方法可以解决。首先,如上所示,尚未生成任何输出的响应处理程序可以简单地返回错误代码,在这种情况下,服务器将自动生成错误响应。其次,它可以通过调用扩展到其他处理程序ap_internal_redirect,这是调用 上述内部重定向机制的方式。内部重定向的响应处理程序应始终返回OK

ap_internal_redirect不是响应处理程序的处理程序中调用将导致严重的混乱)。

身份验证处理程序的特殊注意事项

应该在这里详细讨论的东西:

日志处理程序的特殊注意事项

当一个请求在内部被重定向后,就会出现要记录什么的问题。Apache通过将整个重定向链绑定到request_rec通过r->prevr->next指针进行线程处理的结构列表中来处理此问题。将request_rec被传递到在这种情况下,记录处理程序是最初建为来自客户端的初始请求的一个; 请注意,该bytes_sent字段仅在链中的最后一个请求(实际发送了响应的请求)中才是正确的。

最佳

资源分配和资源池

编写和设计服务器池服务器的问题之一是防止泄漏,即分配资源(内存,打开的文件),而不随后释放它们。通过设计资源池机制,可以很容易地防止这种情况的发生,方法是允许分配资源,以便在服务器处理完资源后自动释放资源 。

其工作方式如下:为处理特定请求而分配的内存,文件打开,与为该请求分配的资源池相关联 。池是一个数据结构,它本身跟踪有问题的资源。

处理完请求后,将清除池。届时,将释放与之关联的所有内存以供重用,并关闭与之关联的所有文件,并运行与该池关联的任何其他清除功能。当结束时,我们可以确信与该池相关联的所有资源都已释放,并且没有一个泄漏。

服务器重新启动,以及按服务器配置的内存和资源分配以类似的方式处理。有一个配置池,该跟踪在读取服务器配置文件并处理其中的命令时分配的资源(例如,为每个服务器模块配置分配的内存,日志文件和其他已打开的文件) ,依此类推)。当服务器重新启动并必须重新读取配置文件时,将清除配置池,因此上次读取它们所占用的内存和文件描述符可供重新使用。

应该注意的是,除了诸如日志记录处理程序之类的情况外,通常不强制使用池机制,在这种情况下,您确实需要注册清除程序以确保在服务器重启时关闭日志文件(这很容易做到)。使用函数ap_pfopen,该函数还安排在任何子进程(例如CGI脚本)被exec编译之前关闭基础文件描述符,或者在您使用超时机制的情况下(此处尚未进行说明)。但是,使用它有两个好处:分配给池的资源永远不会泄漏(即使您分配了临时字符串,也只是忘记了它);同样,对于内存分配,ap_palloc通常比快 malloc

我们从描述内存如何分配到池开始,然后讨论资源池机制如何跟踪其他资源。

池中的内存分配

通过调用函数将内存分配给池,该函数 ap_palloc带有两个参数,一个是指向资源池结构的指针,另一个是要分配的内存量(以chars为单位)。在用于处理请求的处理程序中,获取资源池结构的最常见方法是查看pool相关的插槽request_rec。因此,以下成语在模块代码中反复出现:

int my_handler(request_rec *r)
{
struct my_structure *foo;
...

foo = (foo *)ap_palloc (r->pool, sizeof(my_structure));
}

需要注意的是,没有ap_pfree - ap_palloc只有当相关联的资源池清空版内存被释放。这意味着ap_palloc不必做太多的会计malloc(); 在典型情况下,它所做的只是舍入大小,增加指针大小和进行范围检查。

(这也增加了ap_palloc过度使用可能导致服务器进程变得过大的可能性 。有两种处理方法,下面将介绍;简要地说,您可以使用malloc,并尝试确保所有显式地获得内存freed,或者您可以分配主池的子池,在子池中分配内存,并定期清除它,后一种技术在下面的子池部分中讨论,并使用在目录索引代码中,以避免在列出包含数千个文件的目录时避免过多的存储分配。

分配初始化的内存

有一些函数分配初始化的内存,并且经常有用。该函数ap_pcalloc具有与相同的接口ap_palloc,但是会在返回之前清除它分配的内存。该函数ap_pstrdup 将资源池和a char *作为参数,并为指针所指向的字符串副本分配内存,并返回指向副本的指针。最后ap_pstrcat是一个varargs样式的函数,该函数使用一个指向资源池的指针和至少两个 char *参数,最后一个参数必须为 NULL。它以一个单位分配足够的内存来容纳每个字符串的副本。例如:

ap_pstrcat (r->pool, "foo", "/", "bar", NULL);

返回一个指向8字节内存的指针,该指针已初始化为 "foo/bar"

Apache Web服务器中的常用池

实际上,池的生命周期定义比其他任何事情都重要。http_main中有一些静态池,它们在适当的时候作为参数传递给各种非http_main函数。他们来了:

permanent_pool
从未传给其他任何人,这是所有泳池的始祖
pconf
  • 永久池的子池
  • 在配置“周期”开始时创建;在服务器终止或重新启动之前一直存在;通过cmd-> pool传递给所有配置时间例程,或者作为不占用池的那些传递给“ pool * p”参数
  • 传递给模块init()函数
ptemp
  • 对不起,我撒谎了,在1.3中当前未将此池称为此池,我在pthreads开发中将其重命名。我指的是在父级中使用ptrans ...与之相比,在子级中使用ptrans的更高定义。
  • 永久池的子池
  • 在配置“周期”开始时创建;存在直到配置解析结束;通过 cmd-> temp_pool 传递给配置时例程。有点像“野孩子”,因为它并非随处可见。用于某些配置例程可能需要的临时暂存空间,但在配置末尾将其删除。
pchild
  • 永久池的子池
  • 在产生一个孩子(或创建一个线程)时创建;一直活到那个孩子(线程)被破坏为止
  • 传递给模块child_init函数
  • 在调用child_exit函数之后立即发生销毁...(这可能解释了为什么我认为child_exit是多余且不需要的)
ptrans
  • 应该是pchild的子池,但当前是permanent_pool的子池,请参见上文
  • 在进入accept()循环以接收连接之前由孩子清除
  • 用作连接->池
r->pool
  • 对于主要请求,这是connection-> pool的子池;对于子请求,它是父请求池的子池。
  • 一直存在直到请求结束( ap_destroy_sub_req,或process_request完成后在child_main中)
  • 注意,r本身是从r-> pool分配的; ,首先创建r-> pool,然后首先从中创建r palloc()d

人们几乎所做的所有事情r->pool都是使用游泳池。但是您可以看到其他生存期(例如pchild)对于某些模块有什么用处……例如需要每个孩子一次打开数据库连接,并希望在孩子死后进行清理的模块。

您还可以看到一些错误是如何自我显现的,例如connection->user从中设置一个值 r->pool-在这种情况下,连接存在的生存期为ptrans,该生存期长于 r->pool(尤其是如果r->pool是子请求!)。因此,正确的做法是从分配 connection->pool

mod_include /中还有一个有趣的错误mod_cgi。您会在其中看到他们进行了此测试,以确定是否应使用r->poolr->main->pool。在这种情况下,他们正在注册用于清除的资源是一个子进程。如果已在中注册 r->pool,则该代码将wait()在子请求完成时针对孩子。有了mod_include这个可能是任何旧的#include,并且延迟可能长达3秒...并且经常发生。而是注册了子进程,该子进程r->main->pool导致在整个请求完成时(,在将输出发送到客户端并进行日志记录之后)对其进行清理。

跟踪打开的文件等

如上所述,资源池还用于跟踪内存以外的其他种类的资源。最常见的是打开的文件。通常用于此的例程是ap_pfopen,它使用一个资源池和两个字符串作为参数。字符串与的典型参数相同fopen例如

...
FILE *f = ap_pfopen (r->pool, r->filename, "r");

if (f == NULL) { ... } else { ... }

还有一个ap_popenf例程,与低级open系统调用并行。这两个例程都安排在清除相关资源池时关闭文件。

与内存的情况下,功能与分配关闭文件ap_pfopen,并且ap_popenf,即 ap_pfcloseap_pclosef。(这是因为在许多系统上,单个进程可以打开的文件数量非常有限)。使用这些功能来关闭分配给ap_pfopen和的文件很重要ap_popenf,因为否则可能会导致致命错误,例如Linux,如果FILE*多次关闭该文件,则会导致严重错误。

(使用该close功能不是强制性的,因为无论如何该文件最终都会关闭,但是在模块正在打开或可能打开很多文件的情况下,您应该考虑使用它)。

其他种类的资源-清理功能

更多文字在这里。描述实现文件内容的清理原语;另外,spawn_process

池清除将一直进行到clear_pool()被调用之前: clear_pool(a)递归调用destroy_pool() ;的所有子池a。然后调用所有清理 a; 然后释放的所有内存adestroy_pool(a)调用clear_pool(a),然后释放池结构本身。clear_pool(a)不删除a,它只是释放所有资源,您可以立即重新使用它。

精细控制-创建和处理子池,并带有关于子请求的注释

在极少数情况下,过于随意地使用ap_palloc()和关联的原语可能会导致不希望的挥霍资源分配。您可以通过以下方法处理这种情况:创建一个子池,在子池而不是主池中分配,然后清除或销毁该子池,从而释放与之关联的资源。(这确实一种罕见的情况;在标准模块集中出现这种情况的唯一情况是列出目​​录,然后仅在非常大的目录中列出。不必要地使用此处讨论的原语可能会使您的代码更加混乱。位,几乎没有收益)。

创建子池的原语是ap_make_sub_pool,它使用另一个池(父池)作为参数。清除主池后,子池将被销毁。通过分别调用函数ap_clear_poolap_destroy_pool,也可以随时清除或销毁子池 。(不同之处在于,ap_clear_pool释放与池相关联的资源,同时ap_destroy_pool还释放池本身。在前一种情况下,您可以在池中分配新资源,然后再次清除它,依此类推;在后一种情况下,这很简单消失了)。

最后一点-子请求有自己的资源池,这些资源池是主请求的资源池的子池。回收与已分配(使用ap_sub_req_...功能)的子请求关联的资源的有礼貌的方法是 ap_destroy_sub_req,这将释放资源池。在调用此函数之前,请确保将您关心的任何内容复制到子请求的资源池中可能会分配到不稳定程度较小的位置(例如,其request_rec结构中的文件名 )。

(同样,在大多数情况下,您不应该调用此函数;一个典型的子请求仅分配2K左右的内存,并且在清除主请求池后仍将释放该内存。仅当您为单个主请求分配了许多子请求,因此您应该认真考虑这些 ap_destroy_...功能)。

最佳

配置,命令等

该服务器的设计目标之一是保持与NCSA 1.3服务器的外部兼容性,即读取相同的配置文件,正确处理其中的所有指令,并且通常是对它们的直接替代。 NCSA。另一方面,另一个设计目标是将尽可能多的服务器功能转移到与单片服务器核心尽可能少的模块中。协调这些目标的唯一方法是将大多数命令的处理从中央服务器移到模块中。

但是,仅提供模块命令表不足以将它们与服务器核心完全分开。服务器必须记住命令以便以后对它们执行操作。这涉及维护模块专用的数据,这些数据可以是每个服务器或每个目录。大多数事情都是按目录进行的,尤其包括访问控制和授权信息,还包括有关如何根据后缀确定文件类型的信息,可以通过AddTypeForceType指令对其进行修改 ,等等。通常,管理原则是:可以通过目录配置的任何事物应该是可配置的。每个服务器的信息通常在标准模块集中使用,例如 Aliases和Redirect在请求绑定到基础文件系统中的特定位置之前起作用的。

模拟NCSA服务器的另一个要求是能够处理每个目录的配置文件,通常称为 .htaccess文件,尽管即使在NCSA服务器中,它们也可以包含与访问控制完全无关的指令。因此,在URI->文件名转换之后,但在执行任何其他阶段之前,服务器将沿着转换的路径名遍历基础文件系统的目录层次结构,以读取.htaccess可能存在的任何 文件。然后,读入的信息必须与服务器自身的配置文件中的适用信息合并(可以从中的<Directory>章节中 access.conf,也可以从默认的中合并)中合并srm.conf,实际上在大多数情况下的行为几乎都与相似<Directory />)。

最后,在处理了涉及读取.htaccess文件的请求后 ,我们需要丢弃为处理文件而分配的存储空间。通过将那些结构绑定到每个事务资源池,可以用与解决其他任何类似问题的方法相同的方式解决该问题。

每目录配置结构

让我们看一下所有这些如何在中发挥作用mod_mime.c,它定义了文件类型处理程序,该处理程序模仿NCSA服务器从后缀确定文件类型的行为。我们将在这里看到实现AddTypeAddEncoding命令的代码。这些命令可以显示在 .htaccess文件中,因此必须在模块的按目录的私有数据中进行处理,实际上,该数据由两个单独的MIME类型和编码信息表组成,声明如下:

typedef struct {
    table *forced_types;      /* Additional AddTyped stuff */
    table *encoding_types;    /* Added with AddEncoding... */
} mime_dir_config;

当服务器读取<Directory>包含MIME模块的命令之一的配置文件或部分时,它需要创建一个 mime_dir_config结构,因此这些命令要起作用。每目录配置槽”它通过调用它发现在模块的功能做到这一点`创建,有两个参数:到这个配置信息适用(或目录名 NULLsrm.conf),并指向一个资源池,其中分配应该发生。

(如果我们正在读取.htaccess文件,则该资源池是请求的每个请求资源池;否则,它是用于配置数据并在重新启动时清除的资源池。无论哪种方式,对于结构通过清除池中的清理(如果需要,在池中注册清除)来创建以消失的方式创建)。

对于MIME模块,每个目录的配置创建函数只是 ap_palloc上面的结构,并且会创建几个表来填充它。看起来像这样:

void *create_mime_dir_config (pool *p, char *dummy)
{
mime_dir_config *new =
(mime_dir_config *) ap_palloc (p, sizeof(mime_dir_config));

new->forced_types = ap_make_table (p, 4);
new->encoding_types = ap_make_table (p, 4);

return new;
}

现在,假设我们已经读了一个.htaccess文件。我们已经有了层次结构中下一个目录的按目录的配置结构。如果.htaccess我们只是在阅读文件没有任何AddTypeAddEncoding命令,其对MIME模块每目录的配置结构仍然是有效的,我们可以只使用它。否则,我们需要以某种方式合并这两个结构。

为此,服务器调用模块的每目录配置合并功能(如果存在)。该函数带有三个参数:两个要合并的结构,以及在其中分配结果的资源池。对于MIME模块,所有需要做的就是将新的按目录配置结构中的表与父表中的表重叠:

void *merge_mime_dir_configs (pool *p, void *parent_dirv, void *subdirv)
{
mime_dir_config *parent_dir = (mime_dir_config *)parent_dirv;
mime_dir_config *subdir = (mime_dir_config *)subdirv;
mime_dir_config *new =
(mime_dir_config *)ap_palloc (p, sizeof(mime_dir_config));

new->forced_types = ap_overlay_tables (p, subdir->forced_types,
parent_dir->forced_types);
new->encoding_types = ap_overlay_tables (p, subdir->encoding_types,
parent_dir->encoding_types);

return new;
}

注意-如果不存在按目录的合并功能,则服务器将仅使用子目录的配置信息,而忽略父目录的配置信息。对于某些模块,它工作得很好(例如,对于includes模块,其按目录配置信息仅由的状态组成XBITHACK),对于那些模块,您不能声明一个,而在其中保留相应的结构插槽模块本身NULL

命令处理

现在我们有了这些结构,我们需要能够弄清楚如何填充它们。这涉及处理实际AddTypeAddEncoding命令。要查找命令,服务器将在模块的命令表中查找。该表包含有关命令使用多少个参数,以什么格式,允许在哪里等等的信息。该信息足以使服务器使用预解析的参数调用大多数命令处理功能。事不宜迟,让我们看一下AddType 命令处理程序,它看起来像这样(AddEncoding命令看起来基本相同,在这里不再显示):

char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext)
{
if (*ext == '.') ++ext;
ap_table_set (m->forced_types, ext, ct);
return NULL;
}

这个命令处理程序非常简单。如您所见,它包含四个参数,其中两个是预先解析的参数,第三个是所讨论模块的按目录配置的结构,第四个是指向该cmd_parms结构的指针。该结构包含一堆参数,这些参数经常用于某些(但不是全部)命令,包括资源池(可以从中分配内存以及应绑定清理的资源池)以及正在配置的(虚拟)服务器,如果需要,可以从中获取模块的每服务器配置数据。

该特定命令处理程序异常简单的另一种方式是,它不会遇到任何错误情况。如果存在,它将返回错误消息,而不是NULL; stderr如果在主配置文件中,则会导致在服务器上打印出错误 ,然后快速退出;对于.htaccess文件,语法错误会记录在服务器错误日志中(并附带错误来源),并且该请求会与服务器错误响应一起退回(HTTP错误状态,代码500)。

MIME模块的命令表包含这些命令的条目,如下所示:

command_rec mime_cmds[] = {
{ "AddType", add_type, NULL, OR_FILEINFO, TAKE2,
"a mime type followed by a file extension" },
{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, TAKE2,
"an encoding (e.g., gzip), followed by a file extension" },
{ NULL }
};

这些表中的条目是:

最后,设置好所有这些之后,我们必须使用它。最终,这是在模块的处理程序中完成的,特别是对于其文件类型处理程序而言,看起来或多或少都像这样;请注意,request_rec使用ap_get_module_config函数从的每个目录配置向量中 提取了每个目录的配置结构。

int find_ct(request_rec *r)
{
int i;
char *fn = ap_pstrdup (r->pool, r->filename);
mime_dir_config *conf = (mime_dir_config *)
ap_get_module_config(r->per_dir_config, &mime_module);
char *type;

if (S_ISDIR(r->finfo.st_mode)) {
r->content_type = DIR_MAGIC_TYPE;
return OK;
}

if((i=ap_rind(fn,'.')) < 0) return DECLINED;
++i;

if ((type = ap_table_get (conf->encoding_types, &fn[i])))
{
r->content_encoding = type;

/* go back to previous extension to try to use it as a type */
fn[i-1] = '\0';
if((i=ap_rind(fn,'.')) < 0) return OK;
++i;
}

if ((type = ap_table_get (conf->forced_types, &fn[i])))
{
r->content_type = type;
}

return OK;
}

旁注-每个服务器的配置,虚拟服务器

每服务器模块配置的基本思想与按目录配置的思想基本相同。有一个创建函数和一个合并函数,后者在虚拟服务器已部分覆盖基本服务器配置的地方被调用,并且必须计算一个组合结构。(与按目录配置一样,如果未指定合并功能且在某些虚拟服务器中配置了模块,则默认情况是基本配置将被忽略)。

唯一的实质区别是,当命令需要配置每个服务器专用模块数据时,它需要转到该cmd_parms数据以获取该 数据。这是一个来自alias模块的示例,该示例还指示了如何返回语法错误(请注意,命令处理程序的按目录配置参数声明为虚拟,因为该模块实际上没有按目录配置数据):

char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url)
{
server_rec *s = cmd->server;
alias_server_conf *conf = (alias_server_conf *)
ap_get_module_config(s->module_config,&alias_module);
alias_entry *new = ap_push_array (conf->redirects);

if (!ap_is_url (url)) return "Redirect to non-URL";

new->fake = f; new->real = url;
return NULL;
}

可用语言: zh 

最佳

注释

注意:
这不是“问答”部分。此处放置的评论应指向有关改进文档或服务器的建议,如果实施或被认为无效/偏离主题,我们的主持人可以将其删除。有关如何管理Apache HTTP Server的问题,应直接指向我们的IRC频道#httpd(位于Freenode上),或发送至我们的邮件列表
目前,此页面已禁用评论。