ngx_http_process_request_line函數的主要作用即是解析請求行,同樣由于涉及到網絡IO操作,即使是很短的一行請求行可能也不能被一次讀完,所以在之前的ngx_http_init_request函數中,ngx_http_process_request_line函數被設置為讀事件的處理函數,它也只擁有一個唯一的ngx_event_t *類型參數,并且在函數的開頭,同樣需要判斷是否是超時事件,如果是的話,則關閉這個請求和連接;否則開始正常的解析流程。先調用ngx_http_read_request_header函數讀取數據。
由于可能多次進入ngx_http_process_request_line函數,ngx_http_read_request_header函數首先檢查請求的header_in指向的緩沖區內是否有數據,有的話直接返回;否則從連接讀取數據并保存在請求的header_in指向的緩存區,而且只要緩沖區有空間的話,會一次盡可能多的讀數據,讀到多少返回多少;如果客戶端暫時沒有發任何數據過來,并返回NGX_AGAIN,返回之前會做2件事情:
1,設置一個定時器,時長默認為60s,可以通過指令client_header_timeout設置,如果定時事件到達之前沒有任何可讀事件,nginx將會關閉此請求;
2,調用ngx_handle_read_event函數處理一下讀事件-如果該連接尚未在事件處理模型上掛載讀事件,則將其掛載上;
如果客戶端提前關閉了連接或者讀取數據發生了其他錯誤,則給客戶端返回一個400錯誤(當然這里并不保證客戶端能夠接收到響應數據,因為客戶端可能都已經關閉了連接),最后函數返回NGX_ERROR;
如果ngx_http_read_request_header函數正常的讀取到了數據,ngx_http_process_request_line函數將調用ngx_http_parse_request_line函數來解析,這個函數根據http協議規范中對請求行的定義實現了一個有限狀態機,經過這個狀態機,nginx會記錄請求行中的請求方法(Method),請求uri以及http協議版本在緩沖區中的起始位置,在解析過程中還會記錄一些其他有用的信息,以便后面的處理過程中使用。如果解析請求行的過程中沒有產生任何問題,該函數會返回NGX_OK;如果請求行不滿足協議規范,該函數會立即終止解析過程,并返回相應錯誤號;如果緩沖區數據不夠,該函數返回NGX_AGAIN。
在整個解析http請求的狀態機中始終遵循著兩條重要的原則:減少內存拷貝和回溯。
內存拷貝是一個相對比較昂貴的操作,大量的內存拷貝會帶來較低的運行時效率。nginx在需要做內存拷貝的地方盡量只拷貝內存的起始和結束地址而不是內存本身,這樣做的話僅僅只需要兩個賦值操作而已,大大降低了開銷,當然這樣帶來的影響是后續的操作不能修改內存本身,如果修改的話,會影響到所有引用到該內存區間的地方,所以必須很小心的管理,必要的時候需要拷貝一份。
這里不得不提到nginx中最能體現這一思想的數據結構,ngx_buf_t,它用來表示nginx中的緩存,在很多情況下,只需要將一塊內存的起始地址和結束地址分別保存在它的pos和last成員中,再將它的memory標志置1,即可表示一塊不能修改的內存區間,在另外的需要一塊能夠修改的緩存的情形中,則必須分配一塊所需大小的內存并保存其起始地址,再將ngx_buf_t的temporary標志置1,表示這是一塊能夠被修改的內存區域。
再回到ngx_http_process_request_line函數中,如果ngx_http_parse_request_line函數返回了錯誤,則直接給客戶端返回400錯誤; 如果返回NGX_AGAIN,則需要判斷一下是否是由于緩沖區空間不夠,還是已讀數據不夠。如果是緩沖區大小不夠了,nginx會調用ngx_http_alloc_large_header_buffer函數來分配另一塊大緩沖區,如果大緩沖區還不夠裝下整個請求行,nginx則會返回414錯誤給客戶端,否則分配了更大的緩沖區并拷貝之前的數據之后,繼續調用ngx_http_read_request_header函數讀取數據來進入請求行自動機處理,直到請求行解析結束;
如果返回了NGX_OK,則表示請求行被正確的解析出來了,這時先記錄好請求行的起始地址以及長度,并將請求uri的path和參數部分保存在請求結構的uri字段,請求方法起始位置和長度保存在method_name字段,http版本起始位置和長度記錄在http_protocol字段。還要從uri中解析出參數以及請求資源的拓展名,分別保存在args和exten字段。接下來將要解析請求頭,將在下一小節中接著介紹。
- 上篇: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 模塊編譯,調試與測試