### CONTENT階段[](http://tengine.taobao.org/book/chapter_12.html#content "永久鏈接至標題")
CONTENT階段可以說是整個執行鏈中最重要的階段,請求從這里開始執行業務邏輯并產生響應,下面來分析一下它的checker函數:
[](http:// "點擊提交Issue,反饋你的意見...")
ngx_int_t
ngx_http_core_content_phase(ngx_http_request_t *r,
ngx_http_phase_handler_t *ph)
{
size_t root;
ngx_int_t rc;
ngx_str_t path;
if (r->content_handler) {
r->write_event_handler = ngx_http_request_empty_handler;
ngx_http_finalize_request(r, r->content_handler(r));
return NGX_OK;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"content phase: %ui", r->phase_handler);
rc = ph->handler(r);
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return NGX_OK;
}
/* rc == NGX_DECLINED */
ph++;
if (ph->checker) {
r->phase_handler++;
return NGX_AGAIN;
}
/* no content handler was found */
if (r->uri.data[r->uri.len - 1] == '/') {
if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"directory index of \"%s\" is forbidden", path.data);
}
ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
return NGX_OK;
}
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
return NGX_OK;
}
CONTENT階段有些特殊,它不像其他階段只能執行固定的handler鏈,還有一個特殊的content_handler,每個location可以有自己獨立的content handler,而且當有content handler時,CONTENT階段只會執行content handler,不再執行本階段的handler鏈。
默認情況下,Nginx會在CONTENT階段的handler鏈掛上index模塊,靜態文件處理模塊等的handler。另外模塊還可以設置獨立的content handler,比如ngx_http_proxy_module的proxy_pass指令會設置一個名為ngx_http_proxy_handler的content handler。
接下來看一下上面的checker函數的執行流程,首先檢查是否設置了r->content_handler,如果設置了的話,則執行它,需要注意的是在執行它之前,Nginx將r->write_event_handler設置為了ngx_http_request_empty_handler,先看一下設置r->write_event_handler之前的值是什么,在ngx_http_handler函數中它被設置為ngx_http_core_run_phases,而ngx_http_core_run_phases會運行每個階段的checker函數。正常流程中,如果某個階段需要等待某個寫事件發生時,該階段的handler會返回NGX_OK來中斷ngx_http_core_run_phases的運行,等到下次寫事件過來時,會繼續執行之前階段的handler;當執行r->content_handler的流程時,Nginx默認模塊會去處理r->write_event_handler的值,也就是假設r->content_handler只能執行1次,如果模塊設置的content handler涉及到IO操作,就需要合理的設置處理讀寫事件的handler(r->read_event_handler和r->write_event_handler)。
還有一個需要注意的點是r->content_handler執行之后,Nginx直接用其返回值調用了ngx_http_finalize_request函數,Nginx將一大堆耦合的邏輯都集中在了這個函數當中,包括長連接,lingering_close,子請求等的處理都涉及到該函數,后面會有一節單獨介紹這個函數。這里需要提醒的是r->content_handler如果并未完成整個請求的處理,而只是需要等待某個事件發生而退出處理流程的話,必須返回一個合適的值傳給ngx_http_finalize_request,一般而言是返回NGX_DONE,而且需要將請求的引用計數(r->count)加1,確保ngx_http_finalize_request函數不會將該請求釋放掉。
函數的其他部分處理走handler鏈的情況,特殊的地方是CONTENT階段是ngx_http_core_run_phases函數跑的最后一個階段,如果最后一個handler返回NGX_DECLINED,此時Nginx會給客戶端返回NGX_HTTP_FORBIDDEN(403)或NGX_HTTP_NOT_FOUND(404)。
- 上篇:nginx模塊開發篇
- nginx平臺初探
- 初探nginx架構
- nginx基礎概念
- connection
- request
- keepalive
- pipe
- lingering_close
- 基本數據結構
- ngx_str_t
- ngx_pool_t
- ngx_array_t
- ngx_hash_t
- ngx_hash_wildcard_t
- ngx_hash_combined_t
- ngx_hash_keys_arrays_t
- ngx_chain_t
- ngx_buf_t
- ngx_list_t
- ngx_queue_t
- nginx的配置系統
- 指令參數
- 指令上下文
- nginx的模塊化體系結構
- 模塊的分類
- nginx的請求處理
- handler模塊
- handler模塊簡介
- 模塊的基本結構
- 模塊配置結構
- 模塊配置指令
- 模塊上下文結構
- 模塊的定義
- handler模塊的基本結構
- handler模塊的掛載
- handler的編寫步驟
- 示例: hello handler 模塊
- handler模塊的編譯和使用
- 更多handler模塊示例分析
- http access module
- http static module
- http log module
- 過濾模塊
- 過濾模塊簡介
- 過濾模塊的分析
- upstream模塊
- upstream模塊
- upstream模塊接口
- memcached模塊分析
- 本節回顧
- 負載均衡模塊
- 配置
- 指令
- 鉤子
- 初始化配置
- 初始化請求
- peer.get和peer.free回調函數
- 本節回顧
- 其他模塊
- core模塊
- event模塊
- 模塊開發高級篇
- 變量
- 下篇:nginx原理解析篇
- nginx架構詳解
- nginx的源碼目錄結構
- nginx的configure原理
- 模塊編譯順序
- nginx基礎設施
- 內存池
- nginx的啟動階段
- 概述
- 共有流程
- 配置解析
- nginx的請求處理階段
- 接收請求流程
- http請求格式簡介
- 請求頭讀取
- 解析請求行
- 解析請求頭
- 請求體讀取
- 讀取請求體
- 丟棄請求體
- 多階段處理請求
- 多階段執行鏈
- POST_READ階段
- SERVER_REWRITE階段
- FIND_CONFIG階段
- REWRITE階段
- POST_REWRITE階段
- PREACCESS階段
- ACCESS階段
- POST_ACCESS階段
- TRY_FILES階段
- CONTENT階段
- LOG階段
- Nginx filter
- header filter分析
- body filter分析
- ngx_http_copy_filter_module分析
- ngx_http_write_filter_module分析
- subrequest原理解析
- https請求處理解析
- 附錄A 編碼風格
- 附錄B 常用API
- 附錄C 模塊編譯,調試與測試