1 定义
ngx_http_alloc_request 函数 定义在 ./nginx-1.24.0/src/http/ngx_http_request.c
staticngx_http_request_t*ngx_http_alloc_request(ngx_connection_t*c){ngx_pool_t*pool;ngx_time_t*tp;ngx_http_request_t*r;ngx_http_connection_t*hc;ngx_http_core_srv_conf_t*cscf;ngx_http_core_main_conf_t*cmcf;hc=c->data;cscf=ngx_http_get_module_srv_conf(hc->conf_ctx,ngx_http_core_module);pool=ngx_create_pool(cscf->request_pool_size,c->log);if(pool==NULL){returnNULL;}r=ngx_pcalloc(pool,sizeof(ngx_http_request_t));if(r==NULL){ngx_destroy_pool(pool);returnNULL;}r->pool=pool;r->http_connection=hc;r->signature=NGX_HTTP_MODULE;r->connection=c;r->main_conf=hc->conf_ctx->main_conf;r->srv_conf=hc->conf_ctx->srv_conf;r->loc_conf=hc->conf_ctx->loc_conf;r->read_event_handler=ngx_http_block_reading;r->header_in=hc->busy?hc->busy->buf:c->buffer;if(ngx_list_init(&r->headers_out.headers,r->pool,20,sizeof(ngx_table_elt_t))!=NGX_OK){ngx_destroy_pool(r->pool);returnNULL;}if(ngx_list_init(&r->headers_out.trailers,r->pool,4,sizeof(ngx_table_elt_t))!=NGX_OK){ngx_destroy_pool(r->pool);returnNULL;}r->ctx=ngx_pcalloc(r->pool,sizeof(void*)*ngx_http_max_module);if(r->ctx==NULL){ngx_destroy_pool(r->pool);returnNULL;}cmcf=ngx_http_get_module_main_conf(r,ngx_http_core_module);r->variables=ngx_pcalloc(r->pool,cmcf->variables.nelts*sizeof(ngx_http_variable_value_t));if(r->variables==NULL){ngx_destroy_pool(r->pool);returnNULL;}#if(NGX_HTTP_SSL)if(c->ssl&&!c->ssl->sendfile){r->main_filter_need_in_memory=1;}#endifr->main=r;r->count=1;tp=ngx_timeofday();r->start_sec=tp->sec;r->start_msec=tp->msec;r->method=NGX_HTTP_UNKNOWN;r->http_version=NGX_HTTP_VERSION_10;r->headers_in.content_length_n=-1;r->headers_in.keep_alive_n=-1;r->headers_out.content_length_n=-1;r->headers_out.last_modified_time=-1;r->uri_changes=NGX_HTTP_MAX_URI_CHANGES+1;r->subrequests=NGX_HTTP_MAX_SUBREQUESTS+1;r->http_state=NGX_HTTP_READING_REQUEST_STATE;r->log_handler=ngx_http_log_error_handler;returnr;}
ngx_http_alloc_request 函数的作用是: 为给定的 HTTP 连接分配并初始化一个 `ngx_http_request_t` 结构体, 为后续请求处理做好准备。 若过程中任何步骤失败, 则释放已分配资源并返回 `NULL`。
2 详解
1 函数签名
staticngx_http_request_t*ngx_http_alloc_request(ngx_connection_t*c)
返回类型 ngx_http_request_t * 返回值:指向新分配并完成初始化的 ngx_http_request_t 结构体的指针。 失败处理: 当内存分配失败或初始化过程中出现任何错误时, 函数会清理已分配资源并返回 NULL。 调用者必须检查该返回值。 结构体意义: ngx_http_request_t 是 nginx HTTP 处理流程的核心数据结构,贯穿整个请求生命周期。 调用者获得返回的请求结构体后, 拥有了通过 r->pool 管理的整个请求内存池的所有权, 同时也承担了最终释放(通过引用计数递减)的责任。
参数 ngx_connection_t *c 指向一个已建立的网络连接对象的指针
2 逻辑流程
1 局部变量 2 创建 ngx_http_request_t 3 设置各个字段 4 返回结构体指针
1 局部变量
{ngx_pool_t*pool;ngx_time_t*tp;ngx_http_request_t*r;ngx_http_connection_t*hc;ngx_http_core_srv_conf_t*cscf;ngx_http_core_main_conf_t*cmcf;
2 创建 ngx_http_request_t
hc=c->data;
从通用连接对象中取出 HTTP 数据
cscf=ngx_http_get_module_srv_conf(hc->conf_ctx,ngx_http_core_module);
从 HTTP 连接的配置上下文 hc->conf_ctx 中获取核心模块的 server 级别配置
pool=ngx_create_pool(cscf->request_pool_size,c->log);if(pool==NULL){returnNULL;}
创建内存池
r=ngx_pcalloc(pool,sizeof(ngx_http_request_t));if(r==NULL){ngx_destroy_pool(pool);returnNULL;}r->pool=pool;
#1 从刚创建的内存池中分配并清零 ngx_http_request_t 结构体 #2 将请求池指针存入请求结构体,建立双向关联
3 设置各个字段
r->http_connection=hc;r->signature=NGX_HTTP_MODULE;r->connection=c;r->main_conf=hc->conf_ctx->main_conf;r->srv_conf=hc->conf_ctx->srv_conf;r->loc_conf=hc->conf_ctx->loc_conf;
#1 关联 HTTP 连接上下文 #2 设置请求结构体的“签名”字段 NGX_HTTP_MODULE 是一个常量,用于标识该结构体属于 HTTP 模块 #3 将底层连接对象指针存入请求 #4 将配置上下文的三级配置数组指针直接复制到请求结构体中
r->read_event_handler=ngx_http_block_reading;
设置请求的读事件处理器为“阻塞读取”。 初始时,请求尚未准备好读取数据。 设置 ngx_http_block_reading 会临时从事件循环中移除该连接的读事件, 直到请求处理流程需要读取数据时再重新设置。 防止在未初始化完成时意外触发读事件(理论上不会发生,防御性编程), 保证事件驱动的正确性。
r->header_in=hc->busy?hc->busy->buf:c->buffer;
初始化请求头读取缓冲区指针。
若 “读事件触发时,请求 req1 和 请求 req2 的数据都已到达” 两个请求的数据一开始都在 c->buffer 中 解析 req1 时,解析器顺着 c->buffer 的指针逐个字节前进,直到越过 req1 的整个报文。 此时 c->buffer 中指向剩余数据的指针已经指向了 req2 的开始。 nginx 把剩下的数据(即 req2 的前部)从原缓冲区中分离,构造为 hc->busy 链。 于是 hc->busy->buf 现在持有 req2 的数据。 然后 nginx 在下一次读事件时继续读取 req2 的后半部分数据, 这些数据会继续追加保存到 hc->busy->buf 中
if(ngx_list_init(&r->headers_out.headers,r->pool,20,sizeof(ngx_table_elt_t))!=NGX_OK){ngx_destroy_pool(r->pool);returnNULL;}
初始化响应头链表 r->headers_out 代表 HTTP 响应的头部信息区域(状态行 + 头部 + 尾部) r->headers_out.headers 是响应头
if(ngx_list_init(&r->headers_out.trailers,r->pool,4,sizeof(ngx_table_elt_t))!=NGX_OK){ngx_destroy_pool(r->pool);returnNULL;}
初始化响应尾部(trailers)链表
r->ctx=ngx_pcalloc(r->pool,sizeof(void*)*ngx_http_max_module);if(r->ctx==NULL){ngx_destroy_pool(r->pool);returnNULL;}
分配模块上下文数组。 ngx_http_max_module 是编译进 nginx 的所有 HTTP 模块的数量(包括核心模块与第三方模块)。 为每个模块预留一个 void * 指针位置,初始全部为 NULL。 模块在使用时通过 ngx_http_get_module_ctx(r, module) 获取自己的上下文,如果为 NULL 则分配。 意义:提供了模块间独立存储请求私有数据的机制,是实现模块化架构的基础。
cmcf=ngx_http_get_module_main_conf(r,ngx_http_core_module);
获取核心模块的 main 级别配置
r->variables=ngx_pcalloc(r->pool,cmcf->variables.nelts*sizeof(ngx_http_variable_value_t));if(r->variables==NULL){ngx_destroy_pool(r->pool);returnNULL;}
分配请求变量值数组 cmcf->variables.nelts 是所有配置中定义的变量总数
#if(NGX_HTTP_SSL)if(c->ssl&&!c->ssl->sendfile){r->main_filter_need_in_memory=1;}#endif
SSL 的特殊处理
r->main=r;r->count=1;
设置请求的 main 指针指向自身。 表示这是一个主请求(而非子请求)。 子请求的 main 会指向创建它的主请求。 意义:在 nginx 的请求层级结构中,子请求通过 main 指针链回主请求,便于统一资源管理和引用计数。
初始化引用计数为 1。 逻辑:引用计数用于追踪请求的活跃引用数(如被输出链、异步操作等持有)。 当计数降为 0 时,请求会被最终释放(调用 ngx_http_free_request)。 意义:实现安全的请求生命周期管理,防止提前释放仍在使用的请求。
tp=ngx_timeofday();r->start_sec=tp->sec;r->start_msec=tp->msec;
记录请求开始的精确时间(秒和毫秒)。
r->method=NGX_HTTP_UNKNOWN;r->http_version=NGX_HTTP_VERSION_10;
#1 HTTP 方法初始化为 UNKNOWN。 在成功解析请求行之前,方法未确定。 该值会在解析阶段被更新为 NGX_HTTP_GET、NGX_HTTP_POST 等。 意义:明确区分“尚未解析”与实际的 HTTP 方法,避免误操作。
#2 默认 HTTP 版本设为 1.0。 若客户端请求未指定版本(如古老的非标准请求),则假定为 HTTP/1.0。 正常解析请求行后会更新为 NGX_HTTP_VERSION_09、NGX_HTTP_VERSION_11 等。 意义:提供合理的默认值,保证后续处理
r->headers_in.content_length_n=-1;r->headers_in.keep_alive_n=-1;r->headers_out.content_length_n=-1;r->headers_out.last_modified_time=-1;
初始化几个重要的头部数值字段为 -1。 content_length_n: 分别代表请求和响应正文的长度,-1 表示未设置(例如没有 Content-Length 头部)。 keep_alive_n: Keep-Alive 头部中的超时值,-1 表示未设置。 last_modified_time: 响应资源的最后修改时间,-1 表示无。 意义:用负数作为“缺失”标志,因为长度和时间戳都是非负数,避免了额外布尔标志。
r->uri_changes=NGX_HTTP_MAX_URI_CHANGES+1;r->subrequests=NGX_HTTP_MAX_SUBREQUESTS+1;r->http_state=NGX_HTTP_READING_REQUEST_STATE;r->log_handler=ngx_http_log_error_handler;
#1 初始化 URI 重写(内部重定向)的剩余允许次数。 NGX_HTTP_MAX_URI_CHANGES 是 nginx 的编译期常量(默认值为 11), 表示一个请求最多允许发生多少次 URI 重写(如 rewrite 指令、内部重定向等)。 此处赋值为 最大值 + 1(即默认 12)。 在进入请求处理循环时,会先执行 r->uri_changes--, 所以实际允许的重写次数正好是 11。 每次发生一次内部 URI 变更(例如执行 rewrite 后重新匹配 location),该值会递减。 当递减到 0 时,nginx 会认为发生了重写循环,返回 500 错误,防止无限循环消耗服务器资源。 用一个计数器来限制单次请求内重写或内部重定向的次数上限, 避免配置错误导致无限循环。
#2 初始化子请求的最大允许数量 防止因配置不当或递归逻辑导致无限创建子请求,从而耗尽内存和连接资源
#3 设置请求处理的初始状态。 http_state 是 ngx_http_request_t 中的一个状态机字段, 用于控制请求处理的不同阶段 NGX_HTTP_READING_REQUEST_STATE 是一个枚举常量, 表示“正在读取请求行或请求头”。这是请求处理的最早期阶段
#4 设置请求专属的错误日志处理函数
4 返回结构体指针
returnr;}