nginx的请求接收流程(二)
在ngx_http_process_request_line函數(shù)中,解析完請(qǐng)求行之后,如果請(qǐng)求行的uri里面包含了域名部分,則將其保持在請(qǐng)求結(jié)構(gòu)的headers_in成員的server字段,headers_in用來保存所有請(qǐng)求頭,它的類型為ngx_http_headers_in_t:
typedef struct {ngx_list_t ? ? ? ? ? ? ? ? ? ? ? ?headers;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*host;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*connection;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*if_modified_since;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*if_unmodified_since;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*user_agent;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*referer;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*content_length;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*content_type;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*range;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*if_range;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*transfer_encoding;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*expect;#if (NGX_HTTP_GZIP)ngx_table_elt_t ? ? ? ? ? ? ? ? ?*accept_encoding;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*via; #endifngx_table_elt_t ? ? ? ? ? ? ? ? ?*authorization;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*keep_alive;#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)ngx_table_elt_t ? ? ? ? ? ? ? ? ?*x_forwarded_for; #endif#if (NGX_HTTP_REALIP)ngx_table_elt_t ? ? ? ? ? ? ? ? ?*x_real_ip; #endif#if (NGX_HTTP_HEADERS)ngx_table_elt_t ? ? ? ? ? ? ? ? ?*accept;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*accept_language; #endif#if (NGX_HTTP_DAV)ngx_table_elt_t ? ? ? ? ? ? ? ? ?*depth;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*destination;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*overwrite;ngx_table_elt_t ? ? ? ? ? ? ? ? ?*date; #endifngx_str_t ? ? ? ? ? ? ? ? ? ? ? ? user;ngx_str_t ? ? ? ? ? ? ? ? ? ? ? ? passwd;ngx_array_t ? ? ? ? ? ? ? ? ? ? ? cookies;ngx_str_t ? ? ? ? ? ? ? ? ? ? ? ? server;off_t ? ? ? ? ? ? ? ? ? ? ? ? ? ? content_length_n;time_t ? ? ? ? ? ? ? ? ? ? ? ? ? ?keep_alive_n;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?connection_type:2;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?msie:1;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?msie6:1;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?opera:1;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?gecko:1;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?chrome:1;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?safari:1;unsigned ? ? ? ? ? ? ? ? ? ? ? ? ?konqueror:1; } ngx_http_headers_in_t;
接著,該函數(shù)會(huì)檢查進(jìn)來的請(qǐng)求是否使用的是http0.9,如果是的話則使用從請(qǐng)求行里得到的域名,調(diào)用ngx_http_find_virtual_server()函數(shù)來查找用來處理該請(qǐng)求的虛擬服務(wù)器配置,之前通過端口和地址找到的默認(rèn)配置不再使用,找到相應(yīng)的配置之后,則直接調(diào)用ngx_http_process_request()函數(shù)處理該請(qǐng)求,因?yàn)閔ttp0.9是最原始的http協(xié)議,它里面沒有定義任何請(qǐng)求頭,顯然就不需要讀取請(qǐng)求頭的操作。
? ? ? ? ? ?
if (r->host_start && r->host_end) {host = r->host_start;n = ngx_http_validate_host(r, &host,r->host_end - r->host_start, 0);if (n == 0) {ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent invalid host in request line");ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);return;}if (n < 0) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return;}r->headers_in.server.len = n;r->headers_in.server.data = host;}if (r->http_version < NGX_HTTP_VERSION_10) {if (ngx_http_find_virtual_server(r, r->headers_in.server.data,r->headers_in.server.len)== NGX_ERROR){ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return;}ngx_http_process_request(r);return;}
當(dāng)然,如果是1.0或者更新的http協(xié)議,接下來要做的就是讀取請(qǐng)求頭了,首先nginx會(huì)為請(qǐng)求頭分配空間,ngx_http_headers_in_t結(jié)構(gòu)的headers字段為一個(gè)鏈表結(jié)構(gòu),它被用來保存所有請(qǐng)求頭,初始為它分配了20個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)的類型為ngx_table_elt_t,保存請(qǐng)求頭的name/value值對(duì),還可以看到ngx_http_headers_in_t結(jié)構(gòu)有很多類型為ngx_table_elt_t*的指針成員,而且從它們的命名可以看出是一些常見的請(qǐng)求頭名字,nginx對(duì)這些常用的請(qǐng)求頭在ngx_http_headers_in_t結(jié)構(gòu)里面保存了一份引用,后續(xù)需要使用的話,可以直接通過這些成員得到,另外也事先為cookie頭分配了2個(gè)元素的數(shù)組空間,做完這些內(nèi)存準(zhǔn)備工作之后,該請(qǐng)求對(duì)應(yīng)的讀事件結(jié)構(gòu)的處理函數(shù)被設(shè)置為ngx_http_process_request_headers,并隨后馬上調(diào)用了該函數(shù)。
? ? ? ? ?
? if (ngx_list_init(&r->headers_in.headers, r->pool, 20,sizeof(ngx_table_elt_t))!= NGX_OK){ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return;}if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,sizeof(ngx_table_elt_t *))!= NGX_OK){ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return;}c->log->action = "reading client request headers";rev->handler = ngx_http_process_request_headers;ngx_http_process_request_headers(rev);
ngx_http_process_request_headers函數(shù)循環(huán)的讀取所有的請(qǐng)求頭,并保存和初始化和請(qǐng)求頭相關(guān)的結(jié)構(gòu),下面詳細(xì)分析一下該函數(shù):
因?yàn)閚ginx對(duì)讀取請(qǐng)求頭有超時(shí)限制,ngx_http_process_request_headers函數(shù)作為讀事件處理函數(shù),一并處理了超時(shí)事件,如果讀超時(shí)了,nginx直接給該請(qǐng)求返回408錯(cuò)誤:
讀取和解析請(qǐng)求頭的邏輯和處理請(qǐng)求行差不多,總的流程也是循環(huán)的調(diào)用ngx_http_read_request_header()函數(shù)讀取數(shù)據(jù),然后再調(diào)用一個(gè)解析函數(shù)來從讀取的數(shù)據(jù)中解析請(qǐng)求頭,直到解析完所有請(qǐng)求頭,或者發(fā)生解析錯(cuò)誤為主。當(dāng)然由于涉及到網(wǎng)絡(luò)io,這個(gè)流程可能發(fā)生在多個(gè)io事件的上下文中。
接著來細(xì)看該函數(shù),先調(diào)用了ngx_http_read_request_header()函數(shù)讀取數(shù)據(jù),如果當(dāng)前連接并沒有數(shù)據(jù)過來,再直接返回,等待下一次讀事件到來,如果讀到了一些數(shù)據(jù)則調(diào)用ngx_http_parse_header_line()函數(shù)來解析,同樣的該解析函數(shù)實(shí)現(xiàn)為一個(gè)有限狀態(tài)機(jī),邏輯很簡(jiǎn)單,只是根據(jù)http協(xié)議的解析一個(gè)請(qǐng)求頭的name/vale對(duì),每次調(diào)用該函數(shù)最多解析出一個(gè)請(qǐng)求頭,該函數(shù)返回4種不同返回值,表示不同解析結(jié)果:
1,返回NGX_OK,表示解析出了一行請(qǐng)求頭,這時(shí)還要判斷解析出的請(qǐng)求頭名字里面是否有非法字符,名字里面合法的字符包括字母,數(shù)字和連字符(-),另外如果設(shè)置了underscores_in_headers指令為on,則下劃線也是合法字符,但是nginx默認(rèn)下劃線不合法,當(dāng)請(qǐng)求頭里面包含了非法的字符,nginx默認(rèn)只是忽略這一行請(qǐng)求頭;如果一切都正常,nginx會(huì)將該請(qǐng)求頭及請(qǐng)求頭名字的hash值保存在請(qǐng)求結(jié)構(gòu)體的headers_in成員的headers鏈表,而且對(duì)于一些常見的請(qǐng)求頭,如Host,Connection,nginx采用了類似于配置指令的方式,事先給這些請(qǐng)求頭分配了一個(gè)處理函數(shù),當(dāng)解析出一個(gè)請(qǐng)求頭時(shí),會(huì)檢查該請(qǐng)求頭是否有設(shè)置處理函數(shù),有的話則調(diào)用之,nginx所有有處理函數(shù)的請(qǐng)求頭都記錄在ngx_http_headers_in全局?jǐn)?shù)組中:
typedef struct {ngx_str_t ? ? ? ? ? ? ? ? ? ? ? ? name;ngx_uint_t ? ? ? ? ? ? ? ? ? ? ? ?offset;ngx_http_header_handler_pt ? ? ? ?handler; } ngx_http_header_t;ngx_http_header_t ?ngx_http_headers_in[] = {{ ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),ngx_http_process_host },{ ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),ngx_http_process_connection },{ ngx_string("If-Modified-Since"),offsetof(ngx_http_headers_in_t, if_modified_since),ngx_http_process_unique_header_line },{ ngx_string("If-Unmodified-Since"),offsetof(ngx_http_headers_in_t, if_unmodified_since),ngx_http_process_unique_header_line },{ ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),ngx_http_process_user_agent },{ ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),ngx_http_process_header_line },{ ngx_string("Content-Length"),offsetof(ngx_http_headers_in_t, content_length),ngx_http_process_unique_header_line },{ ngx_string("Content-Type"),offsetof(ngx_http_headers_in_t, content_type),ngx_http_process_header_line },{ ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),ngx_http_process_header_line },{ ngx_string("If-Range"),offsetof(ngx_http_headers_in_t, if_range),ngx_http_process_unique_header_line },{ ngx_string("Transfer-Encoding"),offsetof(ngx_http_headers_in_t, transfer_encoding),ngx_http_process_header_line },{ ngx_string("Expect"),offsetof(ngx_http_headers_in_t, expect),ngx_http_process_unique_header_line },#if (NGX_HTTP_GZIP){ ngx_string("Accept-Encoding"),offsetof(ngx_http_headers_in_t, accept_encoding),ngx_http_process_header_line },{ ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),ngx_http_process_header_line }, #endif{ ngx_string("Authorization"),offsetof(ngx_http_headers_in_t, authorization),ngx_http_process_unique_header_line },{ ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),ngx_http_process_header_line },#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO){ ngx_string("X-Forwarded-For"),offsetof(ngx_http_headers_in_t, x_forwarded_for),ngx_http_process_header_line }, #endif#if (NGX_HTTP_REALIP){ ngx_string("X-Real-IP"),offsetof(ngx_http_headers_in_t, x_real_ip),ngx_http_process_header_line }, #endif#if (NGX_HTTP_HEADERS){ ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),ngx_http_process_header_line },{ ngx_string("Accept-Language"),offsetof(ngx_http_headers_in_t, accept_language),ngx_http_process_header_line }, #endif#if (NGX_HTTP_DAV){ ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),ngx_http_process_header_line },{ ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),ngx_http_process_header_line },{ ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),ngx_http_process_header_line },{ ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),ngx_http_process_header_line }, #endif{ ngx_string("Cookie"), 0, ngx_http_process_cookie },{ ngx_null_string, 0, NULL } };
ngx_http_headers_in數(shù)組當(dāng)前包含了25個(gè)常用的請(qǐng)求頭,每個(gè)請(qǐng)求頭都設(shè)置了一個(gè)處理函數(shù),當(dāng)前其中一部分請(qǐng)求頭設(shè)置的是公共的處理函數(shù),這里有2個(gè)公共的處理函數(shù),ngx_http_process_header_line和ngx_http_process_unique_header_line。
先來看一下處理函數(shù)的函數(shù)指針定義:
typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
????ngx_table_elt_t *h, ngx_uint_t offset);
它有3個(gè)參數(shù),r為對(duì)應(yīng)的請(qǐng)求結(jié)構(gòu),h為該請(qǐng)求頭在headers_in.headers鏈表節(jié)點(diǎn)的指針,offset為該請(qǐng)求頭的引用在ngx_http_headers_in_t結(jié)構(gòu)中的偏移。
再來看ngx_http_process_header_line函數(shù):
?
這個(gè)函數(shù)只是簡(jiǎn)單將該請(qǐng)求頭在ngx_http_headers_in_t結(jié)構(gòu)中保存一份引用。ngx_http_process_unique_header_line功能類似,不同點(diǎn)在于該函數(shù)會(huì)檢查這個(gè)請(qǐng)求頭是否是重復(fù)的,如果是的話,則給該請(qǐng)求返回400錯(cuò)誤。
ngx_http_headers_in數(shù)組中剩下的請(qǐng)求頭都有自己特殊的處理函數(shù),這些特殊的函數(shù)根據(jù)對(duì)應(yīng)的請(qǐng)求頭有一些特殊的處理,下面我們拿Host頭的處理函數(shù)ngx_http_process_host做一下介紹:
static ngx_int_t ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,ngx_uint_t offset) {u_char ? *host;ssize_t ? len;if (r->headers_in.host == NULL) {r->headers_in.host = h;}host = h->value.data;len = ngx_http_validate_host(r, &host, h->value.len, 0);if (len == 0) {ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,"client sent invalid host header");ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);return NGX_ERROR;}if (len < 0) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return NGX_ERROR;}if (r->headers_in.server.len) {return NGX_OK;}r->headers_in.server.len = len;r->headers_in.server.data = host;return NGX_OK; }
此函數(shù)的目的也是保存Host頭的快速引用,它會(huì)對(duì)Host頭的值做一些合法性檢查,并從中解析出域名,保存在headers_in.server字段,實(shí)際上前面在解析請(qǐng)求行時(shí),headers_in.server可能已經(jīng)被賦值為從請(qǐng)求行中解析出來的域名,根據(jù)http協(xié)議的規(guī)范,如果請(qǐng)求行中的uri帶有域名的話,則域名以它為準(zhǔn),所以這里需檢查一下headers_in.server是否為空,如果不為空則不需要再賦值。
其他請(qǐng)求頭的特殊處理函數(shù),不再做介紹,大致都是根據(jù)該請(qǐng)求頭在http協(xié)議中規(guī)定的意義及其值設(shè)置請(qǐng)求的一些屬性,必備后續(xù)使用。
對(duì)一個(gè)合法的請(qǐng)求頭的處理大致為如上所述;
2,返回NGX_AGAIN,表示當(dāng)前接收到的數(shù)據(jù)不夠,一行請(qǐng)求頭還未結(jié)束,需要繼續(xù)下一輪循環(huán)。在下一輪循環(huán)中,nginx首先檢查請(qǐng)求頭緩沖區(qū)header_in是否已滿,如夠滿了,則調(diào)用ngx_http_alloc_large_header_buffer()函數(shù)分配更多緩沖區(qū),下面分析一下ngx_http_alloc_large_header_buffer函數(shù):
static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,ngx_uint_t request_line) {u_char ? ? ? ? ? ? ? ? ? ?*old, *new;ngx_buf_t ? ? ? ? ? ? ? ? *b;ngx_http_connection_t ? ? *hc;ngx_http_core_srv_conf_t ?*cscf;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http alloc large header buffer");/** 在解析請(qǐng)求行階段,如果客戶端在發(fā)送請(qǐng)求行之前發(fā)送了大量回車換行符將* 緩沖區(qū)塞滿了,針對(duì)這種情況,nginx只是簡(jiǎn)單的重置緩沖區(qū),丟棄這些垃圾* 數(shù)據(jù),不需要分配更大的內(nèi)存。*/if (request_line && r->state == 0) {/* the client fills up the buffer with "\r\n" */r->request_length += r->header_in->end - r->header_in->start;r->header_in->pos = r->header_in->start;r->header_in->last = r->header_in->start;return NGX_OK;}/* 保存請(qǐng)求行或者請(qǐng)求頭在舊緩沖區(qū)中的起始地址 */old = request_line ? r->request_start : r->header_name_start;cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);/* 如果一個(gè)大緩沖區(qū)還裝不下請(qǐng)求行或者一個(gè)請(qǐng)求頭,則返回錯(cuò)誤 */if (r->state != 0&& (size_t) (r->header_in->pos - old)>= cscf->large_client_header_buffers.size){return NGX_DECLINED;}hc = r->http_connection;/* 首先在ngx_http_connection_t結(jié)構(gòu)中查找是否有空閑緩沖區(qū),有的話,直接取之 */if (hc->nfree) {b = hc->free[--hc->nfree];ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http large header free: %p %uz",b->pos, b->end - b->last);/* 檢查給該請(qǐng)求分配的請(qǐng)求頭緩沖區(qū)個(gè)數(shù)是否已經(jīng)超過限制,默認(rèn)最大個(gè)數(shù)為4個(gè) */} else if (hc->nbusy < cscf->large_client_header_buffers.num) {if (hc->busy == NULL) {hc->busy = ngx_palloc(r->connection->pool,cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));if (hc->busy == NULL) {return NGX_ERROR;}}/* 如果還沒有達(dá)到最大分配數(shù)量,則分配一個(gè)新的大緩沖區(qū) */b = ngx_create_temp_buf(r->connection->pool,cscf->large_client_header_buffers.size);if (b == NULL) {return NGX_ERROR;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http large header alloc: %p %uz",b->pos, b->end - b->last);} else {/* 如果已經(jīng)達(dá)到最大的分配限制,則返回錯(cuò)誤 */return NGX_DECLINED;}/* 將從空閑隊(duì)列取得的或者新分配的緩沖區(qū)加入已使用隊(duì)列 */hc->busy[hc->nbusy++] = b;/** 因?yàn)閚ginx中,所有的請(qǐng)求頭的保存形式都是指針(起始和結(jié)束地址),* 所以一行完整的請(qǐng)求頭必須放在連續(xù)的內(nèi)存塊中。如果舊的緩沖區(qū)不能* 再放下整行請(qǐng)求頭,則分配新緩沖區(qū),并從舊緩沖區(qū)拷貝已經(jīng)讀取的部分請(qǐng)求頭,* 拷貝完之后,需要修改所有相關(guān)指針指向到新緩沖區(qū)。* status為0表示解析完一行請(qǐng)求頭之后,緩沖區(qū)正好被用完,這種情況不需要拷貝*/if (r->state == 0) {/** r->state == 0 means that a header line was parsed successfully* and we do not need to copy incomplete header line and* to relocate the parser header pointers*/r->request_length += r->header_in->end - r->header_in->start;r->header_in = b;return NGX_OK;}ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http large header copy: %d", r->header_in->pos - old);r->request_length += old - r->header_in->start;new = b->start;/* 拷貝舊緩沖區(qū)中不完整的請(qǐng)求頭 */ngx_memcpy(new, old, r->header_in->pos - old);b->pos = new + (r->header_in->pos - old);b->last = new + (r->header_in->pos - old);/* 修改相應(yīng)的指針指向新緩沖區(qū) */if (request_line) {r->request_start = new;if (r->request_end) {r->request_end = new + (r->request_end - old);}r->method_end = new + (r->method_end - old);r->uri_start = new + (r->uri_start - old);r->uri_end = new + (r->uri_end - old);if (r->schema_start) {r->schema_start = new + (r->schema_start - old);r->schema_end = new + (r->schema_end - old);}if (r->host_start) {r->host_start = new + (r->host_start - old);if (r->host_end) {r->host_end = new + (r->host_end - old);}}if (r->port_start) {r->port_start = new + (r->port_start - old);r->port_end = new + (r->port_end - old);}if (r->uri_ext) {r->uri_ext = new + (r->uri_ext - old);}if (r->args_start) {r->args_start = new + (r->args_start - old);}if (r->http_protocol.data) {r->http_protocol.data = new + (r->http_protocol.data - old);}} else {r->header_name_start = new;r->header_name_end = new + (r->header_name_end - old);r->header_start = new + (r->header_start - old);r->header_end = new + (r->header_end - old);}r->header_in = b;return NGX_OK; }
當(dāng)ngx_http_alloc_large_header_buffer函數(shù)返回NGX_DECLINED)時(shí),表示客戶端發(fā)送了過大的一行請(qǐng)求頭,或者是整個(gè)請(qǐng)求頭部超過了限制,nginx會(huì)返回494錯(cuò)誤,注意到nginx再返回494錯(cuò)誤之前將請(qǐng)求的lingering_close標(biāo)識(shí)置為了1,這樣做的目的是在返回響應(yīng)之丟棄掉客戶端發(fā)過來的其他數(shù)據(jù);
3,返回NGX_HTTP_PARSE_INVALID_HEADER,表示請(qǐng)求頭解析過程中遇到錯(cuò)誤,一般為客戶端發(fā)送了不符合協(xié)議規(guī)范的頭部,此時(shí)nginx返回400錯(cuò)誤。
4,返回NGX_HTTP_PARSE_HEADER_DONE,表示所有請(qǐng)求頭已經(jīng)成功的解析,這時(shí)請(qǐng)求的狀態(tài)被設(shè)置為NGX_HTTP_PROCESS_REQUEST_STATE,意味著結(jié)束了請(qǐng)求讀取階段,正式進(jìn)入了請(qǐng)求處理階段,但是實(shí)際上請(qǐng)求可能含有請(qǐng)求體,nginx在請(qǐng)求讀取階段并不會(huì)去讀取請(qǐng)求體,這個(gè)工作交給了后續(xù)的請(qǐng)求處理階段的模塊,這樣做的目的是nginx本身并不知道這些請(qǐng)求體是否有用,如果后續(xù)模塊并不需要的話,一方面請(qǐng)求體一般較大,如果全部讀取進(jìn)內(nèi)存,則白白耗費(fèi)大量的內(nèi)存空間,另一方面即使nginx將請(qǐng)求體寫進(jìn)磁盤,但是涉及到磁盤io,會(huì)耗費(fèi)比較多時(shí)間。所以交由后續(xù)模塊來決定讀取還是丟棄請(qǐng)求體是最明智的辦法。
讀取完請(qǐng)求頭之后,nginx調(diào)用了ngx_http_process_request_header()函數(shù),這個(gè)函數(shù)主要做了兩個(gè)方面的事情,一是調(diào)用ngx_http_find_virtual_server()函數(shù)查找虛擬服務(wù)器配置;二是對(duì)一些請(qǐng)求頭做一些協(xié)議的檢查。比如對(duì)那些使用http1.1協(xié)議但是卻沒有發(fā)送Host頭的請(qǐng)求,nginx給這些請(qǐng)求返回400錯(cuò)誤。還有nginx現(xiàn)在的版本并不支持chunked格式的輸入,如果某些請(qǐng)求申明自己使用了chunked格式的輸入(請(qǐng)求帶有值為chunked的transfer_encoding頭部),nginx給這些請(qǐng)求返回411錯(cuò)誤。等等。
最后調(diào)用ngx_http_process_request()函數(shù)處理請(qǐng)求;
至此,nginx接收請(qǐng)求接收流程就介紹完畢。
---------------------?
作者:fengmo_q?
來源:CSDN?
原文:https://blog.csdn.net/fengmo_q/article/details/7736695?
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
總結(jié)
以上是生活随笔為你收集整理的nginx的请求接收流程(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nginx监听事件流程
- 下一篇: EPOLL事件之EPOLLRDHUP