nginx divides the processing of http requests into 11 stages. Each stage has different functions. Some built-in modules implement their functions in different stages. These stages are defined as the following enumeration types:
typedef enum { NGX_HTTP_POST_READ_PHASE = 0, NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_FIND_CONFIG_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_POST_REWRITE_PHASE, NGX_HTTP_PREACCESS_PHASE, NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, NGX_HTTP_PRECONTENT_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE } ngx_http_phases;
The first phase is the post ﹣ read phase. NGX http realip module works in this stage. It can extract the real IP address of the client according to the x-forward-for header.
The second stage is the server? Rewrite stage. The NGX ﹣ http ﹣ rewrite ﹣ module works at this stage to implement the url rewriting function in the server {} configuration.
The third stage is the find & config stage. No module can get involved in this stage. The function of this stage is to find the matching lockaiton {} according to the requested url.
The fourth stage is the REWRITE stage. NGX ﹣ http ﹣ REWRITE ﹣ module also works at this stage to handle url rewriting in the location {} configuration.
The fifth stage is the Post & rewrite stage. No module can get involved in this stage. The purpose of this stage is to cooperate with NGX HTTP rewrite module.
The sixth stage is the press stage. At this stage, NGX HTTP limit conn module and NGX HTTP limit req module work. They implement the concurrent connection limit and request rate limit functions respectively.
The seventh phase is ACCESS. At this stage, NGX HTTP ACCESS module and NGX HTTP auth basic module work respectively to realize the functions of ACCESS control and basic authentication.
The eighth stage is the post? ACCESS stage. No module can get involved in this stage. This stage is to match the ACCESS stage.
The ninth stage is the prerequisite stage. NGX HTTP mirror module works in this stage to realize the function of traffic mirror.
The tenth stage is the CONTENT stage. This is the most critical stage, and also the stage that external modules like to intervene most. At this stage, NGX HTTP index module and NGX HTTP static module work to realize the function of returning static pages. After analyzing the code, we also focus on this stage.
The last is the LOG phase. At this stage, the NGX HTTP LOG module works to output access logs.
So how do these modules get involved in these stages? Let's first look at the function NGX HTTP index module will be called after the configuration is parsed.
static ngx_int_t ngx_http_index_init(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, " "); cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_index_handler; return NGX_OK; }
It adds a handler to the array of CMCF - > phases [NGX? HTTP? Content? Phase]. Handlers, which is NGX? HTTP? Index? Handler. In this way, the handler processing will be called at this stage of execution.
typedef struct { ... ngx_http_phase_engine_t phase_engine; ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; ... } ngx_http_core_main_conf_t;
As you can see, there is an array of phases in the main level configuration structure NGX ﹣ http ﹣ core ﹣ main ﹣ conf ﹣ created by the core module. Its size is 11. Each phase is represented by NGX ﹣ http ﹣ phase ﹣ T structure.
typedef struct { ngx_array_t handlers; } ngx_http_phase_t;
We see that this structure is actually an array, each item is a function pointer, pointing to a handler. Its prototype is:
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
Let's take a look at another member, phase engine, whose structure is NGX HTTP phase engine t.
typedef struct { ngx_http_phase_handler_t *handlers; ngx_uint_t server_rewrite_index; ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t;
It has a handlers member, which points to an NGX ﹣ http ﹣ phase ﹣ handler ﹣ T structure array. The definition of this structure is as follows:
struct ngx_http_phase_handler_s { ngx_http_phase_handler_pt checker; ngx_http_handler_pt handler; ngx_uint_t next; };
Here we find the handler.
So how is the phase UU engine member initialized? Let's take a look at the function that initializes it, NGX? HTTP? Init? Phase? Handlers.
ngx_http_init_phase_handlers
It is called by NGX ﹣ http ﹣ block. At this time, each module has added a handler.
cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1; cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1; find_config_index = 0; use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; n = 1 /* find config phase */ + use_rewrite /* post rewrite phase */ + use_access; /* post access phase */ for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; } ph = ngx_pcalloc(cf->pool, n * sizeof(ngx_http_phase_handler_t) + sizeof(void *)); if (ph == NULL) { return NGX_ERROR; } cmcf->phase_engine.handlers = ph;
First, calculate the number of all handler s and allocate the corresponding memory.
n = 0; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { h = cmcf->phases[i].handlers.elts; switch (i) { case NGX_HTTP_SERVER_REWRITE_PHASE: if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.server_rewrite_index = n; } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_FIND_CONFIG_PHASE: find_config_index = n; ph->checker = ngx_http_core_find_config_phase; n++; ph++; continue; case NGX_HTTP_REWRITE_PHASE: if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.location_rewrite_index = n; } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_POST_REWRITE_PHASE: if (use_rewrite) { ph->checker = ngx_http_core_post_rewrite_phase; ph->next = find_config_index; n++; ph++; } continue; case NGX_HTTP_ACCESS_PHASE: checker = ngx_http_core_access_phase; n++; break; case NGX_HTTP_POST_ACCESS_PHASE: if (use_access) { ph->checker = ngx_http_core_post_access_phase; ph->next = n; ph++; } continue; case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; default: checker = ngx_http_core_generic_phase; } n += cmcf->phases[i].handlers.nelts; for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) { ph->checker = checker; ph->handler = h[j]; ph->next = n; ph++; } } return NGX_OK;
Then, according to each stage, set the checker, handler and next in the NGX ﹣ http ﹣ phase ﹣ handler ﹣ s structure.
Handler points to the handler added by each module.
Note that the for loop is traversed from the back to the front, so the handler added later executes first.
The checker points to one of NGX ﹐ http ﹐ core ﹐ rewrite ﹐ phase, NGX ﹐ http ﹐ core ﹐ find ﹐ config ﹐ phase, NGX ﹐ http ﹐ core ﹐ phase, NGX ﹐ content ﹐ phase, NGX ﹐ core ﹐ generic ﹐ phase. Because the function of each stage is different, different checkers need to be specified to perform different processes according to the returned results of the handler.
The index of each handler in the array is its number.
The next member is the number of the first handler in the next phase. In this way, you can directly jump to the next stage of execution.
Note that in the switch statement, only the checker is set when dealing with the find ﹣ config, post ﹣ rewrite, and post ﹣ access phases, and then continue. Therefore, there are only three stages: chcker and no handler. Modules cannot add handlers to these stages.
During the loop, the value of server REWRITE index in the phase engine member is set to the number of the handler that rewrites the url in server {} in the server REWRITE phase. The value of server ﹣ REWRITE ﹣ index is set to the number of the handler that rewrites the url in location {} in the REWRITE phase. In this way, you can directly jump to these two phases.
After learning about initialization, look at the code for the process.
ngx_http_process_request
After reading the request (excluding the package body), the request processing stage is entered. Start with the NGX? HTTP? Process? Request function.
void ngx_http_process_request(ngx_http_request_t *r) { ngx_connection_t *c; c = r->connection; if (c->read->timer_set) { ngx_del_timer(c->read); } c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; r->read_event_handler = ngx_http_block_reading; ngx_http_handler(r); ngx_http_run_posted_requests(c); }
Set the read-write event handler of the connection to NGX ﹣ http ﹣ request ﹣ handler.
Set the requested read event handler to NGX? HTTP? Block? Reading.
Then the ngx_http_handler function is called to process the request.
Finally, the ngx_http_run_posted_requests function is invoked to execute the sub request. I will send a separate article on the content of the sub request, which will not be mentioned here.
ngx_http_request_handler
First look at NGX ﹣ http ﹣ request ﹣ handler. Its code is very simple:
static void ngx_http_request_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; c = ev->data; r = c->data; ngx_http_set_log_request(c->log, r); if (ev->delayed && ev->timedout) { ev->delayed = 0; ev->timedout = 0; } if (ev->write) { r->write_event_handler(r); } else { r->read_event_handler(r); } ngx_http_run_posted_requests(c); }
After that, when a read event occurs on the connection, the requested read event handler is called.
When a write event occurs, the requested write event handler is called.
Finally, the sub request is executed.
ngx_http_handler
void ngx_http_handler(ngx_http_request_t *r) { ngx_http_core_main_conf_t *cmcf; r->connection->log->action = NULL; if (!r->internal) { switch (r->headers_in.connection_type) { case 0: r->keepalive = (r->http_version > NGX_HTTP_VERSION_10); break; case NGX_HTTP_CONNECTION_CLOSE: r->keepalive = 0; break; case NGX_HTTP_CONNECTION_KEEP_ALIVE: r->keepalive = 1; break; } r->lingering_close = (r->headers_in.content_length_n > 0 || r->headers_in.chunked); r->phase_handler = 0; } else { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); r->phase_handler = cmcf->phase_engine.server_rewrite_index; } r->valid_location = 1; r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); }
If the request is an external request, set whether to turn on the Keepalive function according to the situation. I will send a separate article to analyze the implementation of Keepalive.
Then set the phase "handler to 0, indicating that the request needs to be processed from the first phase post" read ".
If it is an internal request, set R - > phase? Handler to server? Rewrite? Index, indicating that the request needs to be processed from the server? Rewrite stage.
Then set the request's write event handler to NGX ﹣ http ﹣ core ﹣ run ﹣ phases and call it. In this way, when the request cannot be processed at one time and a write event appears on the connection, it is called to continue processing.
ngx_http_core_run_phases
This function calls the checker s of each stage to execute their handler s.
void ngx_http_core_run_phases(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_phase_handler_t *ph; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); if (rc == NGX_OK) { return; } } }
The loop starts at the stage where the handler with the number R - > phase \. When its checker returns NGX ﹣ OK, it exits the loop and the whole event processing ends. Otherwise, it is possible to execute the next handler of the current phase or jump to another phase.
The next article analyzes the handler code of the checker function NGX ﹣ http ﹣ core ﹣ CONTENT ﹣ phase and NGX ﹣ http ﹣ index ﹣ module and NGX ﹣ http ﹣ static ﹣ module in the CONTENT phase.