本文我們則主要講解nginx是如何讀取客戶端傳送來的請求頭的資料,並且解析這些資料的。本質上來講,請求行的資料和請求頭的資料讀取流程是基本一致的,因為其都面臨著如何從間斷的資料流中讀取到資料,也面臨著如何對資料進行處理的問題。
在介紹請求頭的讀取流程之前,我們首先展示乙個http請求報文的示例:
/** * 解析客戶端傳送來的header資料 */static void ngx_http_process_request_headers(ngx_event_t *rev) cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); rc = ngx_again; for (;;) // 客戶端傳送的header太長,超出了large_client_header_buffers指定的最大大小 if (rv == ngx_declined) len = r->header_in->end - p; if (len > ngx_max_error_str - 300) ngx_http_finalize_request(r, ngx_http_request_header_too_large); return; } } // 嘗試讀取連線上客戶端新傳送來的資料 n = ngx_http_read_request_header(r); if (n == ngx_again || n == ngx_error) } cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); // 這裡主要是對讀取到的資料進行轉換 rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers); // ngx_ok表示成功解析得到了乙個header資料 if (rc == ngx_ok) // 建立乙個儲存header的結構體 h = ngx_list_push(&r->headers_in.headers); if (h == null) h->hash = r->header_hash; // 把header的name作為hash表的key h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; h->key.data[h->key.len] = '0'; // 把header的value作為hash表的value h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; h->value.data[h->value.len] = '0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == null) if (h->key.len == r->lowcase_index) else // headers_in_hash中儲存了所有的header,這裡是查詢當前客戶端傳的header是否為有效的header hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); // 這裡的handler是在ngx_http_headers_in中為每乙個header定義的處理方法,經過各個header的 // handler()方法處理後,客戶端傳來的header就都轉換到r->headers_in結構體中的各個屬性中了 if (hh && hh->handler(r, h, hh->offset) != ngx_ok) continue; } // ngx_http_parse_header_done表示已經將所有的header都處理完成了 if (rc == ngx_http_parse_header_done) ngx_http_process_request(r); return; } // ngx_again表示讀取到的header行資料不完全,還需要繼續讀取 if (rc == ngx_again) ngx_log_error(ngx_log_info, c->log, 0, "client sent invalid header line"); ngx_http_finalize_request(r, ngx_http_bad_request); return; }}
這裡請求頭的讀取主要分為如下幾個步驟:
可以看到,對請求頭的讀取主要有兩個方法:ngx_http_read_request_header()和ngx_http_parse_header_line()。這裡的第二個方法比較長,但是其邏輯非常的簡單,主要就是解析讀取到的資料是否能組成乙個完整的請求頭(name: value的形式,並且占用一行),如果是,則返回ngx_ok,否則返回ngx_again以期待繼續讀取資料。對於這個方法,我們這裡不進行講解,讀者可自行閱讀原始碼,我們主要講解ngx_http_read_request_header()方法是如何讀取客戶端傳送來的請求頭資料的:
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r) // 走到這裡,說明當前讀取到的資料都已經處理完了,因而這裡會進行判斷,如果當前事件的ready引數為1, // 則表示當前連線的控制代碼上儲存還未讀取的資料,因而呼叫c->recv()方法讀取資料,否則繼續將當前事件新增到 // 事件佇列中,並且繼續監聽當前連線控制代碼的讀事件 if (rev->ready) else // 如果n為ngx_again,則將當前事件新增到事件***中,並且繼續監聽當前epoll控制代碼的讀事件 if (n == ngx_again) if (ngx_handle_read_event(rev, 0) != ngx_ok) return ngx_again; } // 如果n為0,說明客戶端關閉了連線 if (n == 0) // 如果客戶端關閉了連線或者讀取異常,則**當前的request結構體 if (n == 0 || n == ngx_error) // 更新當前讀取到的資料指標 r->header_in->last += n; return n;}
這裡請求頭資料的讀取主要分為如下幾個步驟:
本文主要對nginx是如何讀取並且解析請求頭的流程進行了講解,並且著重講解了讀取資料的主流程**和讀取的詳細步驟。
請求行,請求頭,請求體
1.請求報文 請求行 請求頭 請求資料 空行 請求行求方法字段 url欄位和http協議版本 例如 get index.html http 1.1 get方法將資料拼接在url後面,傳遞引數受限 請求方法 get post head put delete options trace connect ...
請求行 請求頭 請求體
請求行 是請求方法,get和post是最常見的http方法,除此以外還包括delete head options put trace。為請求對應的url位址,它和報文頭的host屬性組成完整的請求url。是協議名稱及版本號。請求頭 是http的報文頭,報文頭包含若干個屬性,格式為 屬性名 屬性值 服...
請求行 請求頭與請求體
請求行 請求頭與請求體可以通過httpwatch檢視 請求行,則為第一行,其中包括 get或post url http版本 注意 url的資訊必須是已經urlencoded編碼後的 瀏覽器不會自動編碼 否則將不符合要求,如 中文 請求頭,則第二行之後的資訊,可以在 httpconext.reques...